Angular defines a run() block for an application module. This runs after config() and before any controllers and directives are loaded.
Is there a step in the lifecycle to run something after all controllers, directives, services, etc are loaded?
I need this in order to broadcast a message in my authorization pubsub service, and I want to ensure that everything is loaded before I publish the message. While I can check authentication in the run block (basically, just checking localstorage for a JWT via my authentication service), if I publish in the run() block I can't be sure if everything has loaded. I'm wondering if Angular exposes anything like this or whether I need to find a different solution.
Thanks!
An optional answer, taken from here
You can use a custom postDigest callback. if you need only the first postDigest callback, add a flag to indicate it happened
function postDigest(callback){
var unregister = $rootScope.$watch(function(){
unregister();
$timeout(function(){
callback();
postDigest(callback);
},0,false);
});
}
postDigest(function(){
console.log('do something');
})
Related
I am registering the service worker by using the Service-Worker-Allowed header with '/' value and specifying scope while registration.
Registering S/W:
navigator.serviceWorker.register('/myapp/js/serviceWorker.js', {scope: '/'})
^ This is successful.
Serving the S/W:
res.setHeader('Service-Worker-Allowed', '/');
The workflow is something like below, myapp is not directly called but is done behind the scenes without user seeing actual url.
User navigates to
www.domain.com/otherapp?callmyapp=true
and behind the scenes myapp www.domain.com/myapp is initiated where I am doing above registrations with root scope.
Once user navigates to
www.domain.com/otherapp/somethingelse&myapp=false
myapp is not available to the user to interact.
Earlier (while NOT using root scope) the service worker was getting redundant but now it is not. However it is marked deleted.
I don't receive any push event after this redirection.
serviceWorker.js
self.addEventListener("push", function (event) {
console.log('sw push event');
if (event.data) {
const notificationData = JSON.parse(event.data.text());
self.registration.showNotification(notificationData.title, notificationData);
}
});
From what i understand with scopes is if i register the service worker with root scope it should still be available to receive push event and not get marked deleted.
I see many posts around service worker and angular, might be worth mentioning that myapp is old school backbone/vanilla/dust JS while otherapp is Angular.
ANSWER:
The issue was the otherApp was un-subscribing all the service workers. Only if i knew. :/ It took a while for me to debug and conclude from the logs even after explicitly caching myApp files.
It isn't clear what you mean by "behind the scenes" or "not available to the user to interact". The best way to get help with these kinds of issues is to create a reduced example, as in something which demonstrates the bug with all the irrelevant stuff removed.
I've tried to recreate the system you're talking about. https://glitch.com/edit/#!/sw-scope-example?path=server.js:22:0.
If you visit https://sw-scope-example.glitch.me/install-service-worker/ it calls:
navigator.serviceWorker.register('/deep/path/to/sw.js', { scope: '/' });
That service worker has a header:
Service-Worker-Allowed: /
The service worker is really basic:
addEventListener('fetch', (event) => {
event.respondWith(new Response('This is a response from the service worker.'));
});
It registers fine. Now if you visit any URL on the origin, such as https://sw-scope-example.glitch.me/other-app/whatever, you'll see the response from the service worker.
This works fine, so there's some key difference between your example & mine. Can you remix the Glitch above so it demonstrates the issue you're seeing?
I have an angular web application that is currently calling the backend API every time I need to display the user name or user image. However, I would now like to be able to cache this information in localStorage when the application is first started. What would be the best way or best place to do this in Angular? I image it would be something equivalent to the jquery $(document).ready method. Any hints would be appreciated.
You could put it in the run block. This will run once when your app starts up
angular.module('myApp').run(function () {
//Run init code
});
While you can use module.run for this, a better option would be a thematically related service. Angular services are created once, and so you can just put your loading code on the top level.
E.g.
angular.module('myApp').service('currentUser', function() {
// load user data from local storage, if not found load from server
// then store in localStorage
this.name = /* loaded name */;
// etc...
});
Note that most of requests are asynchronous, so you might want to do a factory that would return a user promise instead.
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 our project we're using a lot of directives. The partials of those directives are defined with templateUrl. Now, we have directives that need to be loaded and reevaluated every time the user executes an action on the screen.
One use case:
The user logs in as admin and does something
The user logs out as admin and proceeds doing something else
The user logs in with his personal account
Every time those actions are executed every directive template should be loaded newly in order to show correct UI elements.
Unfortunately
$rootScope.$on('$stateChangeSuccess', function(event, next, current) {
// clear cache to trigger reloading
// (server decides if has permission or not)
if(next && next.templateUrl) {
if($templateCache.get(next.templateUrl)) {
$templateCache.remove(next.templateUrl);
console.log('removed: '+next.templateUrl);
};
};
});
in angular's run method doesn't do the job as it is only called on stateChange and is not concerned with directive templates.
Also, continous $templateCache.removeAll calls don't seem to be working either.
So, is there a way (or workaround) to disable caching of partials/templates altogether?
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;
};
}];