1

I have multiple controllers on a small app I'm writing, and I have successfully shared a 'selected' variable between the controllers like so.

app.service('selectedEmployee', function () {
    var selected = null;

    return 
    { 
        getSelected: function() {
            return selected;
        },
        postSelected: function(employee) {
            selected = employee;
        }
    };

});

I have a side nav bar with a list of employees. When I click on an employee I call the postSelected function then the getSelected to set $scope.selected.

$scope.selectEmployee = function(employee) {

   //Calling Service function postSelected
    selectedEmployee.postSelected(employee);

    $scope.selected = selectedEmployee.getSelected();

    if ($mdSidenav('left').isOpen()) {
        $mdSidenav('left').close();
    }

}

I have a third controller for my main content area, and this is where I don't understand what to do. I want information from the selected employee to be displayed, but angular is compiling the whole page before the first employee has a chance to get set as selected, and subsequent selections of an employee aren't reloading the main content page (because I haven't told them to I think). Here's my main content controller:

app.controller('mainContentController', ['$scope','selectedEmployee',
    function ($scope, selectedEmployee) {
        $scope.selected = selectedEmployee.getSelected();
        console.log($scope.selected);
    }
]);

My main content view is very simple right now

<h2>{{selected.firstName}}{{selected.lastName}}</h2>

My question is how I can tell one controller to effectively update its partial view so that when I select an employee it displays information.

GitLab repo

ajthyng
  • 1,245
  • 1
  • 12
  • 18
  • 1
    Your question is hard to comprehend. but no worries it may be my fault or its a type of question that is hard to understand.. It would be much better if you could push all your code to a simple plunker.... https://plnkr.co/edit/ .. And Share Your Plunk with us! – amanuel2 May 07 '16 at 01:23
  • I will work on that right now, good suggestion. – ajthyng May 07 '16 at 01:24
  • It wasn't as easy as I thought to convert this problem to a usable plunker. I've added a gitlab link for my repo above. – ajthyng May 07 '16 at 01:41
  • 1
    A very quick suggestion: try throwing `$scope.$applyAsync(...)` around the body of your function in `mainContentController`. Maybe if it just needs one digest cycle to reach the controller, that should resolve it. – sg.cc May 07 '16 at 01:49
  • 1
    @UnicornMaster If you didnt know there is a online IDE(Which i think is the best), called Cloud9IDE... I Currently cloned it there. we could discuss it there and solve it also. Link: https://ide.c9.io/amanuel2/gitclone . Once there open the collab tab. – amanuel2 May 07 '16 at 01:51

3 Answers3

1

Don't rely on messy broadcasts if your goal is simply to display & modify the data in the controller's template.

Your controllers do NOT need to "know" when the Service or Factory has updated in order to use it in the template as Angular will handle this for you, as you access the data via dot notation. This is the important concept which you should read more about.

This Fiddle shows both ways of accessing the data, and how using the container object in the template causes Angular to re-check the same actual object on changes - instead of the primitive string value stored in the controller:

http://jsfiddle.net/a01f39Lw/2/

Template:

<div ng-controller="Ctrl1 as c1">
    <input ng-model="c1.Bands.favorite" placeholder="Favorite band?">
</div>
<div ng-controller="Ctrl2 as c2">
    <input ng-model="c2.Bands.favorite" placeholder="Favorite band?">
</div>

JS:

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

app.factory('Bands', function($http) {
    return {
        favorite: ''
    };    
});

app.controller('Ctrl1', function Ctrl1(Bands){
    this.Bands = Bands;
});
app.controller('Ctrl2', function Ctrl2(Bands){
    this.Bands = Bands;
});
William B
  • 1,411
  • 8
  • 10
  • Is there an advantage in this situation to using a factory over a service? As I understand it the basic difference is that services are constructors and factories return object literals. I don't know much more than that. – ajthyng May 09 '16 at 01:44
  • This post can answer more than I can on that subject: http://stackoverflow.com/a/26924234/5046541 – William B May 09 '16 at 02:59
0

So after much research and a lot of really great help from Dsafds, I was able to use $rootScope.$broadcast to notify my partial view of a change to a variable.

If you broadcast from the rootScope it will reach every child controller and you don't have to set a $watch on the service variable.

$scope.selectEmployee = function(employee) {
            selectedEmployee.postSelected(employee);
            $scope.selected = selectedEmployee.getSelected();
            $rootScope.$broadcast('selected:updated', $scope.selected);
            if ($mdSidenav('left').isOpen()) {
                $mdSidenav('left').close();
            }
        }

And in the controller of the main content area

function ($scope) {
    $scope.$on('selected:updated', function(event, data) {
        $scope.selected = data;
    })
}

I don't think you have to pass the data directly, you could also just as easily call selectedEmployee.getSelected()

$rootScope also has to be included in the Parent controller and the broadcasting controller.

Community
  • 1
  • 1
ajthyng
  • 1,245
  • 1
  • 12
  • 18
  • Abusing rootscope broadcasts will cause you trouble in the long run and will not be easy to debug. It's better to have a hierarchal relationship between controllers that need to broadcast events to one another and use emit/broadcast from specific scopes, and even *easier* to use a shared model when possible - see my answer for an example of the latter. – William B May 08 '16 at 23:33
  • Thank you for the advice, I will have to read up on how to set up the controller relationships properly. – ajthyng May 09 '16 at 01:38
0

First of all lets start by good practices, then solve your problem here...

Good Practices

At least by my knowledge, i dont intend to use services the way you do... you see, services are more like objects. so if i were to convert your service to the way i normally use it would produce the following:

app.service('selectedEmployee', [selectedEmployeeService])

    function selectedEmployeeService(){
      this.selected = null;
      this.getSelected = function(){
        return this.selected;
      }
      this.postSelected = function(emp){
        this.selected = emp;
      }
    }

You see there i put the function seperately, and also made the service an actual object.. i would reccomend you format your controller function argument like this... If you want to disuss/see good practices go here. Anways enough about the good practices now to the real problem.

Solving the problem

Ok The Andrew actually figured this out!! The problem was:that he need to broadcast his message using $rootScope:

$rootScope.$broadcast('selected:updated', $scope.selected);

And then you have to check when $scope.selected is updated.. kinda like $scope.$watch...

$scope.$on('selected:updated', function(event, data) {
          $scope.selected = data;
})

After that it autmoatically updates and works! Hope this helped!

PS: Did not know he anwsered already...

amanuel2
  • 4,508
  • 4
  • 36
  • 67
  • I just wanted to point out here that you're not using the rules of scope in the way that service is written. The keyword 'this' is going to change which function it's referring to. When you say 'return this.selected;' you're returning an undefined value because this.selected in the scope of the getSelected member has not been defined yet. This code won't work. – ajthyng May 08 '16 at 22:17