Angularjs factory method implementation - angularjs

I am writing an angularjs factory to do some calculation. I named it as CalcEngineService This factory has one method which is used by controllers,directives. This factory method is called several hundreds of times.
My question is that the factory method requires collection of rules to do the calculation. I get this collection of rules from http service.When I use http call to get rules in side the method I see several hundreds of http calls and the performance is affected. How can I restrict the call to get rules collection to just once and also make sure any change to rules is also taken care of.
defaultRules = [];
function calculateDuedate(test) {
getDefaultRules().then(function(rules){
// DO calculations with rules and find the duedate and update test with duedate
});
return test.dueDate;
}
function getDefaultRules() {
return $q(function (resolve, reject) {
if (utility.isEmpty(defaultRules)) {
rulesService.getDefaultRules().then(function (rules) {
defaultRules = rules.data;
resolve(defaultRules);
});
}
else {
resolve(defaultRules)
}
});
}
I am fairly new to angularjs and dont know many features fully to take advantage of. Can anybody suggest a good way of handling this ?

You'll need to add some logic around your $http call, specifically once you've made one request don't make anymore. Wait for the one request to return and then continue your calculations.

Related

What is the right way to chain calls to services in AngularJS?

I need to invoke a Restful service endpoint to get an Id and once I got the result I should invoke a second Restful service endpoint through a angularjs service.
First call returns a customer ID, with this customer ID I need to search for additional info about that customer.
As I should call and and wait for promises I don't think nesting calls to promises is a good pratice. What I mean is that something like the below code isn't supposed to be a neat solution. Am I right or wrong? What should be a good way to achieve this task.
someService.someAction().then( function(data){
var id = data.data;
antoherCallToAnotherService.someOtherAction(id).then(function(data2){
//do some stuff
);
);
Thank you
This is quite typical approach. However, you can optimise it by chaining multiple promises:
someService.someAction().then(function(response) {
var id = response.data;
return antoherCallToAnotherService.someOtherAction(id);
})
.then(function(data2) {
// data is available
});
This is not that bad at all, however can be improved even more if you want. You should probably not make the first promise resolve with entire response object, underlying service should resolve with actual (maybe processed data). Then your code could become:
someService.someAction()
.then(antoherCallToAnotherService.someOtherAction)
.then(function(data) {
// data is available
});
For above to work, someService.someAction should resolve with response.data (which is an id in your case).

Angularjs caching vs fetching live data

I have a service I'm using to fetch data for an application.
Since a service is a singleton, I'd like to cache some reference data in the service, but I'm not sure how to get that to happen.
I'm using Strongloop and right now have a function in the Service like this:
function fetchReferenceData() {
return refInfo.find().$promise;
}
I'd like to have a property where I store the reference data.
I could add a property in the code, like
var myRefData;
and then I could edit fetchReferenceData() to check that property, but since the client is expecting a promise back, this won't work:
function fetchReferenceData() {
if (myRefData) { return myRefData; }
else { return refInfo.find().$promise;}
}
What's a good programming pattern to work with this kind of thing?
Do I pass in a function to call in a .then() to set the data in the client?
If the client is expecting a promise, you can use the $q service to create a promise from your cached value.
function fetchReferenceData() {
if (myRefData) { return $q.when(myRefData); }
else { return refInfo.find().$promise;}
}
The client can access it with the standard .then method.
fetchReferenceData().then (function (myRefData) {
//use myRefData
});
Best practices
You need a way to destroy the information in your cache.
You need to avoid calling refInfo.find() again if a query is already in progress.
Update for ngResource objects
Methods from Angular $resource services return references to empty objects.
From the Docs:
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data. This means that in most cases one never has to write a callback function for the action methods.
So you can cache the query reference but you need to be aware the data on it will be undefined for a period of time.
The $resource service also adds a $promise property that can be used by clients that accept a promise.
function fetchReferenceData() {
var cache;
return cache = cache || refInfo.find();
}
clientThatNeedsPromise(fetchReferenceData().$promise);

Pattern for returning data from Angular AJAX calls

Take a look at this method returned in a factory:
fetchEmployeeList: function() {
var q = $q.defer();
$http.get('/Employee/')
.success(q.resolve)
.error(ajaxErrorHandler.handleError);
return q.promise;
}
The author of this code says that this is the model we should use for returning data from HTTP endpoints. Basically, any time we need data from a service, we should use this pattern. The author is under the impression that this is preferred over returning $http.get()'s return value instead.
I don't understand the point of this code, though. I thought $http.get() does return a promise.
Can someone explain what this example snippet is doing or what they think the author might be trying to do?
That's the deferred anti-pattern, which practically warps a promise when not needed.
in your case returning a q.promise seems like an abuse since the HTTP object can return a promise itself.
I'd refactor the code to the following:
fetchEmployeeList: function() {
return $http.get('/Employee/');
}
you can take a look to this blog post for more reference as well,
don't be afraid to open a discussion with whoever is suggesting that approach.

Use return data from an angular service resource across all controllers with only one call

I have a custom Web API written in .NET that returns user's information that will be used in my AngularJS application. I want to be able to call my API once, and then use that returned JSON across my entire AngularJS application without having to recall the API within each of my controllers.
I currently have a factory of services, one of which returns all of the client's details I need to use the in rest of the services.
.factory('customApiService', function ($resource) {
return {
userInfo: function(userId, callback){
var api = $resource('../../api/UserInfo/:userId', {
userId: userId
}, {
fetch: 'JSONP',
'query': { isArray: false }
});
api.fetch(function (response) {
callback(response);
});
}
}
)
Now I don't want to call this userInfo service in every controller, but I do want the data to be passed into each without calling my API multiple times.
customApiService.userInfo(userId, function (d) {
var gaProfileId = d.gaProfileId;
var yelpId = d.yelpId;
var tripAdvisorId = d.tripAdvisorId;
var facebookPageName = d.facebookPage;
var twitterHandle = d.twitterHandle;
var clientName = d.clientName;
var searchlightAccountId = d.searchlightAccountId;
var searchlightDomain = d.searchlightDomainId;
}
You can try global variables .
use a $rootScope https://docs.angularjs.org/guide/scope
$rootScope is available in all controllers an templates .Just inject $rootscope in your controller or wherever required.
From what I read of your description and responses to other questions, it sounds like you're trying to make an asynchronous call before the rest of your app starts up. This is possible, but complex, and sort of voids the point of Angular in the first place. As I see it, you have two options:
QUICK HACK: If you really want this kind of behavior, why start your app at all? Do your request first, before you define your app in the first place, then define your app in the result handler for the request.
RIGHT WAY: Alter the behavior of your services and controllers to tolerate not having enough information to fully start. A lot of times this is less difficult than it sounds. Usually you can just chain a promise into their initialization block of code to wait for the data you need. Take a look at Brian Ford's "Angular Modal" project, at the lines of code I've highlighted here:
https://github.com/btford/angular-modal/blob/master/modal.js#L25-L36
This technique sets up a promise to return from the function. If the data it needs is already loaded from the service, it resolves the promise immediately. Otherwise, it makes the call to get what it's after, and you can see later (line 39) that the module uses promise.then() to wait until it has the data it needs to run.
This is a great pattern for both controllers and services when working with asynchronous data.
If using a $resource call instead, note that most $resource calls return a promise in a property called $promise. You can do something like this:
var MyController = function($scope) {
// This will get set soon!
$scope.myData = null;
var myResource = $resource('/path/to/:someId', { someId: '#id' });
myResource.get({ someId: 1 }).$promise.then(function(data) {
$scope.myData = data;
});
};
You can do more things in the .then() resolution callback for the promise, like initialize more parts of your controller, etc. There are also ways you can delay starting your entire controller until the resource is available. One really cool way is if you happen to be using the Angular ui-router module, there is a "resolve" option when defining each route. You can use that to call the $resource as shown above, and ui-router will wait to start your controller/view until it has what it needs.

AngularJS Download Settings from Servers

We have an endpoint on our API that includes a set of settings (like default text, other endpoints, etc.). Our frontend is written in AngularJS and we're trying to figure out the best way to get them back to the client, and make them available throughout all directives in the application. Right now our best solution is to include settings as a directive:
angular.module('ourapp')
.factory('settings', function ($http) {
var url = 'http://localhost:8080/settings';
return function (callback){
$http.get(url).success(callback);
};
});
But then all the other calls are wrapped asynchronously.
Is there a better way to do this?
Since the settings come asynchronously from the server, their availability will inherently be asynchronous. If your logic depends on the settings being available, then there is probably no better solution than using promises.
angular.module('ourapp').factory('settings', function($http) {
var url = 'http://localhost:8080/settings';
return $http.get(url); // returns a promise
});
You could use $route to resolve the promise before instantiating controllers. The settings would then be synchronously available in the controllers.
You can also simulate promise unwrapping, i.e. immediately (synchronously) returning an object, which later will be filled with real data. This is great for scopes and templates, and was previously a feature of Angular itself. Be aware that the simulated promise unwrapping may cause bugs if not used cautiously, because the settings data may or may not be there.
Example:
angular.module('ourapp').factory('settings', function($http) {
var url = 'http://localhost:8080/settings';
var settings = {};
$http.get(url).success(function(data) {
angular.extend(settings, data); // fills in data from server
});
return settings; // immediately (synchronously) returned
});

Resources