0

TL;DR - The $on should change the value of practiceCount ...why isn't it? Here is codepen: http://codepen.io/anon/pen/qNERva

enter image description here

It's my third day practicing MEAN development, so I don't have too much experience with this.

So, I've got a controller MainController. I want this controller to be available on static/constantly available elements (that won't show/hide on condition) such as my nav-bar, and its purpose is to broadcast global data such as the total number of X in the application.

app.controller('MainController', [ '$scope','$http', function($scope, $http){

    $scope.practiceCount = 0;
    $scope.$on('practiceInfo', function(event, practiceInfo){
    $scope.practiceCount = practiceInfo.count;
    });

}]);

I then have a PracticeController controller. This controller is specifically for managing the Practices on this application (Practice in medical terms), i.e. creating/deleting/editing Practices.

app.controller('PracticeController', ['$scope', '$http', function($scope,$http){

    //refresh function that fetches practices
    var refresh = function(){
        $http.get('/practices').success(function(response){

            $scope.practices = response;
            $scope.practiceCount = response.length;
            $scope.$emit('practiceInfo', {
                count: $scope.practiceCount
            });

        });
    }

    refresh();

    $scope.createPractice = function(){

        if($scope.practice)
            $http.post('/practices', $scope.practice).success(function(response){
                console.log(response);
                refresh();
            })

    }
}]);

Finally, in my index.html (template) file, I have hooked up the MainController to the <ul> of my nav-bar, and am trying to display practiceCount, like such:

<ul ng-controller="MainController">

 {{practiceCount}}

</ul>

My expectation according to my current understanding

Now... I don't know much about this yet but from my understanding, when I emit practiceInfo with the new value stored in count, then it should be received by MainController, and the practiceInfo.count property should be set as the new value of $scope.practiceCount in MainController ... meaning its value has been changed/reset to whatever the result of the function reception was. With that, shouldn't the new number change automatically on the HTML?

It just stays as 0, which is how i initialized it in MainController

Advice would be much appreciated, on this issue, or anything else that's relevant.

Thanks.

Barry D.
  • 549
  • 2
  • 6
  • 21
  • where are you using the `PracticeController`? – Daniel A. White Jun 01 '16 at 00:41
  • @DanielA.White on `practices.html` (a dynamically loaded template) - `ngRoute` takes care of it. – Barry D. Jun 01 '16 at 00:43
  • emit is dependent on controller tree hiearchy. we don't know what that is. Is the `$scope.$on('practiceInfo'` firing? – charlietfl Jun 01 '16 at 00:49
  • I would suggest you check this SO post http://stackoverflow.com/a/31358365/5583283 – j3ff Jun 01 '16 at 00:50
  • @charlietfl Yes, it fires, it's handled by the `MainController` controller, the new value is received and logged to console, it's even set to `$scope.practiceCount` in `MainController`. However, I cannot `console.log()` its value outside of the `$on` because it just shows 0, because it will always execute before the `$on` is triggered. – Barry D. Jun 01 '16 at 00:55
  • well that is expected. Events are asynchronous. – charlietfl Jun 01 '16 at 00:56
  • @charlietfl I understand that, but ... I'm trying to understand why this is happening, specifically, hehe. As in my question. – Barry D. Jun 01 '16 at 01:00
  • Suggest you create a demo in plunker that replicates issue. Much easier for people to use browser dev tools that way and inspec/edit the code. Doesn't seem like you ever increment it either – charlietfl Jun 01 '16 at 01:02
  • @charlietfl Added codepen link at the top. – Barry D. Jun 01 '16 at 01:14
  • 1
    @BarryD. Are you open to some refactoring? I think you're really better off using services/factories for encapsulating the work for communicating with the API and transforming data as necessary, then you just inject that factory/service where you need it and set it on the scope and can bind to the properties of the service/factory and have it deal with getting the data from the API and massging it as necessary before exposing it on a property that the view uses. Also not great to rely on events as a primary way of communicating between parts tracing all the async stuff is a mess. – shaunhusain Jun 01 '16 at 01:23
  • @shaunhusain Yes. This is my first project with MEAN-dev and I could use some constructive advice. I know a bit about factories, they behave like globally accessible modules that you can inject into your controllers? Bad wording but just making sure my concept of it is correct. So how would I make, for example, this factore have `practiceCount` property that I can access somehow in my HTML? – Barry D. Jun 01 '16 at 01:35
  • @BarryD. yup your explanation sounds about right, the $injector service deals with "getting" or the first time creating an instance of each service/factory and injecting it in where it's seen as an argument. I also took out the minsafe [] array syntax, usually easier to just let ngAnnotate or some other build task handle adding those in (error prone if you leave one off or swap the order) – shaunhusain Jun 01 '16 at 01:39

