4

Okay, this is really bugging me. I have a directive with isolate scope, using the controllerAs syntax and bindToController:

function exampleDirectiveFactory() {
    var bindings = {
        foo: '=',
        bar: '@'
    }

    return {
        bindToController: true,
        controller      : 'ExampleController',
        controllerAs    : 'vm',
        scope           : bindings,
        template        : 'foo = {{ vm.foo }}<br />bar = {{ vm.bar }}'
    };
}

Assuming a usage like this:

<example foo="FOO" bar="BAR"></example>

...I expect the value of vm.foo to be two-way bound to the value of the foo attribute. Instead, it is undefined.

The value of vm.bar is equal to the attribute value bar of the HTML element, which I expect.

When I try to change the value of vm.bar using a filter, no change persists.

When I store the filtered value of vm.bar to a new variable, vm.baz, that works as expected.

Here is a fiddle

So my question has two parts:

A) Why is the value of vm.foo undefined when using '='?

B) Why can't I change the value of vm.bar in the scope of the controller, even if that change does not propagate to the HTML element attribute (which it shouldn't, because I'm using '@')?

Shaun Scovil
  • 3,905
  • 5
  • 39
  • 58

1 Answers1

11

1.4 changed how bindToController works. Though it appears that angular's documentation is still referring to the field as true/false. Now it can accept an object just like the scope attribute where the attributes indicate what you want bound and how to bind it.

function exampleDirectiveFactory() {
    var bindings = {
        foo: '=',
        bar: '@'
    }

    return {
        bindToController: bindings, //<-- things that will be bound
        controller      : 'ExampleController',
        controllerAs    : 'vm',
        scope           : {}, //<-- isolated scope
        template        : 'foo = {{ vm.foo }}<br />bar = {{ vm.bar }}'
    };
}

In addition, in your fiddle, FOO is undefined on the parent scope, so when it binds, it will pull that undefined into the directive's bound controller's scope.

Further reading: One major thing that this new bindToController syntax allows is the ability for the directive to not be an isolated scope and still identify what to bind. You can actually set scope to true on your directive to have a new child scope that will inherit from it's ancestors.

TheSharpieOne
  • 25,646
  • 9
  • 66
  • 78
  • thanks for your response. I did read about the changes to `bindToController` in 1.4 and experimented with that syntax as well. But according to your answer, or my understanding of it, this should result in `vm.foo` equalling `MAIN.FOO` and it does not: http://jsfiddle.net/sscovil/r4esw0a0/ – Shaun Scovil Jul 07 '15 at 16:48
  • You are referencing the scoped variable `FOO` in your directive (via `foo="FOO"` attribute on your element). You must define `FOO`. http://jsfiddle.net/r4esw0a0/2/. Also, you should not name all of the controllers as `vm` that defeats the purpuse of controllerAs. http://jsfiddle.net/r4esw0a0/3/ – TheSharpieOne Jul 07 '15 at 16:55
  • 1
    I'm starting to see what's going on here. By using `'='`, I am implying that the value of the attribute is a variable name, not a string literal...apposed to when I use `'@'`, which binds the string value of the attribute. But why then am I not able to change the value of the bound `vm.foo` or `vm.bar` values in this example? http://jsfiddle.net/sscovil/960zLavt/ – Shaun Scovil Jul 07 '15 at 17:45
  • With two-way binding you must have an assignable variable to bind, using an expression with a string is not assignable. To make it more obvious: what you are doing would be similar to trying to bind to `foo="1+1"`... sure, it equals 2 which is the one-way binding but how would two-way binding set a value back to `foo`? https://docs.angularjs.org/error/$compile/nonassign?p0=%27FOO%27&p1=example – TheSharpieOne Jul 07 '15 at 18:02
  • 1
    Thanks for talking through it with me. These comments do a better job of answering my question...you may want to copy them into the answer for visibility. That said, I'll accept and really appreciate the help. Cheers! – Shaun Scovil Jul 07 '15 at 18:36
  • What worked for me is to mark scope: true in directive when using bindToController. It allows two-way binding. – Tarun Aug 13 '19 at 14:02