My problem is part of a larger program but I extracted a simulation of the problem in a simpler jsfiddle.
I'm implementing a "detailed view" show/hide controller for a todo item. When I click on the link, a detailed view is shown or hidden. The detailed view state is stored in todo.showDetails. This works fine.
When I mark a todo as completed, I persist its current state on the server using:
// Persist immediately as clicked on
$scope.checkTodo = function (todo) {
todo.$save();
};
This causes my detailed view to go hidden as the todo.showDetails state is lost because it's not part of the persisted item (i.e., it's overridden with the server's view of the todo item, which doesn't contain the UI state todo.showDetails). I'm not surprised, this makes sense.
My question is: how can my todo items contain both local UI state and data that's persisted on the server? When I todo.$save(), I'd like the todo's data to be saved on the server while retaining the value of todo.showDetails in the client code. I.e., whenever I call todo.$save(), I'd like my UI state to remain unchanged.
Is there an idiomatic way to do this in AngularJS? I wouldn't want to persist UI values like showDetails on the server.
One way I've been thinking of implementing this would be to have a separate service that stores UI state for each todo item. When I need to access local state, instead of accessing like todo.showDetails, I could do something like AppState.lookup(todo.id).showDetails or similar. But perhaps there's a simpler way..
-- UPDATE 2013-02-25 --
I did as in the below answer and simply separated UI state from todo items created by $resource.get/query calls. It was a lot simpler than I thought (see commit):
My controller changes:
$scope.todos = Todo.query();
+ $scope.todoShowDetails = [];
+ $scope.toggleShowDetails = function (todo) {
+ $scope.todoShowDetails[todo.id] = !$scope.todoShowDetails[todo.id];
+ }
+
+ $scope.showDetails = function (todo) {
+ return $scope.todoShowDetails[todo.id];
+ }
Template changes:
- <label class="btn btn-link done-{{todo.done}}" ng-click="todo.showDetails = !todo.showDetails">
+ <label class="btn btn-link done-{{todo.done}}" ng-click="toggleShowDetails(todo)">
..
- <div ng-show="todo.showDetails" ng-controller="TodoItemCtrl">
+ <div ng-show="showDetails(todo)" ng-controller="TodoItemCtrl">
It's simple when I put it in code. My jsfiddle example is a little bit contrived, it makes more sense in the context of my todo app. Perhaps I should just delete the question if it's either difficult to understand or too trivial?
Is it possible for you to extract that UI state from your todo object? You'd still place the state object on the scope, just not inside the todo object. It sounds like that type of state doesn't belong in the todo object anyway, since you don't want to persist it.