1 Answers1

1

http://codepen.io/anon/pen/XKJMrm?editors=1000

JS

(function() {

  var app = angular.module('pcentrum', ['ngRoute']);

  app.controller('MainController', function($scope, PracticeService) {

    $scope.PracticeService = PracticeService;

  });

  // Practice Service
  app.service('PracticeService', function($http) {
    var practiceService = {
      refresh: function() {
        return $http.get('http://jsonplaceholder.typicode.com/posts').success(function(response) {
          practiceService.practices = response;
          practiceService.practiceCount = response.length;
        });
      }
    }
    practiceService.refresh();
    return practiceService;

  });
}());

HTML

<html lang="en" ng-app="pcentrum">

<head>
  <meta charset="UTF-8">
  <title>PCentrum</title>
  <!-- Latest compiled and minified CSS -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
  <link href='https://fonts.googleapis.com/css?family=Roboto:300' rel='stylesheet' type='text/css'>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">

</head>

<body>
  <div class="container-fluid top-bar">
    <span>Dashboard</span>
  </div>

  <div class="container-fluid rest none">
    <div class="col-md-2 bg-info menu">
      <h4>Menu</h4>
      <hr>
      <ul ng-controller="MainController">
        <a href="#/dashboard">
          <li><i class="fa fa-user"></i>Dashboard</li>
        </a>
        <a href="#/practices">
          <li><i class="fa fa-user"></i>Practices&nbsp<span class="badge">{{PracticeService.practiceCount}}</span></li>
        </a>
        <a href="#/doctors">
          <li><i class="fa fa-user"></i>Doctors</li>
        </a>
        <a href="#/pickups">
          <li><i class="fa fa-user"></i>Pickups</li>
        </a>
        <a href="#/reports">
          <li><i class="fa fa-user"></i>Reports</li>
        </a>
      </ul>

    </div>

    <!-- Main Body Content -->
    <div class="col-md-10 content" ng-controller="MainController">

      <!-- angular templating -->
      <!-- The respective content will be inject here-->
      <div ng-view></div>


    </div>
  </div>

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-route.js"></script>
</body>

</html>

I made a tweaked version of your code based on how I would typically do it or how I've seen others typically do it in the past. There's a good write up on making a generic API service here too that you might find interesting:

https://gist.github.com/jelbourn/6276338


Some problems I've seen with using events to communicate changes throughout an application.

  • Difficult to determine which emitter/broadcaster of an event actually triggered the handler
  • No single point to debug since everything is trying to stay "in sync" by having events that notify it when things updated
  • If you move an element its scope relationship with other parts changes and the order it receives events in changes (since they propagate up or down the scope hierarchy)
  • If you end up stopping propagation of events in order to stop outer parts from receiving an event and then move a part (see above) you have confusion

Events definitely have their place and in systems like Node seem to work well but the event system in Angular is best if used sparingly. Places I see that it makes sense are when you want to globally broadcast that something like a login event occurred or where you basically want very loose coupling between components where any number of children of any type might be listening for the event any time after they are created. That said if there is a way to solve the problem with a service or factory it's typically easier to debug and trace what is happening.

shaunhusain
  • 19,630
  • 4
  • 38
  • 51