28

This entire weekend, I was in much distress, not understanding why a parent controller's function was not being recognized by a child controller.

I soon realized that having my controller as vm was the cause:

 <div data-ng-controller="ParentCtrl as vm">
   <div data-ng-controller="Child1 as vm"></div>
   <div data-ng-controller="Child2 as vm"></div>
 </div>

Sure, it all seems obvious now that neither child1 nor 2 will see functions in ParentCtrl, and if instead I had used the prior pattern of working without vm, and instead had $scope, all would be well.

So my question is "What does it benefit anyone for using the "vm" method, and if it is superior to not using it, how can one call function calls in the ParentCtrl short of emit?

Thank You

Mike Jones
  • 475
  • 2
  • 5
  • 12
  • I've seen the "as" syntax before, but have never used it. I don't like my views to access the controller directly / by name. Also, from an encapsulation perspective, Child1 or Child2 should not know anything about the parent controller; and as such should not call functions on the parent controller. Communication between controllers should either be by modifying a shared service; or by using emit as an event dispatcher. – JeffryHouser Mar 09 '14 at 17:55
  • So Reboog, if I am understanding you correctly, it is your opinion NOT to use "vm', but nevertheless, not rely on $scope, and use emit regardless of the fact that the Parent's function are accessible to the children thru scope, correct? – Mike Jones Mar 09 '14 at 17:58
  • Yes, that is my opinion and the approach I've been taking. There may be issues I do not yet understand, but I have yet to run into any limitations / problems. – JeffryHouser Mar 09 '14 at 18:01

5 Answers5

27

One advantage of using the controller as syntax is that it allows you to define your controllers as a simple javascript constructor function with properties and functions exposed directly from the instantiated object rather than the $scope.

For example:

function MyController() {

    var ctl = this;

    ctl.message = 'You have not clicked anything yet.';

    ctl.onClick = function() {
        ctl.message = 'You clicked something.';
    };

    return ctl;
}

...

myModule.controller('MyController', MyController);

...

<div ng-controller="MyController as vm">
    <span>{{vm.message}}</span>
    <button ng-click="vm.onClick()">Click Me</button>
</div>

Notice that we are able to use a controller that is plain old javascript without even being tied to angular. For scenarios where you need additional dependencies such as $scope or other services, you can still easily pass them in to your constructor, but this pattern encourages less clutter directly on your $scope and also solves the problem of variable hiding when variables are set directly on the scope.

Ultimately it really comes down to a matter of preference, but for me I really like not having to define everything directly on the scope and to treat my controllers as any old javascript object as much as possible.

Here's an excellent article on the usage of controller as: http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/

bingles
  • 11,582
  • 10
  • 82
  • 93
16

I used to use controller as vm syntax, but lately I've been moving away from it. I'm finding that when I build complex pages with nested directives using scope isolation, the traditional $scope approach is far easier to work with.

Your question is one I wondered about for a while. The only real value I can see is that when you use nested controllers in a page, you can get semantic reference to each of the scopes so that your markup becomes a little easier to read. So, for instance:

<div ng-controller="CustomersCtrl as customers">
    <div ng-controller="OrdersCtrl as orders">
         <p>Showing{{customers.model.length}} customers with a total of {{orders.model.length}} orders</p>
    </div>
</div>

Other than this, I don't really see the value and if you prefer nesting with directives as I do, the value is quickly nullified imho.

zpydee
  • 4,487
  • 4
  • 16
  • 19
9

What you are experiencing with this example is not specifically due to the use of the controller as syntax; instead, it is due to your nested objects hiding the parent due to naming.

The controller as option is highly useful when working extensively with other languages that compile to JavaScript such as CoffeeScript or TypeScript. It also allows you to create much more lightweight controllers which can be interchanged with non-angular components, due to not needing the $scope injection. It is simply an alternate syntax, but you can still use $scope if you wish.

The real question here is why people who have written examples of the controller as syntax have decided to use the "as vm". Technically, the syntax is designed to provide an MVVM style coding experience, and so using "as vm" may make sense, but this exposes the issue you are seeing. Your parent controller is an object named vm, and your child object is also named vm, so the child object is hiding the parent object from view. If, instead, you name your objects differently, your child object will have no issue accessing the parent object, and it will actually be very clear in code which object the property you are working with is coming from.

Claies
  • 22,124
  • 4
  • 53
  • 77
5

I think one of the main advantages is that it automatically makes sure you'll end up with a . in your bindings. Because the rule of thumb in Angular is that if you don't have a . in your bindings you might shoot yourself in the foot.

Consider you have this:

<input ng-model="foo" />

This might not work as intended. On the other hand, if you use the SomeController as vm syntax you would automatically end up with this:

<input ng-model="vm.foo" />

This will save you from the potential trouble with the data binding not working as expected.

The reasons behind that lay in the way prototypal inheritance works in JavaScript and it's been described in very much detail here: What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

Community
  • 1
  • 1
Christoph
  • 26,519
  • 28
  • 95
  • 133
2

As I understand, the main reason to use "controller as vm" syntax is that controllers in angularjs actually serve as models(encapsulate data and provide behaviours), or view models(expose data to html).

In most cases it worked well, but one main fallback I've encountered is that it's difficult to access data in parent controller from child controller, and possible solution(Get parent controller in child controller which all use 'controller as vm' notation) is not elegant.

Community
  • 1
  • 1
aqingsao
  • 2,104
  • 1
  • 19
  • 18