Reputation: 181
I am learning AngularJS and ionic framework using this tutorial:
http://www.htmlxprs.com/post/12/tutorial-on-using-parse-rest-api-and-ionic-framework-together
Everything works well up until the point when I create a new item in the createTodo state and then call $state.go('todos') to go back to my items list, here is the code for the create Todo Controller:
.controller('TodoCreateController', ['$scope', 'Todo', '$state', function($scope, Todo, $state) {
$scope.todo = {};
$scope.create = function() {
Todo.create({content: $scope.todo.content}).success(function(data) {
$state.go('todos', {}, {reload: true});
});
}
}])
Here is the code for the item list controller:
.controller('TodoListController', ['$scope', 'Todo', '$state', function($scope, Todo) {
Todo.getAll().success(function(data) {
$scope.items = data.results;
});
$scope.deleteItem = function(item) {
Todo.delete(item.objectId);
$scope.items.splice($scope.items.indexOf(item), 1);
};
}])
Here are the states configured
.config(function($stateProvider) {
$stateProvider.state('todos', {
url: '/todos',
controller: 'TodoListController',
templateUrl: 'views/todos.html'
}).state('createTodo', {
url: '/todo/new',
controller: 'TodoCreateController',
templateUrl: 'views/create-todo.html'
});
})
When the application starts, the method of the TodoListController is invoked thanks to the last line added and the end of the .run method in the main app.js (or at least thats my understanding):
.run(function($ionicPlatform, $state) {
$ionicPlatform.ready(function() {
if(window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if(window.StatusBar) {
StatusBar.styleDefault();
}
});
$state.go('todos');
})
My problem is that, as soon as the new item is created and I invoke $state.go('todos') from the TodoCreateController, it takes me back to the item list but the new item is not there and the method of the TodoListController is never invoked, therefore leaving the list outdated.
How can I refresh the list in the 'todos' state after a new item has been created?
Upvotes: 6
Views: 10379
Reputation: 13007
I've encountered this and similar problems and it turned out that ionic view caching often was the reason for not calling the controller code when returning to a cached view.
Possible Solution
When separating controller code into one-time-initialization and code running when view is entered, you typically end with controllers looking like this:
// one-time initialization
$scope.updateList = function() {
Todo.getAll().success(function(data) {
$scope.items = data.results;
});
};
$scope.$on('$ionicView.beforeEnter', function () {
$scope.updateList();
});
This should solve your problem without having to trigger events or adding params or data to the route.
Alternative
It is possible to disable view caching in different ways:
cache: false
to the state definitioncache-view="false"
to the html <ion-view ...>
$ionicConfigProvider.views.maxCache(0);
(wouldn't recommend this)Pitfall when using Sub-States
Not in your case, but if editing/creating rows in Sub-States (e.g. states todo
for the list and todo.create
for creating a new todo), there is an additional problem coming from ui-router:
When navigating from todo.create
to state todo
, your super-state todo
won't be "executed", because you're already in this state when coming from todo.create
. This is a common problem and can be solved by one of the more complicated solutions provided in other answers to this post.
Upvotes: 3
Reputation: 104
This did the trick for me:
$state.transitionTo('new-state', $state.$current.params, {reload: true});
Upvotes: 2
Reputation: 181
I found a rather elegant solution:
Inside my TodoListController I define an event listener like this:
$rootScope.$on('todo:listChanged', function() {
$scope.updateList();
});
then comes the updateList() method
$scope.updateList = function() {
Todo.getAll().success(function(data) {
$scope.items = data.results;
});
};
And finally in the TodoCreateController I $emit the event upwards as soon as the item is created and right before changing state
$scope.create = function() {
Todo.create({content: $scope.todo.content}).success(function(data) {
$scope.$emit('todo:listChanged');
$state.go('todos');
});
};
And voila! The list updates accordingly and I can now use the same event if I delete or update a list item
Upvotes: 12
Reputation: 711
My suggestion:
In your config add:
$stateProvider.state('todos', {
url: '/todos',
controller: 'TodoListController',
templateUrl: 'views/todos.html',
params : {
updated : false
}
})
Read about params with ui-router here
Then in your $state.go of your create todo I would change it to this:
$scope.create = function() {
Todo.create({content: $scope.todo.content}).success(function(data) {
$state.go('todos', {updated: true});
});
}
In your todo controller I would add this
if($stateParams.updated == true){
Todo.getAll().success(function(data) {
$scope.items = data.results;
});
$stateParams.updated = false;
}
This way rather than counting on the reload to run the function you are explicitly looking for a value that will trigger the reload. You can also do the same thing using the data
object rather than the params
object and set/retrieve values via:
$state.get('createTodo').data.updated = true;
if($state.current.data.updated == true);
Hope this helps!
Another Thought
add a state change listener to the mix:
$rootScope.$on('$stateChangeSuccess', function(e, toState, toParams, fromState, fromParams) {
if(fromState.name === 'createTodo' && toParams.updated == true){
Todo.getAll().success(function(data) {
$scope.items = data.results;
});
$stateParams.updated = false;
}
}
Upvotes: 2