In an angularjs app, I am attempting to use a custom service to hold the app's main data, "items". However, I would like to bootstrap the data into the service upon the initial page load, in order to avoid a separate ajax request to the server to get it. What would be cleanest way to go about doing this?
Here is a snippet of my service for reference:
app.factory('items', function() {
var itemsService = {},
items = [];
itemsService.list = function() {
return items;
};
itemsService.add = function() {
/* ... */
};
return itemsService;
});
And on the backend I am using node.js + expressjs + jade, so I would be injecting the data into the page using:
!{JSON.stringify(items)}
Put your bootstrapped data from the server into a global JavaScript variable. Have your service assign items to that global variable (or copy the data into items).
How about something like this:
app.run(['items', function (items) {
items.load();
}])
This presumes your items service has a load function (or something like it) that does the actual Ajax work using either $http or $resource.
Related
I have a common service which serves configuration details from backend that I am using in other services...is there a way to load the common service before loading other services.
Since I am getting an exception in JS "cannot read property "url" of null". Sometimes It works fine if the configuration loaded successfully before other calls
It sounds like using module.run() would help solve your problem as you can initialise your common service:
var app = angular
.module('demoApp', [])
.run(['commonService', function(commonService) {
commonService.Init()
}]);
Few suggestions,
You can make use module.run function which will execute immediately after module.config function, make AJAX/HTTP call their and keep it in $rootScope/Service, or as angular constant.
Interceptors: you can also make use of the $http's interceptors, so when ever you makes an AJAX request we can configure to execute the interceptor first, so that we can check whether the configs loaded or not, if not then we can grab that first.
If you are using angular routing, then we can make use of resolve convention.
You can set up a service deferred, which holds a promise that gets resolved after initialization of the service is complete. Then when asking for a value of the service return a promise chained to the deferred promise:
angular.module('YourApp').factory("CommonService", function(){
var initDeferred = $q.defer();
var initialize = function() {
/* initialize your service, maybe do something async, maybe set local variables with obtained info
* then resolve deferred promise (maybe with obtained values)*/
initDeferred.resolve(/* initialized service data */);
};
var getUrl = function() {
return initDeferred.promise.then(
function(initializedServiceData) {
return initializedServiceData.url;
}
);
};
return {
initialize: initialize,
getUrl: getUrl
}
});
And don't forget to initialize the service. A good point to do so is the run() function of the module as Brian Giggle already suggested.
angular.module('YourApp', []).run(function(CommonService){
CommonService.initialize();
});
You are able then to retrieve the initialized data as a promise:
angular.module('YourApp').controller('YourController', function($scope, CommonService){
CommonService.getUrl()
.then(function(url){
$scope.url = url;
})
});
I am new to angular and I want to pass data from one page to another page using local storage.I have tried a lot but unable to find the correct answer
Use factory like below.
angular
.module('xx.services', [])
.factory('xxStorage', ['$window', function($window) {
return {
set: function(_key, _value) {
$window.localStorage[_key] = _value;
},
get: function(_key) {
return $window.localStorage[_key];
}
};
}])
You can use set function to save data in the first page, and then reuse using get function in the other page.
The best way is to use library which already wrapped localStorage functionality in angular area. So there are few such popular solutions like: ngStorage and angular-local-storage. I prefer first one.
Git page, here you can find all information about installing, and using.
Just for example here is sample
var app = angular.module('app', ['ngStorage'])
.config(['$localStorageProvider',
function ($localStorageProvider) {
$localStorageProvider.get('MyKey');
$localStorageProvider.set('MyKey', { k: 'value' });
}]);
I've created a service (angular.js) that represents a model in my application:
angular.module("MyApp").factory('ItemService', function() {
this.items = [1, 2, 3];
this.getItems = function() {
return items;
};
this.addItem = function(i) { ... };
this.deleteItem = function(i) { ... };
return this;
});
and my controller:
angular.module('ItemController', function($scope, ItemService) {
$scope.getItems = function() {
return ItemService.getItems();
};
});
Now I want to wire up my service to a REST backend that retrieves, adds, deletes the items permanently. So I want to get rid of the initialization of the items array in my service and want to retrieve it from the server instead. Some things I came across:
The getItems function gets called from Angular multiple times (like 5-6 times when the page loads up and more often when page elements change). So I want to avoid to make each time a REST api call in the getItems function.
I could create an init() method in my service that initializes the items array using a REST request. But how can I ensure that this initialization is done before getItems gets called.
I could create a condition in getItems() that checks if items array is null. If so, initialize it from backend, if not return the saved array.
Note: getItems is not the only function that uses the items array. e.g. addItem, deleteItem etc. use it too.
Many ways to do the same thing, but what is the correct/best way to do this using Angular.js?
I would do it by leveraging the $cacheFactory service that is available as part of the angular ng module. This service will help you put,remove and get objects from cache on the browser side.
Coming to your implementation :
1) Inject $cacheFactory in your service
angular.module("MyApp").factory('ItemService', ['$cacheFactory',function($cacheFactory)
2) Modify your getItems method using the basic caching pattern
var items = $cacheFactory.get('cachedItems')
if (!items){
//make a $http call
items = $http.get to your url
//set the retrieved object in cache
$cacheFactory.put('cachedItems',items);
}
return items;
3) In your other Add/Delete/Update methods i will clear the cache after your server side update is successful
$cacheFactory.remove('cachedItems');
You can find more details about $cacheFactory here : https://docs.angularjs.org/api/ng/service/$cacheFactory
Angular $resources (start from 1.2) https://docs.angularjs.org/api/ngResource/service/$resource
It provides wrapper to interact (CRUD) with your REST service and supports caching out of the box.
Example:
var User = $resource('/user/:userId', {userId:'#id'});
User.get({userId:123}, function(user) {
user.abc = true;
user.$save();
});
In my app, there is some data will be used by many views or controllers. You can think they are some data dictionary, like status map definition.
Now my solution is to get them in AppContoller, and put them into $scope or $ rootScope. (the $scope in AppController is the parent scope of all controllers, so the data is accessible in all controllers.)
But the problem is, it will be initiated with $resource asynchronously, so maybe they are not ready to be used. Because all data will be got with $resource. Sometime the other data is got before the necessary global data.
Now in the controllers using the global data, I have to check the global data, and if it is not ready, call the initializing function later with timeout.
So, my question is, is there a better solution to initialize the necessary data used by all app?
Well, as far as i understand your problem, you need to ensure that when your app starts, you have some data fetched from server that can be used globally throughout your app.
My suggestion would be to go by the following approach,
Create a service to hold your global data.
var app = angular.module('myApp', []);
app.service('constantService', function() {
// All your constants declared here.
});
Now in your app.run method, make an ajax call or whatever you want to do, and initialize all the constants inside the service.
app.run(function($rootScope, $http, constantService) {
/*
Make an ajax call here and fetch all the Constants from your server.
var request = {};
request.method = 'POST';
request.data = {};
request.url = url;
var promise = $http(request);
var service = this;
promise.success(function(data, status, header, config) {
if (showLoader === true || showLoader === undefined) {
service.hideModal();
}
successCallback(data);
});
promise.error(function(data, status, header, config) {
});
*/
});
You can show a loading message while these constants are being loaded, to avoid user intervention during the call.
The best way of handling Firebase in AngularJS surely has to be from within a service, so it's available to all Controllers across the App.
I just can't get it to work! ... I first tried using angularFire(new Firebase(url)), hoping I could bind to the service's scope, but Angular complains that it cannot $watch it.
So I tried angularFireCollection instead like this:
app.factory('myService', function myService(angularFireCollection) {
var url = 'https://myfirebase.firebaseio.com';
return {
getAll: function(path) {
var ref = angularFireCollection(new Firebase(url + '/' + path));
console.log(ref);
return ref;
},
...
};
});
However, the angularFireCollection is an Object containing a load of methods etc. if I bind it to a controller $scope I just get garbage. It also complains that it can't call the Firebase functions before I try to use them (e.g. Error: Firebase.push failed: second argument must be a valid function.)... anyone got any ideas where I'm going wrong?
See this PLUNKER
If you want to encapsulate some of the functionality into a service, consider keeping the returned ref in state of the service. I expanded on your plunker. It seems to mostly do what you were trying for.
http://plnkr.co/edit/Uf2fB0
Jeff answered the question correctly ... I'm just posting a further development on Jeff's example for those who are interested.
I have abstracted the Firebase service creation, so you can dynamically create an instance of whatever Firebase service you want:-
var registerFirebaseService = function (serviceName) {
app.factory(serviceName, function (angularFire) {
var _url = null;
var _ref = null;
return {
init: function (url) {
_url = url;
_ref = new Firebase(_url);
},
setToScope: function (scope, localScopeVarName) {
angularFire(_ref, scope, localScopeVarName);
}
};
});
};
You first create an instance of the service as follows
registerFirebaseService('itemsService'); // create itemsService instance
Then you can inject the itemsService service into your controllers. The instance is initialised using your Firebase URL e.g.
itemsService.init('https://firebase.firebaseio.com/' + userId + '/items');
The Firebase can now be bound to your controller e.g.
itemsService.setToScope($scope, 'items');
adapted PLUNKER