I am trying to create a "Todo App" with angularjs ui-router
. It has 2 columns:
我正在尝试使用angularjs ui-router创建一个“Todo App”。它有2列:
- Column 1: list of Todos
- 第1列:Todos列表
- Column 2: Todo details or Todo edit form
- 第2列:Todo详细信息或Todo编辑表单
In the Edit and Create controller after saving the Todo I would like to reload the list to show the appropriate changes. The problem: after calling $state.go('^')
when the Todo is created or updated, the URL in the browser changes back to /api/todo
, but the ListCtrl is not executed, i.e. $scope.search
is not called, hence the Todo list (with the changed items) is not retrieved, nor are the details of the first Todo displayed in Column 2 (instead, it goes blank).
在保存Todo后的编辑和创建控制器中,我想重新加载列表以显示相应的更改。问题:在创建或更新Todo时调用$ state.go('^')后,浏览器中的URL会更改回/ api / todo,但ListCtrl不会被执行,即$ scope.search未被调用因此,未检索Todo列表(包含已更改的项目),第2列中的第一个Todo的详细信息也未显示(相反,它变为空白)。
I have even tried $state.go('^', $stateParams, { reload: true, inherit: false, notify: false });
, no luck.
我甚至尝试过$ state.go('^',$ stateParams,{reload:true,inherit:false,notify:false});,没有运气。
How can I do a state transition so the controller eventually gets executed?
如何进行状态转换,以便控制器最终执行?
Source:
资源:
var TodoApp = angular.module('TodoApp', ['ngResource', 'ui.router'])
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/api/todo');
$stateProvider
.state('todo', {
url: '/api/todo',
controller: 'ListCtrl',
templateUrl: '/_todo_list.html'
})
.state('todo.details', {
url: '/{id:[0-9]*}',
views: {
'detailsColumn': {
controller: 'DetailsCtrl',
templateUrl: '/_todo_details.html'
}
}
})
.state('todo.edit', {
url: '/edit/:id',
views: {
'detailsColumn': {
controller: 'EditCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
.state('todo.new', {
url: '/new',
views: {
'detailsColumn': {
controller: 'CreateCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
;
})
;
TodoApp.factory('Todos', function ($resource) {
return $resource('/api/todo/:id', { id: '@id' }, { update: { method: 'PUT' } });
});
var ListCtrl = function ($scope, $state, Todos) {
$scope.todos = [];
$scope.search = function () {
Todos.query(function (data) {
$scope.todos = $scope.todos.concat(data);
$state.go('todo.details', { id: $scope.todos[0].Id });
});
};
$scope.search();
};
var DetailsCtrl = function ($scope, $stateParams, Todos) {
$scope.todo = Todos.get({ id: $stateParams.id });
};
var EditCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Edit';
var id = $stateParams.id;
$scope.todo = Todos.get({ id: id });
$scope.save = function () {
Todos.update({ id: id }, $scope.todo, function () {
$state.go('^', $stateParams, { reload: true, inherit: false, notify: false });
});
};
};
var CreateCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Create';
$scope.save = function () {
Todos.save($scope.todo, function () {
$state.go('^');
});
};
};
3 个解决方案
#1
5
I would give an example (a draft) of HOW TO nest edit
into detail
. Well, firstly let's amend the templates.
我将举例说明(草稿)如何将编辑嵌套到细节中。好吧,首先让我们修改模板。
The Detail
template, contains full definition of the detail. Plus it now contains the attribute ui-view="editView"
. This will assure, that the edit, will "replace" the detail from the visibility perspective - while the edit scope will inherit all the detail settings. That's the power of ui-router
详细信息模板包含详细信息的完整定义。此外,它现在包含属性ui-view =“editView”。这将确保编辑将从可见性角度“替换”细节 - 而编辑范围将继承所有细节设置。这就是ui-router的强大功能
<section ui-view="editView">
<!-- ... here the full description of the detail ... -->
</section>
So, secondly let's move the edit state
, into the detail
所以,其次让我们将编辑状态移动到细节中
// keep detail definition as it is
.state('todo.details', {
url: '/{id:[0-9]*}',
views: {
'detailsColumn': {
controller: 'DetailsCtrl',
templateUrl: '/_todo_details.html'
}
}
})
// brand new definition of the Edit
.state('todo.details.edit', { // i.e.: url for detail like /todo/details/1/edit
url: '/edit',
views: {
'editView': { // inject into the parent/detail view
controller: 'EditCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
Having this adjusted state
and template
mapping, we do have a lot. Now we can profit from the ui-router
in a full power.
有了这个调整后的状态和模板映射,我们确实有很多。现在我们可以充分利用ui-router获利。
We'll define some methods on a DetailCtrl
(remember, to be available on the inherit Edit state)
我们将在DetailCtrl上定义一些方法(记住,在继承Edit状态下可用)
var DetailsCtrl = function ($scope, $stateParams, Todos) {
$scope.id = $stateParams.id // keep it here
// model will keep the item (todos) and a copy for rollback
$scope.model = {
todos : {},
original : {},
}
// declare the Load() method
$scope.load = function() {
Todos
.get({ id: $stateParams.id })
.then(function(response){
// item loaded, and its backup copy created
$scope.model.todos = response.data;
$scope.model.original = angular.copy($scope.model.todos);
});
};
// also explicitly load, but just once,
// not auto-triggered when returning back from Edit-child
$scope.load()
};
OK, it should be clear now, that we do have a model
with the item model.todos
and its backup model.original
.
好了,现在应该很清楚,我们确实有一个带有item.todos项目及其备份模型的模型。原文。
The Edit controller could have two actions: Save()
and Cancel()
编辑控制器可以有两个操作:Save()和Cancel()
var EditCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Edit';
// ATTENTION, no declaration of these,
// we inherited them from parent view !
//$scope.id .. // we DO have them
//$scope.model ...
// the save, then force reload, and return to detail
$scope.save = function () {
Todos
.update({ id: id })
.then(function(response){
// Success
$scope.load();
$state.go('^');
},
function(reason){
// Error
// TODO
});
};
// a nice and quick how to rollback
$scope.cancel = function () {
$scope.model.todos = Angular.copy($scope.model.original);
$state.go('^');
};
};
That should give some idea, how to navigate between parent/child states and forcing reload.
这应该给出一些想法,如何在父/子状态之间导航并强制重新加载。
NOTE in fact, instead of Angular.copy() I am using lo-dash _.cloneDeep()
but both should work
注意事实上,而不是Angular.copy()我使用lo-dash _.cloneDeep()但两者都应该工作
#2
5
Huge thanks for Radim Köhler for pointing out that $scope
is inherited. With 2 small changes I managed to solve this. See below code, I commented where I added the extra lines. Now it works like a charm.
非常感谢RadimKöhler指出$ scope是继承的。通过2次小改动,我设法解决了这个问题。看下面的代码,我评论了我添加额外行的地方。现在它就像一个魅力。
var TodoApp = angular.module('TodoApp', ['ngResource', 'ui.router'])
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/api/todo');
$stateProvider
.state('todo', {
url: '/api/todo',
controller: 'ListCtrl',
templateUrl: '/_todo_list.html'
})
.state('todo.details', {
url: '/{id:[0-9]*}',
views: {
'detailsColumn': {
controller: 'DetailsCtrl',
templateUrl: '/_todo_details.html'
}
}
})
.state('todo.edit', {
url: '/edit/:id',
views: {
'detailsColumn': {
controller: 'EditCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
.state('todo.new', {
url: '/new',
views: {
'detailsColumn': {
controller: 'CreateCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
;
})
;
TodoApp.factory('Todos', function ($resource) {
return $resource('/api/todo/:id', { id: '@id' }, { update: { method: 'PUT' } });
});
var ListCtrl = function ($scope, $state, Todos) {
$scope.todos = [];
$scope.search = function () {
Todos.query(function (data) {
$scope.todos = $scope.todos(data); // No concat, just overwrite
if (0 < $scope.todos.length) { // Added this as well to avoid overindexing if no Todo is present
$state.go('todo.details', { id: $scope.todos[0].Id });
}
});
};
$scope.search();
};
var DetailsCtrl = function ($scope, $stateParams, Todos) {
$scope.todo = Todos.get({ id: $stateParams.id });
};
var EditCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Edit';
var id = $stateParams.id;
$scope.todo = Todos.get({ id: id });
$scope.save = function () {
Todos.update({ id: id }, $scope.todo, function () {
$scope.search(); // Added this line
//$state.go('^'); // As $scope.search() changes the state, this is not even needed.
});
};
};
var CreateCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Create';
$scope.save = function () {
Todos.save($scope.todo, function () {
$scope.search(); // Added this line
//$state.go('^'); // As $scope.search() changes the state, this is not even needed.
});
};
};
#3
3
I might have faced a similar problem the approach i took was to use $location.path(data.path).search(data.search);
to redirect the page then in the controller I caught the $locationChangeSuccess event. I other words I use the $location.path(...).search(...)
as apposed to $state.go(...)
then caught the $locationChangeSuccess event which will be fired when the location changes occurs before the route is matched and the controller invoked.
我可能遇到了类似的问题,我采用的方法是使用$ location.path(data.path).search(data.search);重定向页面然后在控制器中我捕获$ locationChangeSuccess事件。换句话说,我使用$ location.path(...)。search(...)作为$ state.go(...),然后捕获了$ locationChangeSuccess事件,该事件将在位置发生变化之前触发路由匹配并调用控制器。
var TodoApp = angular.module('TodoApp', ['ngResource', 'ui.router'])
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/api/todo');
$stateProvider
.state('todo', {
url: '/api/todo',
controller: 'ListCtrl',
templateUrl: '/_todo_list.html'
})
.state('todo.details', {
url: '/{id:[0-9]*}',
views: {
'detailsColumn': {
controller: 'DetailsCtrl',
templateUrl: '/_todo_details.html'
}
}
})
.state('todo.edit', {
url: '/edit/:id',
views: {
'detailsColumn': {
controller: 'EditCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
.state('todo.new', {
url: '/new',
views: {
'detailsColumn': {
controller: 'CreateCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
;
})
;
TodoApp.factory('Todos', function ($resource) {
return $resource('/api/todo/:id', { id: '@id' }, { update: { method: 'PUT' } });
});
var ListCtrl = function ($scope, $state, Todos, todo.details) {
/*here is where i would make the change*/
$scope.$on('$locationChangeSuccess', function () {
$scope.search();
$route.reload();
});
$scope.todos = [];
$scope.search = function () {
Todos.query(function (data) {
$scope.todos = $scope.todos.concat(data);
});
};
$scope.search();
};
var DetailsCtrl = function ($scope, $stateParams, Todos) {
$scope.todo = Todos.get({ id: $stateParams.id });
};
var EditCtrl = function ($scope, $stateParams, $state, Todos, $location) {
$scope.action = 'Edit';
var id = $stateParams.id;
$scope.todo = Todos.get({ id: id });
$scope.save = function () {
Todos.update({ id: id }, $scope.todo, function () {
//here is where I would make a change
$location.path('todo.details').search($stateParams);
});
};
};
var CreateCtrl = function ($scope, $stateParams, $state, Todos, $location) {
$scope.action = 'Create';
$scope.save = function () {
Todos.save($scope.todo, function () {
//here is where I would make a change
$location.path('todo.details');
});
};
};
the $locationChangeSuccess event occurs before the route is matched and the controller invoked
$ locationChangeSuccess事件发生在匹配路由并调用控制器之前
#1
5
I would give an example (a draft) of HOW TO nest edit
into detail
. Well, firstly let's amend the templates.
我将举例说明(草稿)如何将编辑嵌套到细节中。好吧,首先让我们修改模板。
The Detail
template, contains full definition of the detail. Plus it now contains the attribute ui-view="editView"
. This will assure, that the edit, will "replace" the detail from the visibility perspective - while the edit scope will inherit all the detail settings. That's the power of ui-router
详细信息模板包含详细信息的完整定义。此外,它现在包含属性ui-view =“editView”。这将确保编辑将从可见性角度“替换”细节 - 而编辑范围将继承所有细节设置。这就是ui-router的强大功能
<section ui-view="editView">
<!-- ... here the full description of the detail ... -->
</section>
So, secondly let's move the edit state
, into the detail
所以,其次让我们将编辑状态移动到细节中
// keep detail definition as it is
.state('todo.details', {
url: '/{id:[0-9]*}',
views: {
'detailsColumn': {
controller: 'DetailsCtrl',
templateUrl: '/_todo_details.html'
}
}
})
// brand new definition of the Edit
.state('todo.details.edit', { // i.e.: url for detail like /todo/details/1/edit
url: '/edit',
views: {
'editView': { // inject into the parent/detail view
controller: 'EditCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
Having this adjusted state
and template
mapping, we do have a lot. Now we can profit from the ui-router
in a full power.
有了这个调整后的状态和模板映射,我们确实有很多。现在我们可以充分利用ui-router获利。
We'll define some methods on a DetailCtrl
(remember, to be available on the inherit Edit state)
我们将在DetailCtrl上定义一些方法(记住,在继承Edit状态下可用)
var DetailsCtrl = function ($scope, $stateParams, Todos) {
$scope.id = $stateParams.id // keep it here
// model will keep the item (todos) and a copy for rollback
$scope.model = {
todos : {},
original : {},
}
// declare the Load() method
$scope.load = function() {
Todos
.get({ id: $stateParams.id })
.then(function(response){
// item loaded, and its backup copy created
$scope.model.todos = response.data;
$scope.model.original = angular.copy($scope.model.todos);
});
};
// also explicitly load, but just once,
// not auto-triggered when returning back from Edit-child
$scope.load()
};
OK, it should be clear now, that we do have a model
with the item model.todos
and its backup model.original
.
好了,现在应该很清楚,我们确实有一个带有item.todos项目及其备份模型的模型。原文。
The Edit controller could have two actions: Save()
and Cancel()
编辑控制器可以有两个操作:Save()和Cancel()
var EditCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Edit';
// ATTENTION, no declaration of these,
// we inherited them from parent view !
//$scope.id .. // we DO have them
//$scope.model ...
// the save, then force reload, and return to detail
$scope.save = function () {
Todos
.update({ id: id })
.then(function(response){
// Success
$scope.load();
$state.go('^');
},
function(reason){
// Error
// TODO
});
};
// a nice and quick how to rollback
$scope.cancel = function () {
$scope.model.todos = Angular.copy($scope.model.original);
$state.go('^');
};
};
That should give some idea, how to navigate between parent/child states and forcing reload.
这应该给出一些想法,如何在父/子状态之间导航并强制重新加载。
NOTE in fact, instead of Angular.copy() I am using lo-dash _.cloneDeep()
but both should work
注意事实上,而不是Angular.copy()我使用lo-dash _.cloneDeep()但两者都应该工作
#2
5
Huge thanks for Radim Köhler for pointing out that $scope
is inherited. With 2 small changes I managed to solve this. See below code, I commented where I added the extra lines. Now it works like a charm.
非常感谢RadimKöhler指出$ scope是继承的。通过2次小改动,我设法解决了这个问题。看下面的代码,我评论了我添加额外行的地方。现在它就像一个魅力。
var TodoApp = angular.module('TodoApp', ['ngResource', 'ui.router'])
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/api/todo');
$stateProvider
.state('todo', {
url: '/api/todo',
controller: 'ListCtrl',
templateUrl: '/_todo_list.html'
})
.state('todo.details', {
url: '/{id:[0-9]*}',
views: {
'detailsColumn': {
controller: 'DetailsCtrl',
templateUrl: '/_todo_details.html'
}
}
})
.state('todo.edit', {
url: '/edit/:id',
views: {
'detailsColumn': {
controller: 'EditCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
.state('todo.new', {
url: '/new',
views: {
'detailsColumn': {
controller: 'CreateCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
;
})
;
TodoApp.factory('Todos', function ($resource) {
return $resource('/api/todo/:id', { id: '@id' }, { update: { method: 'PUT' } });
});
var ListCtrl = function ($scope, $state, Todos) {
$scope.todos = [];
$scope.search = function () {
Todos.query(function (data) {
$scope.todos = $scope.todos(data); // No concat, just overwrite
if (0 < $scope.todos.length) { // Added this as well to avoid overindexing if no Todo is present
$state.go('todo.details', { id: $scope.todos[0].Id });
}
});
};
$scope.search();
};
var DetailsCtrl = function ($scope, $stateParams, Todos) {
$scope.todo = Todos.get({ id: $stateParams.id });
};
var EditCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Edit';
var id = $stateParams.id;
$scope.todo = Todos.get({ id: id });
$scope.save = function () {
Todos.update({ id: id }, $scope.todo, function () {
$scope.search(); // Added this line
//$state.go('^'); // As $scope.search() changes the state, this is not even needed.
});
};
};
var CreateCtrl = function ($scope, $stateParams, $state, Todos) {
$scope.action = 'Create';
$scope.save = function () {
Todos.save($scope.todo, function () {
$scope.search(); // Added this line
//$state.go('^'); // As $scope.search() changes the state, this is not even needed.
});
};
};
#3
3
I might have faced a similar problem the approach i took was to use $location.path(data.path).search(data.search);
to redirect the page then in the controller I caught the $locationChangeSuccess event. I other words I use the $location.path(...).search(...)
as apposed to $state.go(...)
then caught the $locationChangeSuccess event which will be fired when the location changes occurs before the route is matched and the controller invoked.
我可能遇到了类似的问题,我采用的方法是使用$ location.path(data.path).search(data.search);重定向页面然后在控制器中我捕获$ locationChangeSuccess事件。换句话说,我使用$ location.path(...)。search(...)作为$ state.go(...),然后捕获了$ locationChangeSuccess事件,该事件将在位置发生变化之前触发路由匹配并调用控制器。
var TodoApp = angular.module('TodoApp', ['ngResource', 'ui.router'])
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/api/todo');
$stateProvider
.state('todo', {
url: '/api/todo',
controller: 'ListCtrl',
templateUrl: '/_todo_list.html'
})
.state('todo.details', {
url: '/{id:[0-9]*}',
views: {
'detailsColumn': {
controller: 'DetailsCtrl',
templateUrl: '/_todo_details.html'
}
}
})
.state('todo.edit', {
url: '/edit/:id',
views: {
'detailsColumn': {
controller: 'EditCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
.state('todo.new', {
url: '/new',
views: {
'detailsColumn': {
controller: 'CreateCtrl',
templateUrl: '/_todo_edit.html'
}
}
})
;
})
;
TodoApp.factory('Todos', function ($resource) {
return $resource('/api/todo/:id', { id: '@id' }, { update: { method: 'PUT' } });
});
var ListCtrl = function ($scope, $state, Todos, todo.details) {
/*here is where i would make the change*/
$scope.$on('$locationChangeSuccess', function () {
$scope.search();
$route.reload();
});
$scope.todos = [];
$scope.search = function () {
Todos.query(function (data) {
$scope.todos = $scope.todos.concat(data);
});
};
$scope.search();
};
var DetailsCtrl = function ($scope, $stateParams, Todos) {
$scope.todo = Todos.get({ id: $stateParams.id });
};
var EditCtrl = function ($scope, $stateParams, $state, Todos, $location) {
$scope.action = 'Edit';
var id = $stateParams.id;
$scope.todo = Todos.get({ id: id });
$scope.save = function () {
Todos.update({ id: id }, $scope.todo, function () {
//here is where I would make a change
$location.path('todo.details').search($stateParams);
});
};
};
var CreateCtrl = function ($scope, $stateParams, $state, Todos, $location) {
$scope.action = 'Create';
$scope.save = function () {
Todos.save($scope.todo, function () {
//here is where I would make a change
$location.path('todo.details');
});
};
};
the $locationChangeSuccess event occurs before the route is matched and the controller invoked
$ locationChangeSuccess事件发生在匹配路由并调用控制器之前