2

When working on a project, as these things tend to happen, we came across a situation where we were stumped on how to update certain UI elements when other things were done. For example, the navigation contains a counter of how many pending activities are due today. At any point in time during usage of the app, a user might schedule an activity for later today, and the count section would need to call the API to generate a count and the drop-down items associated with it.

How can I make a navigation controller pull the new list of activities when the main controller makes a change?

See this code for an example.

<div ng-app="TestApp">
  <nav ng-controller="navigationController">
    <p>The navigation count is: {{items.length}}</p>
  </nav>
  <div ng-controller="mainController">
    <p>The main count is: {{items.length}}</p>
    <p>
      <button ng-click="addItem()" type="button">Add item.</button>
    </p>
  </div>
</div>

<script>
var app = angular.module('TestApp', []);

app.factory("api", function() {
  return {
    update: function() {
      return ['a', 'b', 'c', 'd', 'e'];
    }
  };
});

app.factory("sharedFactory", function(api) {  
  var obj = {};

  obj.items = ["a"];

  obj.update = function() {
    obj.items = api.update();
  };

  return obj;
});

app.controller("mainController", function(sharedFactory, $scope) {
  $scope.items = sharedFactory.items;

  $scope.addItem = function() {
    sharedFactory.update();
  };
});

app.controller("navigationController", function(sharedFactory, $scope) {
  $scope.items = sharedFactory.items;
});

</script>

Our current solution was to create a callback service that other controllers could subscribe to, and then when an activity was created have those callbacks run as needed. This works nicely, but I'm nervous that I'm "doing it wrong".

We're switching to the Angular UI Router, now, so I'm curious if there's a better way of doing so in it. Right now our navigation handler is a stateless controller that hooks into our callback service still.

3 Answers 3

1

A nice way to handle this could be to use $scope.$on to listen for events, and $scope.$emit to fire an event going up the scope or $scope.$broadcast to fire an even going down the scope.

In each piece of the UI that needs to be updated can be listening with $scope.$on and update itself when an event is fired, like your user scheduling an event for later today.

Angular docs for $on, $emit and $broadcast

Sign up to request clarification or add additional context in comments.

4 Comments

Doesn't each controller instance have it's own $scope? Therefore it wouldn't work to get one controller to update another, would it? I'll give it a test run here in a moment.
I gave that a swing and it doesn't seem to be working: jsfiddle.net/w2h2vgkm/2
Alright, I got a working version thanks to a little help from @JanS. jsfiddle.net/w2h2vgkm/7 does it. Thank you, @Devin!
Exactly! You have to go up or down the scope, $rootScope is at the top =)
1

Though I generally think that registering scope values on a controller with a service is the best way to accomplish another option would be to use a factory and set a property of that on scope.

angular.module('app').factory('myService', function() {
   var myService = {};
   service.count = 0;
   /// other service functions
   return myService;
}


angular.module('app').controller('myController', function(myService) {
   this.count = myService.count;
}

3 Comments

I might have worded my question poorly. Assuming myController and myOtherController are both active on the page, would your method update myController.count if myOtherController updated the service count without any other URL or state change?
Yes it would. Since, the factory's count value is on scope any changes will trigger angular to process the whole page meaning any other controller with count on scope will be updated as well without any changes to the URL.
I realized that I made the question a bit too simple than what I'm trying to achieve. Give it a read and see if it makes more sense? Thank you!
1

However you feel about MVC, you could use angular's internals to automatically do this:

https://jsfiddle.net/gkmtkxpm/

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

myApp.factory('counter', function() {
    return {
        count: 0
    };
});

myApp.controller('CounterController', function (counter) {
    var vm = this;

    vm.counter = counter;
    vm.increment = function() {
        vm.counter.count = vm.counter.count + 1;
    };
});

edit: Concerning your updated question, see the updated fiddle: https://jsfiddle.net/gkmtkxpm/1/

4 Comments

I realized that I made the question a bit too simple than what I'm trying to achieve. Give it a read and see if it makes more sense? Thank you!
Close! I wrote a code example real quick of what I'd like to do. I can see a way and will be doing that real quick, but if you want to give it another stab. My apologies!
Here's a working version of what I want to do: jsfiddle.net/w2h2vgkm/6. Can it be done better?
Unfortunately, I don't understand the use case. You could use angular events like @Devin suggested. You just have to use $rootScope instead of $scope. (and $rootScope.$emit() to create an event and $rootScope.$on to listen to it)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.