I searched a lot about this, and although I found some similar problems, they are not exactly what I want.
Imagine this kind of code:
angular
.module('foobar', ['ngResource'])
.service('Brand', ['$resource', function($resource){
return $resource('/api/v1/brands/:id', { id: '#id' });
}])
.service('Product', ['$resource', function($resource){
return $resource('/api/v1/products/:id', { id: '#id' });
}])
.controller('ProductController', ['$scope', 'Brand', function($scope, Brand){
$scope.brands = Brand.query();
}])
.controller('BrandController', ['$scope', 'Brand', function($scope, Brand){
this.create = function() {
Brand.save({label: $scope.label });
};
}])
For the moment, I'm using $broadcast and $on
In brand controller:
Brand.save({label: $scope.label }, function(brand){
$rootScope.$broadcast('brand-created', brand);
});
In product controller:
$scope.$on('brand-created', function(brand){
$scope.brands.push(brand);
});
It works, but I don't like this way of sync'ing datas.
Imagine you have 10 controllers which must be sync'ed, you should write the $scope.$on part in each. And do the save for each services...
Is there a better way to keep collection sync'ed wherever they are used?
Yes - using a shared service. I've used http in my example, but you could fairly easily substitute it with $resource. The main point over here is to keep an in memory copy of the list of the brands. This copy is referred by the productscontroller and whenever it is updated it will automatically reflect. You simply need to ensure that you update it correctly after making the $http put / resource put call.
http://plnkr.co/edit/k6rwS0?p=preview
angular.module('foobar', [])
.service('Brand', ['$http', '$q',
function($http, $q) {
var brandsList;
return {
getBrands: function() {
var deferred = $q.defer();
//if we have not fetched any brands yet, then we'll get them from the api
if (!brandsList) {
$http.get('brands.json').then(function(response) {
brandsList = response.data;
console.log('brands:' + brandsList);
deferred.resolve(brandsList);
});
} else {
deferred.resolve(brandsList);
}
return deferred.promise;
},
save: function(newBrand) {
// $http.put('brands.json', newBrand).then(function(){
// //update the list here on success
// brandsList.push(newBrand)
// })
brandsList.push({
name: newBrand
});
}
};
}])
.controller('productsController', ['$scope', 'Brand',
function($scope, Brand) {
Brand.getBrands().then(function(brands) {
$scope.brands = brands;
})
}
])
.controller('brandsController', ['$scope', 'Brand',
function($scope, Brand) {
$scope.create = function() {
Brand.save($scope.label);
};
}
])
Related
For the subject matter, I found this SO post: Initialize service with asynchronous data
While this looks good, it is two years old and some of the references are out of date. Ex:
$http.get('url').success replaced with $http.get('url').then
Anyway, I have my model:
app.factory('User', function($http, $q) {
var myData = null;
var promise = $http.get('data.json').then(function (data) {
myData = data;
});
return {
promise: promise,
setData: function (data) {
myData = data;
},
doStuff: function () {
return myData.getSomeData();
}
};
});
and I need to share it across multiple controllers:
app.controller('controllerOne', function(User) {
// do stuff
});
app.controller('controllerTwo', function(User) {
// do stuff
});
app.controller('controllerThree', function(User) {
// do stuff
});
and each controller called from ngRoute:
app.config(function($routeProvider) {
$routeProvider
.when("/one",{,
controller: "controllerOne"
})
.when("/two",{
controller: "controllerTwo"
})
.when("/three",{
controller: "controllerThree"
});
});
... in no particular order
Now, there are a lot of posts that reference angular-deferred-bootstrap, but that library has not been updated since 2015. My question: Is angular-deferred-bootstrap still the preferred method to do this, or is there another / better way?
I am getting more familiar with ui-router and using .then as opposed to .success. However, I am having some difficulty passing in a service to resolve. Currently, resolve is blocking access to the controller. My goal is to get individual information from each user when they are selected.
I referenced the resolve through the ui-router wiki https://github.com/angular-ui/ui-router/wiki#resolve . Translations 2 seems to resemble how I am using my service
(function() {
'use strict';
angular
.module('app.orders')
.config(config);
function config($stateProvider) {
$stateProvider
.state('orders',{
url:'/customers:customerId',
templateUrl: './components/orders/orders.html',
controller: 'OrdersController',
controllerAs: 'ctrl',
resolve: {
customerFactory: 'customerFactory',
customerId: function( $stateParams, customerFactory) {
return customerFactory.getCustomers($stateParams.id);
}
}
})
};
})();
I am referencing the service call in customerId of resolve This is what I am passing to the ordersController.
Here is my service that is getting my customers.
(function() {
angular
.module('app.services',[])
.factory('customersFactory', customersFactory);
function customersFactory($http, $log, $q) {
return {
getCustomers: getCustomers
};
function getCustomers(){
var defer = $q.defer();
return $http.get('./Services/customers.json',{catch: true})
.then(getCustomerListComplete)
.catch(getCustomerListFailed);
function getCustomerListComplete(response) {
console.log('response.data',response.data);
// defer.resolve(response.data);
return response.data;
}
function getCustomerListFailed(error) {
console.log('error', error);
}
}
}
}());
Here is my controller. It is pretty small for right now. I am hoping to access it first. I do have customerId injected into it however.
(function() {
// 'use strict';
angular
.module('app.orders')
.controller('OrdersController', OrdersController);
function OrdersController($stateParams, customerId) {
console.log(customerId);
var vm = this;
vm.title = "Customer Orders";
vm.customer = null;
}
}());
******* Update *****
I found reviewed some code from when I was using ng-Route.
In this I used $route the same way that I am hoping to use $stateParams.
resolve: {
cityDetails:['cacFindCountry','$route', function(cacFindCountry,$route){
var countryCode = $route.current.params.countryCode;
console.log('cityDetails',cacFindCountry(countryCode))
return cacFindCountry(countryCode);
}],
countryNeighbors : ['cacFindNeighbors','$route', function(cacFindNeighbors,$route) {
var countryCode = $route.current.params.countryCode;
// pushes country code into neighbors
return cacFindNeighbors(countryCode);
}],
countryDetails : ['cacCountries', '$route', function(cacCountries, $route) {
var countryCode = $route.current.params.countryCode;
console.log(countryCode);
console.log(cacCountries(countryCode));
// need to get specific country info
// return cacCountries(countryCode);
return countryCode;
}]
}
I'm trying to load data from a service and update the view using $q, but it's not working. It works if I put the http call inside the controller, but I'd prefer it be part of the service.
Any help? Also, is there a better way to do this instead of promises?
Demo and code below.
---------- Fiddle Demo Link ----------
View
<div ng-init="getData()">
<div ng-repeat="item in list">{{item.name}}</div>
</div>
Controller
.controller('ctrl', ['$scope', 'dataservice', '$q', function ($scope, dataservice, $q) {
$scope.list = dataservice.datalist;
var loadData = function () {
dataservice.fakeHttpGetData();
};
var setDataToScope = function () {
$scope.list = dataservice.datalist;
};
$scope.getData = function () {
var defer = $q.defer();
defer.promise.then(setDataToScope());
defer.resolve(loadData());
};
}])
Service
.factory('dataservice', ['$timeout', function ($timeout) {
// view displays this list at load
this.datalist = [{'name': 'alpha'}, {'name': 'bravo'}];
this.fakeHttpGetData = function () {
$timeout(function () {
// view should display this list after 2 seconds
this.datalist = [{'name': 'charlie'}, {'name': 'delta'}, {'name': 'echo'}];
},
2000);
};
return this;
}]);
No need for ngInit or $q. This is how you should do it.
You should also not expose dataservice.list to the controller. That should be private to dataservice, which will contain most of the logic to determine whether to send the controller the existing list or update the list and then send it.
angular.module('app', [])
.controller('ctrl', ['$scope', 'dataservice', function ($scope, dataservice) {
loadData();
function loadData() {
dataservice.fakeHttpGetData().then(function (result) {
$scope.list = result;
});
}
}])
.factory('dataservice', ['$timeout', function ($timeout) {
var datalist = [
{
'name': 'alpha'
},
{
'name': 'bravo'
}
];
this.fakeHttpGetData = function () {
return $timeout(function () {
// Logic here to determine what the list should be (what combination of new data and the existing list).
datalist = [
{
'name': 'charlie'
},
{
'name': 'delta'
},
{
'name': 'echo'
}
];
return datalist;
},
2000);
};
return this;
}]);
Firstly, don't use ng-init in this way. As per the docs:
The only appropriate use of ngInit is for aliasing special properties
of ngRepeat, as seen in the demo below. Besides this case, you should
use controllers rather than ngInit to initialize values on a scope.
Secondly, promises are the perfect thing to use in this case, but you don't need to touch $q, as $http calls return promises for you.
To do this properly, simply return the $http result from the service:
this.getDataFromService = function() {
return $http(/* http call info */);
};
Then, inside you controller:
dataservice.getDataFromService().then(function(result){
$scope.list = result.data;
});
Also here is the updated fiddle: http://jsfiddle.net/RgwLR/
Bear in mind that $q.when() simply wraps the given value in a promise (mimicking the response from $http in your example).
I have abstracted my working code from a controller into a factory, but it doesn't seem to be working and I can't find what's wrong. I opted for a factory rather than a service because I wanted to execute some code that defined the variable before returning that variable; I want to get result.station (a part of the data returned by the API), not the full result.
This is my code:
var app = angular.module("myApp", []);
app.factory('api', ['$http',
function($http) {
var station_list = [];
$http({
method: 'GET',
url: 'http://api.irail.be/stations/?format=json&lang=nl'
})
.success(function(result) {
station_list = result.station;
});
return {
Stations: function() {
return station_list;
}
};
}
]);
app.controller("myController", ['api', '$scope',
function(api, $scope) {
$scope.station_list = api.Stations();
$scope.title = "Stations";
}
]);
and a working example.
Try this:
.success(function(result) {
angular.copy(result.station, station_list);
});
You had a small error, you were replacing the array instead of populating it. I used angular.copy instead of the assignment in your factory and it works
http://plnkr.co/edit/sqgKcFZAcClmkfdXHhrz
The problem is that you are dealing with asynchronous nature of AJAX.
I would suggest to have a delegate method in controller, which will be called when the service call is complete.
Something like the following:
app.controller("myController", ['api', '$scope',
function(api, $scope) {
api.Stations( function(station_list) {
$scope.station_list = station_list;
});
$scope.title = "Stations";
}
]);
The following is a service method excerpt:
return {
Stations: function(delegate) {
if (delegate)
delegate(station_list);
return station_list;
}
};
In my angular app I have a view, a controller and a service.
The service load resources ex:service load persons and initialize value with the result.
I want to load my view after my service finish his function to load his resources.
var myApp = angular.module('myApp',[]);
myApp.controller('PersonsCtrl', ($scope, Persons) {
$scope.persons = Persons.data;
});
myApp.factory('Persons', {
data: [[function that load resources => take time]]
});
So I want to load my controller when my service finish his initialization.
Some ideas?
Assuming you have a route provider, here's a basic example. When the promise is resolved, "personData" will be injected into your controller. There's not much info about what your service does, so I had to give something very generic.
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/persons', {
controller: 'PersonsCtrl',
templateUrl: 'persons.html',
resolve: {
personData: ['Persons', function(Persons) {
return Persons.getData();
}]
}
});
}]);
myApp.controller('PersonsCtrl', ($scope, personData) {
$scope.persons = personData;
});
myApp.factory('Persons', {
getData: function() {//function that returns a promise (like from $http or $q)};
});
Maybe try using promises, example below
var myApp = angular.module('myApp',[]);
myApp.controller('PersonsCtrl', ($scope, Persons) {
$scope.persons = Persons.getData().then(function(response){
//do whatever you want with response
});
});
myApp.factory('Persons', function ($http, $q) {
return {
getData: function () {
var def = $q.defer();
$http.get('url').
success(function (response) {
def.resolve(response);
})
return def.promise();
}
}
});