So I'm working on a Angularjs based mobile app. When a user taps on each of the tabs along the bottom a new route is called which is loading the partial and controller.
In my controllers I often then request data from the server via Angularjs $resource such as
$rootScope.getPage = pageApi.query(); // GET
$rootScope.getPage = $rootScope.getPage.$promise.then(function(data) {
//Do stuff with the returned data
});
I'm using root scope here because I have a spinning loader located outside of the controllers own $scope. While waiting for the response I'm using an Angular directive (Angular Busy) to display a loader.
However as the user moves through the pages on the app, more and more promises are started (that, for example, might not be resolved as the connection has been lost).
How can I resolve / reject any other promises so that only the most recently loaded controller is waiting on a promise ?
All comments / answers are really appreciated!
Thanks,
Mac
Related
How can i block request in node.js for HTML files when user was not authorized. I have html tree something like this:
app.use('/templates', express.static(path.join(__dirname, 'templates')));
index.html (for all)
panel.html (only for authorized)
settings.html (only for authorized)
Is any good and clearcode solutions for block this content ?? I think about something like this:
router.get('/templates/panel.html', function(req, res, next) {
if (req.session && req.session.user) {
// return content
} else {
// block content or send error.html
}
});
My ng-router config:
when('/panel', {
templateUrl: 'templates/panel.html',
controller: 'panelController',
// maybe controls authorize here to
}).
But what will happen when we use $templateCache?
Assuming you don't want other users to access some information, not to hide html templates.
Probably the best way would be to compile all your templates to $templateCache. Then inside your routing you could use the resolve property read here that:
An optional map of dependencies which should be injected into the
controller. If any of these dependencies are promises, the router will
wait for them all to be resolved or one to be rejected before the
controller is instantiated. If all the promises are resolved
successfully, the values of the resolved promises are injected and
$routeChangeSuccess event is fired. If any of the promises are
rejected the $routeChangeError event is fired. For easier access to
the resolved dependencies from the template, the resolve map will be
available on the scope of the route, under $resolve (by default) or a
custom name specified by the resolveAs property (see below). This can
be particularly useful, when working with components as route
templates.
this would allow you to do requests to server side where you will check for user privileges to open desired page.
Plus you would like to have some kind of authorization check on the server side when serving information for those pages-that-need-special-privileges.
It is also good idea to namespace your resources so authorization can be done easier and in 1 place.
I am using super heroic Angularjs in my application. I have a couple of pages like home.html, project.html, map.html. Each pages has a controller associated with it e.g. HomeCtrl, ProjectCtrl, MapCtrl. In my home.html user can create a project or navigate to project.html. In project.html I have list of all projects. When user clicks on a project he is navigated to map.html where I have some information regarding to project. I am using ngRoute for routing between the views. I am facing some challenges to share data across the controllers. I have created a service called dataFactory which stores all the data retrieved from backend. In my project.html view, I load all the data from backend and store them in service. When user lands into a project I use data stored in the dataFactory. The concern is that when user refreshes the page when he is on the map.html, all the data which are stored in dataFactory are wiped out and not loaded back cause data loading happens in the project.html page. I don't know how can I deal with it. I can not make a call to backend in every controller to get the data. I am planning to load the common data in app.run() method. But in that case I have to broadcast/emit the events to notify controllers, which will also be messier and will eventually lead to errors cause I know my application is going to be a huge application.
There is a solution but it needs some additional configuration while making routes in your $routeProvider, You can resolve your route when you have your data fetch from the backend. By using this method if a user refreshes the page on one of your project it will receive the data first then display that particular page.
First you have to modify your dataFactory something like this
app.service('MyService', function($http) {
var myData = null;
var promise = promise || $http.get('data.json').success(function (data) {
myData = data;
});
return {
promise:promise,
doStuff: function () {
return myData
}
};
});
Then in your $routeProvider you could make route to resolve when your data is fetched (that is receives its promise) more over by using this method it wont make another data call if your data is stored.
app.config(function($routeProvider){
$routeProvider
.when('/',{controller:'MainCtrl',
template:'<div>From MyService:<pre>{{data | json}}</pre></div>',
resolve:{
'MyServiceData':function(MyService){
return MyService.promise;
}
}})
.when('/b',{controller:'MainCtrl2',
template:'<div>From MyServic2e:<pre>{{data | json}}</pre></div>',
resolve:{
'MyServiceData':function(MyService){
return MyService.promise;
}
}})
})
I've a made a working Plunker for demo. Fell free to ask any question regarding this.
Hope it helps.
when you refersh the page, the app module loads again wiping out all the stored vars. You can use sessionStorage or localStorage to maintain data.
When you store your data into service/ factory store it in to your browser's local storage too. So, when user refresh the page, you can check whether your local storage has the data or not. If there is data in your local storage just save these data into your service/ factory.
That's all !!!
If you are struggle to implement these things please update me, I can help you on the code also.
In my Angular code, I have a code for long polling, that looks like this
var request = function() {
$http.post(url).then(function(res) {
var shouldStop = handleData(res);
if (!shouldStop()) {
request()
}
};
}
request();
The function gets called immediately after the page load.
However, now I am trying to set up testing in Protractor and I got this error message
Failed: Timed out waiting for Protractor to synchronize with the page after 11 seconds. Please see
https://github.com/angular/protractor/blob/master/docs/faq.md. The following tasks were pending:
In the docs, I read the following:
Before performing any action, Protractor asks Angular to wait until the page is synchronized. This means that all timeouts and http requests are finished. If your application continuously polls $timeout or $http, it will never be registered as completely loaded. You should use the $interval service (interval.js) for anything that polls continuously (introduced in Angular 1.2rc3).
How should I edit my code to use $interval? I thought that interval is an angular wrapper for window.setInterval, I am not sure how to use that for long polling.
Oh, the $interval thing in the docs belongs to the $timeout, not to the $http.
Well, I will just throw away Angular's $http and will just use fetch (with additional $rootScope.$apply and JSON deserialization) to do the same thing
I'm in the process of building out a fairly large Angular app and I've stuck to the design of building 'thin' controllers. My controllers don't try to do too much, they are each focused on one piece of functionality within my app.
There, however, is certain data that is 'shared' between controllers. I aim to avoid using $rootScope and instead rely on services to share data and 'state'.
When looking in the 'Network' tab of Chrome Dev Tools I notice certain services being called half a dozen times. So my question is, is this bad design? Are multiple calls to the same service within an Angular app not the 'Angular way' to do things? Note: these service calls take ~ 20ms each, so clearly not much of a performance hit...so far.
I'd suggest reducing the number of unnecessary HTTP requests that you're making for two reasons:
In production environments, these HTTP requests may take more time to complete when factors such as network latency or server load are taken into account; and
will delay the loading of any other assets such as images (assuming that they're coming from the same domain).
An approach that I've used when dealing with the scenario that you've described is to cache the response/data from the API in the service. So, on subsequent calls to the service, the data can be pulled from the cache rather than the API.
See a brief example below:
angular.module('app', ['ngResource'])
.factory('Post', ['$resource', function($resource) {
var posts = [];
var service = {
all: all
}
return service;
function all() {
// if cached posts exist, return those. Otherwise, make call to external API.
if (posts.length > 0) {
return posts.$promise;
} else {
posts = $resource('http://localhost/api/v1/posts.json').query();
return posts.$promise;
}
}
}]);
Note: you'll also have to consider resetting your cache however this would be dependent on your application logic.
Hope this helps!
L
In this case you should look to use $cacheFactory to reduce service calls.
Are you talking about REST services? Are you making $http calls in order to share data and state between controllers?
Why not use service/factory in Angular?
What you need is
1. DataCache service/factory - which will store your response from server
2. A directive - to call these services. include it in your different views
3. Now inside your service which is responsible for making http call first check if data is available in cache if yes return promise of stored data (you can use $q.when) if not make service call.
I have mentioned point 2 since I am assuming inside your various controller you must be doing something like AbcFactory.getItem().then() to avoid duplication of this code as you never know when the requirement will change since change is the only constant during development ;)
In my AngularJS application, I have a Session service object that contains stuff like the current user, their preferences, the current company they belong to, and the current theme that they are using. Many places in my application refer to the Session service when they need to get at this data.
Because these variables are in a service, I cannot use scope watches to detect changes. So instead, I've decided to use the observer pattern. Various places in the application, such as other services, directives, controllers, etc. will register themselves with the Session service and provide a callback to be executed whenever the Session changes.
For example, if the user changes their theme, the <style> element in index.html that uses a custom directive will be notified, and it will recreate all of the overriding css rules for the new colors.
For another example, whenever the user's avatar is updated, the main menu bar controller will be notified to refresh and redraw the avatar. Stuff like this.
Obviously the data in Session has to be refreshed at least once before the various controllers, directives, etc. use it. The natural place to ask the Session service to get its session data was in a run block for the application-level module. This works pretty well, but I don't think it's the best place either.
One problem I have noticed is that when Firebug is open, the asynchronous nature of things loading causes ordering issues. For example, the directive that runs on the <style> element will run AFTER the Session service has refreshed in the application's run block... which means the theme will not get updated after pressing F5 because the callback is registered after the initialization of the data occured. I would have to call a manual refresh here to keep it in sync, but if I did that, it may execute twice in the times where the order is different! This is a big problem. I don't think this issue is just related to Firebug... it could happen under any circumstance, but Firebug seems to cause it somewhat consistently, and this is bad.
To recap... This asynchronous ordering is good:
Theme Directive registers callback to Session
Menu Bar application controller registers callback to Session
Session.refresh() is called in .run block.
This asynchronous ordering is bad:
Menu Bar application controller registers callback to Session
Session.refresh() is called in .run block.
Theme Directive registers callback to Session, but callback does not get executed since Session.refresh() was already executed.
So rather than use the observer pattern, or refresh the Session state via a run block, what the best way to design the services, etc. so that the session data will ALWAYS get refreshed after (or maybe before) the various other parts of the application require it? Is there some kind of event I can hook into that gets executed before directives and controllers are executed instead of the run block?
If my approach is generally sound, what can I add to it to really make it work the way it should?
Thanks!
In angular.js you have 2 way of using global variables:
use a $rootScope
use a service
Using $rootScope is very easy as you can simply inject it into any controller and change values in this scope. All global variables have problems!
Services is a singletons(What you need)!
I think in your case you can use
$rootScope
And
$scope.$watch
Great answer
Is there a reason you can't access the variables directly like this:
app.factory('SessionService', function() {
var items = {
"avatar": "some url"
};
return items;
});
var MainController = [$scope, 'SessionService', function($scope, SessionService){
$scope.session = SessionService;
$scope.modifyAvatar = function(url){
$scope.session.avatar = "some new url";
};
}];
var HeaderController = [$scope, 'SessionService', function($scope, SessionService){
$scope.session = SessionService;
// You probably wouldn't do this, you would just bind
// to {{session.avatar}} in your template
$scope.getAvatar = function(){
return $scope.session.avatar;
};
}];