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.
Related
Someone posted something very similar to the following code on a thread about resources that I can no longer locate:
$scope.logout = function(){
var outRes = $resource("/admin/logout");
outRes.save({},
function () {
$scope.userObject.lastActivity_type = "Log out";
$rootScope.isLoggedIn = false;
$location.path("/login-register");
},
function errorHandling(err) {
console.log("Not cool, error");
});
}
It was super helpful for me on my assignment and seemed fairly recent so I was hoping to ask: could someone explain how to rewrite this with a broadcast method to let other users/every part of architecture know a user has logged out? The assignment's over and everything, I'm just curious
Assuming this is for Angular 1, broadcasting an event makes it visible to all child scopes of the scope in which it is broadcast. If you want it to be visible to all scopes, you should broadcast it in the root scope, as in this example:
$rootScope.broadcast('my-event');
Documentation is here if you want some more information.
EDIT: I note that you also asked about broadcasting to other users. That's a very different kettle of fish!
Angular is purely a front-end framework so it can't broadcast a message to another client. If you wanted something like a chat or notification system whereby users received notification of changes made by other users in real time, you'd probably want to use websockets via a library like Socket.io. You'd send some kind of notification to the server, either via websockets or an AJAX request, and then on the server that would trigger broadcasting the message to those connected clients that should receive it. You can then use the method described above to trigger a corresponding event in the root scope of your Angular code. I won't go into the details for this as there's loads of tutorials on doing this with Socket.io.
As we work in angular when we route from one url to another then controller data is Rest but service data is not reset.
Can someone please explain why its not reset. Any help is appreciated.
Thanks
Services are only instantiated once and every component depending on the service gets the same shared instance of it. Services are not "reset"/destroyed/torn down, they're permanent. Controllers are bound to scopes and come and go with the scope.
This in fact allows you to have a constant "backend" in the form of services which retain their state throughout the entire life cycle of the app, while controllers are temporary things bound to views which come and go as the GUI changes.
Angular services are:
Lazily instantiated – Angular only instantiates a service when an
application component depends on it.
Singletons – Each component
dependent on a service gets a reference to the single instance
generated by the service factory.
You can read more about it in Angular's documentation: https://docs.angularjs.org/guide/services.
This issue was a bit old but let me share on how I did to delete my data stored in service.
service.ts
data: any;//I made a storage variable like this so it will be undefined value by default
removeProductData() {
this.data= undefined;
}
//In your component just call this function `removeData()` wherever you want to delete your data.
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!
EDIT
The short version:
Say I have application data is many different services. How do I get around needing to inject all of those services into every controller that displays application state?
EDIT
I am building my first Angular application. The basic design is I have a home page that shows the value of about 5 different variables (which are each pretty complicated). While on this page the app is collecting and analyzing data from bluetooth. Occasionally, the these 5 variables and some bluetooth data are saved to a REST back end and also saved to the device. There are pages for each of these 5 variables to change their value.
I have done my best to follow best practices. I have very thin controllers. I use services for all my data. I really only use $scope for binding data between views and controllers.
My issue now is that I started with a global "State" service to keep track of those 5 variables. I inject into any controller that needs to display state, and bind the html to it. Any time I want to change any state, I call a method of that State service to do it. This worked well, but now that State service is getting huge.
I have tried to break functions out to other services, but I run into the issue of needing to read data from the State service, then writing back to other properties of the State service. If I inject the other service into State, I can't inject State into the other service too.
I have thought about how I could have many smaller services, but I keep coming back to when I save the data to the server. When I do that I need to gather up data from every corner of the application to send up. If all this information is stored in different services, I am left with injecting all of them into a single service once again.
As I write this, I am pretty sure I am missing a big concept with using $scope across an application.
Any pointers would be appreciated,
Thanks,
Scott
Could you divide things into sub-services, and then make the State service an aggregator for these sub-services, then instead of injecting State into the sub-services, you inject the specific sub-service that you need? E.g.:
var app = angular.module('services', []);
app.service('sub1', function(){
return {
// ...
}
});
app.service('sub2', function(sub1){
var data = sub1.getData();
data.prop = 'new_value';
sub1.setData(data);
return {
// ...
}
});
app.service('State', function(sub1, sub2){
var data = sub1.getData();
data.prop = 'new_value';
sub1.setData(data);
var data = sub2.getData();
data.prop = 'new_value';
sub2.setData(data);
return {
// ...
}
});
Looks like you need Redux to help you manage your application state
https://github.com/wbuchwalter/ng-redux
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;
};
}];