Shared state between controllers - angularjs
General status between controllers. I am having a hard time finding the right way to do this out of all the possible solutions recommended for SO. I made this sketch to illustrate the basic idea I've had about this so far, using a factory.
There is a factory myFactory
that contains a shared variable sharedVar
.
Controllers Ctrl1, Ctrl2, Ctrl3
want to always get an updated version. They can also call updateViaHttp
.
-
Is this the correct factory target ? (in general, share by state, specific to other parameters such as service and provider).
-
If so, what is the correct way to track changes
sharedVar
? (from object reference, $ watch, events (broadcast, on), ...) - Is there a general pattern that works well for objects, arrays and primitives .
source to share
-
You have the basic idea right, assuming "factory" you mean "service" - this seems to be confusing because services are declared using factory functions. However, it is an important distinction to make it easier for you to find documentation, etc.
-
Watch for changes, either with object references, or with caution about viewing depth in Angular (my preferred method), or by explicitly logging $ watch statements (be careful with watch depths anyway). In general, I am of the opinion that you should not overuse things as it can make your code a little messy. It also leads to the defeat of the service point in this case, which should be the source of the general state.
-
My general pattern for creating services is to bind whatever I want to use with an object (both data and functions) and then return that object to a factory function. Sometimes you need to introduce some extra nesting to keep prototypal Javascript inheritance from messing with you (look at the clock depth again), but that's the general idea.
Sample service for your setup:
angular.factory('shareAndUpdate', ['dependencyInjection', function(dependency) {
var srvc = {};
srvc.sharedVar = 'something';
srvc.updateViaHttp = function(){ something };
return srvc;
}]);
source to share
You are probably looking for something like the pubsub service:
https://www.npmjs.com/package/angular-pubsub
However, in my experience, with proper design, you can minimize the need for data sharing between non-nested controllers.
This is sometimes unavoidable, for example for files like credentials, permissions, etc. that span all applications. In such cases, you can use a service to actually share / receive state between controllers, or you can go for a full fledged pubsub mechanism.
-
A factory is another way to specify a service. A factory, when called, provides a service instance. This service, which you can use for whatever you want, is one of those things that share state between your controllers.
-
You can view a shared variable in many ways, the easiest to inherit is to inherit scopes, but as you mentioned sometimes your controllers don't necessarily inherit their scopes. Then you can use the pubsub service, or you can just pass events in a common scope for both controllers (for example, $ rootScope, which is the parent of all controller scopes for your application).
-
If you were using an existing pubsubservice, it would still be up to your executive controllers to actually subscribe and look at the specific variable and update the appropriate scopes accordingly. However, this can be avoided if you design your application so that your controllers inherit the variable from the shared scope. They will then automatically update their stuff using the normal angular engine. This, unfortunately, cannot always be achieved, and then what is left for you is to execute the pubsub service.
source to share
factories and services versus vendors - the only differences are in how the Dependency Injector provides them with instances. Services are specifically designed to provide single-color packages, but they are just wrappers over factories that add a single, specific functionality. Nothing stops you from returning a singleton from a factory.
using services to share state - you need a singleton and the service makes it easy to define and inject singlets.
SomeService:
var foo = {
bar = 'a';
};
function getFoo() {
return foo;
}
SomeController[SomeService injected here] :
$scope.foo = SomeService.getFoo();
$scope.$watch('foo.bar', ...);
$scope.setFooBar = function(val) {
$scope.foo.bar = val;
};
<a href="" ng-click="setFooBar('2')">2</a>
The big picture here is to never do
$scope.foo = { bar: 'Some other reference' };
, because then all your other stuff that depends on SomeService won't get a new reference when you overwrite it - the infamous always uses the dot in the $ scope stuff release .
source to share