AngularJS - Using service to update view when user goes offline - javascript

I'm building an app that needs to detect when a user loses internet connectivity or cannot reach the server. Multiple controllers and services need to be able to check and set this. I have achieved all of this with no problem using an angular service and
window.addEventListener('offline', function() {OfflineService.checkIfOnline});
then in the service with something like
window.navigator.onLine ? online = true : online = false
The tricky part comes in when I need to update the view when the offline event occurs. I can't seem to find a way to update the scope property or a controller property when the service property gets updated by the event.
When I use $scope.$watch, the function fires 10 times (noted by console.log) and then never again.
I tried to replicate the problem in a jsfiddle, but this is my first time using that tool, and I'm not sure if I did it right:
https://jsfiddle.net/m3nx5yLm/1/
Thank you for your help.

Thank you everyone for your help.
I ended up going with a solution suggested by a buddy of mine. Using $rootScope.$emit('offlineEvent' true); in the service and listening for it in the controller with $rootScope.$on('offlineEvent' this.setControllerProperty);.

https://jsfiddle.net/m3nx5yLm/3/
constructor($scope, OfflineNotificationService){
Looks like you were referencing the class from the scope not the instance created by the injector (needed to pass it in along with $scope). I also used the watch syntax where the first arg is a function just to be clear about making that call, the string syntax is typically just used to reference properties on scope. A few other notes you can just return the window.navigator.onLine and you can store the value on the service instance and reference it directly from the view, you can then call checkOnline periodically with a $timeout loop or listening for the online/offline events on the browser instead of using the watch to fire the function.
https://jsfiddle.net/m3nx5yLm/4/

Related

Where to place init code with Angular 1?

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.

Angular - initiate a response when data loads/changes

Relative Angular newbie here, and I am wrestling with what would seem like something most applications need:
Watching a model/data and doing something when that model is hydrated and/or has a state change.
Use case would be, when a user logs in (user model gets initiated) a complimentary directive/controller sees the state change, and then requests out to the backend to get a list of this users corresponding data elements (ie Notifications, emails, friends, etc)
Ive parsed through StackOverflow and such, and it always appears that a shared service is the way to go, however I never find a definitive answer about how the directives are to watch the state change. Some suggest a broadcast/watch while others say that is a bad pattern.
Our app currently does employ a shared UserService, which contains model representation of a User (data and simple methods is fullName())
This service also has a subscription hook that directives can subscribe to
onLogin: (fn) ->
$rootScope.$on userService::login, fn
and the use is:
UserService.onLoad(myFunction)
When the UserService loads the User, it then broadcasts userService::login and all the listeners are run. Hence everyone that shares the UserService can subscribe and respond to a User logging in.
This all works. But I was thinking there must be a built in Angular way that the directives can just know about the state change and then do myFunction (ie make additional data calls)
Thoughts and feeling would be extremely appreciated!

Angular factory variable changes not reflected in controller

I've been trying to use a factory in my Angular app to be able to share and update variables between controllers, however I haven't had much luck. The app I'm working on basically receives messages into an inbox and then I can decide whether to show them on a visualizer for people to see. It's a real-time interactive app that would be used during a conference or something when the speaker wants to interact with the audience and see their responses to a question/statement.
I'm using a factory to keep track of the messages in the queue that will be displayed on the visualizer page. When I add a message into the queue from the inbox, it appears that the factory is being updated. However, on the visualizer page, the scope variables that are referencing the factory aren't being updated.
Here is my factory code:
angular.module('tellApp')
.factory('VisualizerFactory', function () {
return {
queue: [],
active: {},
addToQueue: function(msg){
this.queue.push(msg);
}
}
});
Basically, from my inbox, I call the addToQueue function and pass in an object containing the message and some details. On the visualizer page all I'm doing is just looking at the VisualizerFactory object to see if it's updating and it's not. It just returns the empty VisualizerFactory object.
Thanks for any help!
The issue is most likely that you have a copy of the factory in your scope, which won't be updated since it's a different reference.
You can perhaps trigger a message from the service to notify the controller instance.

How do I register a listener for a onMessage event on a Port with chrome.dart

I have a chrome extension and I want to use the chrome.runtime.connect functionality to create a connection between my pageAction and the background page. I got stuck while trying to register a listener for the incoming messages.
My question is that I am looking for a working example of exchanging messages over a connection in chrome.dart.
I started by listening for newly created connections. chrome.runtime.OnConnect is an event stream, so I can just listen.
chrome.runtime.onConnect.listen((chrome.Port p) {
...
}
Now, the Port instances have an accessor p.onMessage, which is a chrome.ChromeEvent object. ChromeEvent instances provide a method with the following signature Future addListener().
I tried messing around with the p.onMessage and also the value returned from the future returned after calling p.onMessage.addListener, but I got nowhere. I have no idea what to put instead of the three dots.
The only thing that works for me is looking into the JavaScript API documentation and emulateing with dart:js what they do there.
p.onMessage.jsProxy.callMethod('addListener', [listener]);
while of course having previously defined the listener function as
listener(message, s) {
print('message: $message');
print('message.runtimeType: ${message.runtimeType}');
print('s: $s');
print('s.runtimeType: ${s.runtimeType}');
throw('halt');
}
I believe that the automatic API generation for Dart somehow messed up the API here.
There is actually a bug for this, https://github.com/dart-gde/chrome.dart/issues/135

Syncronizing Session data with other angular directives and controllers

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;
};
}];

Categories

Resources