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
}]);
Related
I am building an Angular app that uses long polling to receive fast updates whenever something changes on the server. I use $resource like this to fetch the actual data:
appServices.factory('Data', ['$resource',
function(){
return $resource('', {}, {
query: {"url": …, isArray: false}
});
}]);
Then I have a service that takes care of the long polling: Wait that the data are loaded; store them somewhere; after one second, start the next long-polling cycle:
app.factory(„DataLoader“, [„Data“, "$timeout", function(Data, $timeout) {
return {
loadData: function() {
var parent = this;
var data = Data.query({},
function(result) {
/* do something to the data,
* then start waiting for an update from the server again
*/
$timeout(function() {
parent.loadData();
}, 1000);
}
);
}
};
});
It works like a charm so far.
However, I am now trying to write Protractor tests for this. The problem is: The server times out the long polling requests after 30 seconds only if there are no changes to the data. As I am waiting for new data inside $timeout, Protractor times out before any results arrive.
I have googled the last hour, but there doesn't seem to be a solution except for using $interval instead of $timeout. This works in a good old polling setup (poll every 3 seconds, get empty results from the server if there's nothing new). However, to avoid exactly that, I implemented long polling. $timeout is just the much more sensible option for me.
Can you give me any tips how to get Protractor running successfully in this environment?
I would suggest taking a look at Protractor's documentation on timeouts. You will probably need to increase your allScriptsTimeout in your configuration file since the default wait for page synchronization is 11 seconds.
Use $interval angular api and $fetch for polling data continuously in your app
If you dont want to use $interval instead of $timeout; which is not a good practice,
then you have to turn off the browser synchronization with angular browser.waitForAngularEnabled(false) and use either browser.sleep() or browser.wait() to achieve synchronization between the elements that you interact with on the page.
I have been struggling to find a consistent and good example of a put operation using AngularJS $resource. An example of when I want to update, but can't seem to is located here: AngularJS PUT on voting application to REST Service
At the core, I need to understand the best practice/normal way to conduct a put operation both for form submissions or in the voting application mentioned in my post above. Does anyone have a good example that demonstrates a put?
If you're creating a new entity in your data store you want to use POST/save. If you're updating the data associated with an already existing entity in your data store you want to use PUT/update. Patch is usually reserved for when you just want to update a subset of the entity data.
Look at the RFC
Several applications extending the Hypertext Transfer Protocol (HTTP)
require a feature to do partial resource modification. The existing
HTTP PUT method only allows a complete replacement of a document. This
proposal adds a new HTTP method, PATCH, to modify an existing HTTP
resource.
You would supply an id with both PUT and PATCH operations. You would not supply one with a POST operation.
When we load our angular forms it is done one of two ways usually. If the form is loaded when we are creating a new entity then we won't have an id. We will know this in the controller and will call resource.save. If we supply the controller loading the form with an id that's used to pull data from an endpoint to populate the form, we now have the id we can use to do a resource.update or resource.patch operations depending on how much of the entity we are updating.
Here's an example save function that handles both update and save operations. Here we check to see if an id was supplied via the route before we make our resource call.
angular.module('appModule').controller('ExampleCtrl',
['$scope', '$routeParams',
function($scope, $routeParams) {
$scope.saveForm = function () {
//Do input validation before you make a resource call
if ($routeParams.id) {
//call resource update since we have an id
}
else {
//call resource save since we don't have an id
}
};
}]);
Here's the example from the angularjs documentation:
How to create a custom PUT request:
var app = angular.module('app', ['ngResource', 'ngRoute']);
// Some APIs expect a PUT request in the format URL/object/ID
// Here we are creating an 'update' method
app.factory('Notes', ['$resource', function($resource) {
return $resource('/notes/:id', null,
{
'update': { method:'PUT' }
});
}]);
// In our controller we get the ID from the URL using ngRoute and $routeParams
// We pass in $routeParams and our Notes factory along with $scope
app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
function($scope, $routeParams, Notes) {
// First get a note object from the factory
var note = Notes.get({ id:$routeParams.id });
$id = note.id;
// Now call update passing in the ID first then the object you are updating
Notes.update({ id:$id }, note);
// This will PUT /notes/ID with the note object in the request payload
}]);
I'm having a trouble looking for an answer for this situation.
Our system server has logs that keeps updating.
The view of the logs is terrible ! and very messy.
So I want to build a web application that will show the logs and will offer filtering options to make our life easier :).
So this is what I've planned:
1) I'll load up to 50 log lines (so it wont be blank when opened);
2) I'll keep pulling information for every line the console write.
Well, How do I approach step 2? how can I be synced with the server logs?
furthermore, When I load the 50 lines maybe a new line alrdy had been put out in the console, which lower the integrity of data provided in the log viewer.
BTW I use PuTTY to see the log console.
Excuse me for my bad English. Thanks from advance :)
Ok, so if you are planning on doing this entirely client side with Angular, you're going to need to:
Create an API, some endpoint/url that you can fetch your logs from. Something like http://www.example.com/my-console-feed/. You might already have this.
Poll the server with angular's $interval at the interval of your choosing.
If you were operating with web sockets, perhaps some continuous feed could be done.
So you could make a factory that looks something like this:
(function() {
'use strict';
angular
.module('myApp')
.factory('pollingFactory', pollingFactory);
function pollingFactory($http, $interval) {
return function() {
// 60000 is 1 minute
var duration = 60000;
// log API endpoint
var myLogAPIEndpoint = 'http://www.example.com/my-console-feed/';
// this will hit the API every minute
$interval(function() {
// poll the api for new logs
$http({
url: myLogAPIEndpoint,
method: 'GET'
})
.success(function(response) {
// do something with the response
})
}, duration);
}
}
})();
If you were going to go this way, I'd use $q as a promise library and in your controller, when the promise resolves, update the model in your scope with the new logs. If you're using ng-repeat, I'd also recommend using track by for performance if the logs have unique IDs.
Here is a fiddle polling an API every 3 seconds.
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.
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
});