Reputation: 1860
I have a question, seeking advice, on how to get data that is stored in service arrays to the controller since unwrapping promises was removed in 1.2.
Example:
When route one initially loaded, the service would make a request to the server for the items, store the items in an array in the service so every request for route one after that would just return an array. When an item was saved, that item was pushed to the array in the service.
If I were to wait for the promise in route one's controller on the initial load, no problem because a response was sent back, but every request to route one after that would return an error because I was returning an array.
Any ideas on how to accomplish something like this in 1.2?
app.factory('Items',function($http) {
var items = [];
return {
list: function() {
if (items.length == 0) { // items array is empty so populate it and return list from server to controller
return $http.get('/?items').then(function(response) {
items = response.data.items;
return response.data.items;
});
}
return items; // items exist already so just return the array
},
save: function(item) {
return $http.post('/',{item:item}).then(function(response) {
items.push(item);
return response;
});
}
}
});
app.controller('RouteOne',function($scope,Items) {
Items.list().then(function(response) {
$scope.items = response;
});
/* used to be this before unwrapped promises were removed
$scope.items = Items.list();
*/
});
app.controller('RouteTwo',function($scope,Items) {
$scope.new_item = {};
$scope.addItem = function() {
Items.save($scope.new_item).then(function(response) {
$location.path('/'); // back to route one
});
};
});
Upvotes: 3
Views: 8999
Reputation: 2109
You can have the service return its own promise, which can can resolve either with the cached value or the result of the $http promise. I've not fully tested this, but it could look something like this:
app.factory('Items', function($q, $http) {
var items = [];
return {
list: function() {
var deferred = $q.defer();
if (items.length == 0) { // items array is empty so populate it and return list from server to controller
$http.get('/?items').then(function(response) {
items = response.data.items;
deferred.resolve(response.data.items);
});
} else {
deferred.resolve(items); // items exist already so just return the array
}
return deferred.promise;
},
save: function(item) {
return $http.post('/',{item:item}).then(function(response) {
items.push(item);
return response;
});
}
}
});
app.controller('RouteOne',function($scope,Items) {
Items.list().then(function(response) {
$scope.items = response;
});
});
Also, with your particular use case you could move the deferred to the service level instead of the function level because you'll only be calling it once, but the way I've written it is a little more flexible in case you want to clear the items array.
Upvotes: 5
Reputation: 10849
The main idea of this example is to use simple js to bind a reference to a model.
Items.save
must always return the reference to its local scope array items
var app = angular.module('app', ['ui.router']);
app.config(function ($stateProvider) {
$stateProvider
.state('one', {
url: '/',
template: '<ul><li data-ng-repeat="item in items">{{item.value}}</li></ul><a href=#/two>Route two</a>',
controller: 'RouteOne'
})
.state('two', {
url: '/two',
template: '<input data-ng-model="new_item.value"></input><button data-ng-click="addItem()">Add item</button>',
controller: 'RouteTwo'
});
});
app.run(function ($state) {
$state.go('one');
});
app.factory('Items',function($http) {
var items = [];
return {
list: function() {
if (items.length === 0) { // items array is empty so populate it and return list from server to controller
// i just modified a little the request for jsfiddle
$http.get('/echo/jsonp/?items=[{"value": 1},{"value": 2},{"value": 3},{"value": 4},{"value": 5}]').then(function(response) {
// you won't need next line with a real server
response.data = JSON.parse(response.data.items);
Array.prototype.push.apply(items, response.data);
});
// simply always return the reference to items
// as it will be populated later
return items;
}
return items; // items exist already so just return the array
},
save: function(item) {
return $http.post('/echo/jsonp/',{item:item}).then(function(response) {
items.push(item);
return response;
});
}
}
});
app.controller('RouteOne',function($scope,Items) {
// don't do anything as you the binding does the work for you
$scope.items = Items.list();
});
app.controller('RouteTwo',function($scope,Items, $location) {
$scope.new_item = {};
$scope.addItem = function() {
Items.save($scope.new_item).then(function(response) {
$location.path('/'); // back to route one
});
};
});
Upvotes: 2