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
});
Related
I am using Angular's default $http cache in one of my services. When the user navigates from a view to another one (I am using ui-router), the cache invalidates and all of the items will be removed from it. I want to not invalidate the cache in the whole lifetime of my application.
EDIT:
For example, this factory does not return cached result after navigating to another route and it calls the server api to get the result:
cardModule.factory("myFactory", function ($http) {
return {
getAll: function () {
return $http.get("all", { cache: true })
}
}
});
How to prevent default cache from removing items from itself after a route change?
I found the source of the problem. It was my own fault. I had a code somewhere that clears the cache after the state change. There is no problem with default angular $http cache.
I would leave this as a comment but I don't have enough points yet..
Could you try some form of memoisation? In other words, have a model on the scope, then, if the model is undefined, trigger the $http call? Something like:
var ctrl = this;
ctrl.product = undefined; // or null
if (ctrl.product === undefined) { // or null
$http.get(...).then(function(resp) {
ctrl.product = resp.data;
};
};
This way the model gets initialized, and called just once. A possible downside would be that the if statement may make this inefficient.
I have not tested this, just throwing the idea out there. I am also very interested in this problem.
That should not be related to ui-router or $http. Here are a few things you need to confirm:
Is your server (which is serving your resources) is setting the cache header or not
Make sure you are not using Ctrl + F5 to refresh the page
If you are using Chrome browser, make sure a setting Disable cache is unchecked
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.
When does a Service / Factory retrieve data from an HTTP request?
When a factory is created in a service, I am curious as to when the HTTP request is sent, and how it is processed after the app is running for some time.
I'm writing my code using Ionic Framework. Once I initialize my app, and it stays open for a day or two, will the JSON data be refreshed at any interval? Or does it only refresh the data once the app is closed, and opened once again?
My requirement for the HTTP request, is that it is updated every day at the 00:01 AM.
I suppose my general question is: how does an HTTP request fetch data? And How does a service work in AngularJS.
Here is my code to retrieve a JSON package:
angular.module('starter.services', [])
.factory('menuJSON', function ($http) {
return {
all : function() {
return $http({
url: 'http://middmenuapi.herokuapp.com/',
method: 'GET'
})
}
}
});
Calls to $http() (or any of the aliases, such as $http.get() etc) invoke a web request immediately (barring any manipulation by inspectors or third-party components). It is analogous to issuing an XmlHttpRequest or JSONP request in other frameworks, like jQuery.
Services are created as singletons, so they are created once when they are first requested, and the same instance is injected from that point on. What the service does is entirely up to you, as Angular only deals with instantiating it, resolving any of its dependencies, and injecting it anywhere it's requested.
If you have an application that is running for long periods of time and needs to be updated with data, you'll need to architect it appropriately. Obviously I don't know the full requirements or particulars of your spec, but I can give you some hints to maybe get you going.
High level, it sounds like you need a function to operate on a timer (using the $timeout service), and if you meet or exceed a time window, invoke an $http request to retrieve the latest data and route it along to various components that need it. Then, it should mark the time frame it should next operate, then set a timeout again so it can wake further down the road and see if it's time to do work again.
The first thing to think about is where should this functionality live? If you only need it to happen in a certain controller, then you can do it all there in that controller using $timeout and $http. On the other hand, if you need to reuse this data in multiple places, you'll want to use a service. If you use a service, which is likely, then you need to figure out the best way to get those changes to the various parts of your app that need it.
My recommendation is to use Angular events on the $rootScope to $broadcast from your service when your $http request has updated data. Then, your various controllers, services, and directives that consume this data can subscribe to this event using $scope.$on and react appropriately. This keeps the service decoupled from the things that use it and allow them to react to changes easily.
All the service does is set a timeout, when it lapses check for data, if it has data, broadcast the data in an event on $rootScope, and set another timeout. The clients just listens and updates its local scope with the new data when it receives the event from the service.
This plunk contains a silly example. You'd want to change it to schedule work at a time of day or whatever you see fit, and then also have it make an $http request rather than send the current date.
angular.module("demo", [])
.service('myService', ['$rootScope', '$timeout', '$http', function($rootScope, $timeout, $http) {
var state = { timeout: null, next: null };
function work() {
var now = Date.now();
if (now >= state.next) { // you can replace with your own logic to schedule when it should occur (like a specific time of day). in this example, we poll every second, but do work every 5.
// this is where your $http service can do work, something like $http.get(...).success(function(data) { $rootScope.$broadcast('myService.data', data); });
$rootScope.$broadcast('myService.data', new Date());
state.next = now + 5000; // do work every five seconds
}
state.timeout = $timeout(work, 1000); // poll every second
}
return {
start: function() {
if (state.timeout) $timeout.cancel(state.timeout); // cancel pending timeout
work(); // first time will just schedule work to be done in the future
},
stop: function() {
if (state.timeout) $timeout.cancel(state.timeout); // cancel pending timeout
}
};
}])
.controller('DemoCtrl', ['$scope', function($scope) {
$scope.title = "Hello, World";
// here, the controller subscribes to the event, and when it occurs, it copies the event data to a local scope item
$scope.$on('myService.data', function(evt, data) {
$scope.$apply(function() {
$scope.myServiceData = data;
});
});
}])
.run(['myService', function(myService) {
myService.start(); // starts the service when the app runs
}]);
The use case is that I have a custom service which needs to be configured based on user input.
So I created a service provider for that service, but now I can only config the provider inside the module.config call, which I think it is loaded only once during the life of the app.
Any solution for this?
Have your service provide some sort of a configuration API to set these configuration values as needed. As a simple example you might do something like this:
function myController(myService, $scope) {
$scope.config = myService.config;
// You can manipulate various config options now through direct binding.
}
However remember that AngularJS services are singletons, which means they will all share the same state. If you need different state, or need a "new" one each time you will want to do something more like the way $resource or $http works which is basically a factory.
function myController(myService, $scope) {
$scope.config = { value1: 'default', value2: 'default' };
var thisService = myService($scope.config);
// You can manipulate various config options now through direct binding.
}
Just remember that services are basically objects and you can manipulate them as per your design as you need. So these are probably not the only, or even necessarily the best way to accomplish your goal. You have total flexibility here.
I don't think a service's provider is what you are looking for here because it is precisely just what you describe it.
As Chris stated, angular services are singletons. However, if you want your service to output "instances" based on user input, I like the following approach.
function myController(myService, $scope) {
var config = { value1: 'default', value2: 'default' };
$scope.newInstance=myService.create(config);
}
app.service('myService', [function(){
function serviceInstance = function (config){
//take config and return output object
}
return {
create: function(config){
return new serviceInstance(config);
}
}
}]);
I haven't thought about being able to manipulate the config variables as Chris suggested. I don't think it would work in my example but you could data bind to the $scope.newInstance
I'm writing a web application using AngularJS. I use a third-party library (that provides an Angular service) to fetch values from a database, and then use those to initialize some dropdown/select boxes on a page.
So, I have simple select boxes like this:
<div ng-controller="ChoiceCtrl">
<select ng-model="selectedFoo" ng-options="foo in foos"></select>
<select ng-model="selectedBar" ng-options="bar in bars"></select>
</div>
And a corresponding controller that initializes the choices for the select boxes. The service I'm using calls the given callback function after it receives values from the database. (The callback functions could be refactored into one but I'm using separate ones for clarity.)
angular.module('choice').controller('ChoiceCtrl', function($scope, ThirdPartyService) {
$scope.selectedFoo = '';
$scope.selectedBar = '';
$scope.foos = '';
$scope.bars = '';
var fooCallback = function(result) {
$scope.foos = result;
$scope.$apply;
}
var barCallback = function(result) {
$scope.bars = result;
$scope.$apply;
}
ThirdPartyService.asyncGetData('fetchFooOptions', fooCallback);
ThirdPartyService.asyncGetData('fetchBarOptions', barCallback);
});
The database calls are asynchronous and finish after the page has been rendered for the first time, so I manually call $scope.$apply in each callback function.
Is this the right way to initialize dropdown/select boxes in an AngularJS app when the values are fetched asynchronously when loading a page?
I've read tutorials saying that calling $scope.$apply manually is always a "code smell"... But since I'm fetching the values from a database, the operation happens "outside of Angular" which I believe makes those calls justified - and actually necessary.
I'm also wondering if the controller is the right place for these calls. In the tutorials I've read the options are always set in the controller but those sandbox examples never have an asynchronous database call happening.
You should modify three things in your code
The service should return a promise :Refer to documentation of angular for creating promise
Inside service resolve the promise when data is recieved from the server
Inside controller just assign proper values to bar and foos when promise is resolved
Remove $scope.apply since now you are modifying the values inside proper angular scope
Link:Use Promise and service together in Angular