I am trying to create a "Todo App" with angularjs ui-router
. It has 2 columns:
- Column 1: list of Todos
- Column 2: Todo details or Todo edit form
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).
I have even tried $state.go('^', $stateParams, { reload: true, inherit: false, notify: false });
, no luck.
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('^');
});
};
};
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.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.the $locationChangeSuccess event occurs before the route is matched and the controller invoked
I would give an example (a draft) of HOW TO nest
edit
intodetail
. Well, firstly let's amend the templates.The
Detail
template, contains full definition of the detail. Plus it now contains the attributeui-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-routerSo, secondly let's move the edit
state
, into the detailHaving this adjusted
state
andtemplate
mapping, we do have a lot. Now we can profit from theui-router
in a full power.We'll define some methods on a
DetailCtrl
(remember, to be available on the inherit Edit state)OK, it should be clear now, that we do have a
model
with the itemmodel.todos
and its backupmodel.original
.The Edit controller could have two actions:
Save()
andCancel()
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