How can I communicate a settings object from a controller to services?

angularjs, javascript, singleton, state

Solution

I have faced something similar in the past, and considered four possible approaches:

- Using `$broadcast`

- Storing settings on `$rootScope`

- Observer pattern (as you have here)

- Using `$watch` from a controller

Here are my thoughts on each:

$broadcast

In an AngularJS presentation I saw, Miško Hevery spoke about the use of `$broadcast` (i.e. events) and the use cases for such. The gist was that `$broadcast` is more intended for reacting to events that are not closely coupled with whatever you are working with, otherwise an alternative is likely preferable. Also on this subject, the Best Practices guide on the angular wiki recommends that:

Only use .$broadcast(), .$emit() and .$on() for atomic events: Events that are relevant globally across the entire app (such as a user authenticating or the app closing).

Here, as you have settings which are closely associated to whatever populates `ng-view`, it would suggest an alternative to using `$broadcast` is preferable.

$rootScope

This is a global state as you mention (and want to avoid). It wasn't/isn't my personal preference either to expose settings to my entire app, despite it often being the easy option. I personally reserve `$rootScope` for configuration settings and 'soft' variables, like page title etc. I wouldn't elect to use this option.

Observer Pattern

Registering callbacks against the Configuration factory is a solid approach. In regard to your persistent callbacks, you can listen for the `$destroy` event on the scope, calling a `remove` method on your Configuration factory to remove the callback. This could be considered a good example of how `$broadcast` be used; the controller is concerned with the event and must react to it, but the event itself is not specific to the data shared by the controllers/Configuration service.

$watch

By using a shared service, it can be injected it into any controller concerned with the settings. Right now, any change to the config will trigger your callback, when perhaps some views may only be concerned with one or two configuration settings. `$watch` will allow you to easier observe changes to only those attributes. I can't speak to the overhead vs registering callbacks, but this feels like the most 'angular' way to me.

This is how this could be implemented using `$watch`:

var app = angular.module("myApp",[]);

app.factory("Configuration",function(){
   var data = {
     settingOne: true,
     settingTwo: false 
   };
   return data;
})

app.controller("SettingsCtrl",function($scope, Configuration){
  // do something
})

app.controller("HomeCtrl",function($scope, Configuration){
   // detect any change to configuration settings
   $scope.$watch(function() {
     return Configuration;
   }, function(data) {
     // do something
   }, true)

   // alternatively only react to settingTwo changing
   $scope.$watch(function() {
     return Configuration.settingTwo
   }, function(data) {
     // do something
   })
})

Note that if you were to require a slightly more complicated Configuration factory, you could shift to using getter/setter methods and keep the config settings themselves private. Then, in the `$watch`, you should watch the method call instead of the property itself.

UPDATE:

At the time of answering, I preferred the approach of a `$watch` within a controller. After some time developing with the framework, I now try to keep `$watch` out of the controller altogether, instead preferring, where possible, to directly invoke a function at the point of change of the value, or through leveraging `ng-change`.

One reason for such is the complexity it adds to testing the controller, but perhaps moreso that it's inefficient: for every `$digest` cycle angular invokes, every registered `$watch` will be evaluated regardless, and it may very well be responding to a change made to a value with an existing `$watch`.

Rather than surmize the cons and solutions on this perspective, there is a very good article on exactly this issue here: Angular JS - you probably shouldn't use $watch in your controllers.

Problem

tl;dr; I need to communicate state which several services need and originates in data bound to the scope of a controller. What would a good and 'Angular zen' way to do so? Back story I'm developing a single page application and after much thought have decided to use AngularJS. The pages are laid out in a way similar to: The actual layout doesn't matter much, the concept remains the same for similar layouts. I need to communicate information that is bound to the scope of `SettingsController` to the services the controllers in the `ngView` require. I also need to update the content obtained from the service in the controller when the users make a modification to any slider. What I've tried The only way I've thought of is something like : http://jsfiddle.net/5sNcG/ where I have to write a binding myself and add listeners to the scope changing. I'm probably way off here and there is an obvious 'Angular' way of doing this - however despite my efforts I'm unable to find it. ``` /code from fiddle. var app = angular.module("myApp",[]); app.controller("HomeCtrl",function($scope,FooService,$interval){ FooService.change(function(){ console.log("HI",FooService.getFoo()); $scope.foo = FooService.getFoo(); }); }); app.factory("Configuration",function(){ var config = {data:'lol'}; var callbacks = []; return { list:function(){ return config;}, update:function(){ callbacks.forEach(function(x){ x();}); }, change:function(fn){ callbacks.push(fn); // I never remove these, so this is a memory leak! } } }); app.service("FooService",function(Configuration){ return { getFoo: function(){ return Configuration.list().data+" bar"; },change:function(fn){ Configuration.change(fn); } } }); app.controller("SettingsCtrl",function($scope,Configuration){ $scope.config = Configuration.list(); $scope.$watch('config',function(){ Configuration.update(); },true); }); ``` I've also considered `$rootScope` broadcasts but that just seems like more global state No matter what I try, I have a singleton with global state and we all know I don't want a singleton. Since this seems like a fairly common Angular use case. What's what's the idiomatic way to solve this problem?

Original source