Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
219 views
in Technique[技术] by (71.8m points)

javascript - AngularJS 1.4 directives: scope, two way binding and bindToController

Update: It must have been something stupid in another part of the code. It works now, so the bindToController syntax is fine.

We are using AngularJS 1.4, which introduced a new way to use bindToController in directives.

After quite a bit of reading (and maybe not understanding everything), we defined our directive like this:

  .directive('mdAddress', function mdAddress() {
    var directive = {
      restrict: 'EA',
      scope: {},
      bindToController: {
        address: '='
      },
      templateUrl: 'modules/address/address.html',
      controller: AddressController,
      controllerAs: 'dir'
    };

Calling it from another view like this:

  <md-address address="vm.address"></md-address>

Having previously defined in the view controller:

  vm.address = {
    street: null,
    countryCode: null,
    cityCode: null,
    postalCode: null
  };

Referencing the variables in the directive template like this:

  <md-input-container>
    <label>{{'ADDRESSNUMBER' | translate}}</label>
    <input type="number" ng-model="dir.address.streetNumber">
  </md-input-container>

We spent 4h trying to figure out why our directive was not working. Well, it was working, but the two-way binding between the controller and the directive was not, vm.address.street was hopelessly set to null.

After a while, we just tried the old way:

  .directive('mdAddress', function mdAddress() {
    var directive = {
      restrict: 'EA',
      scope: {
        address: '='
      },
      bindToController: true,
      templateUrl: 'modules/address/address.html',
      controller: AddressController,
      controllerAs: 'dir'
    };

And it magically worked. Any idea WHY?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Update:

Thanks to the reference to this blog post, I need to update my answer. Since AngularJS 1.4 it really seems, that you can use

scope: {},
bindToController: {
  variable: '='
}

which will do the (exact) same thing as the old syntax:

scope: {
  variable: '='
},
bindToController: true

The useful lines from the AngularJS source code to explain this behavior:

if (isObject(directive.scope)) {
  if (directive.bindToController === true) {
    bindings.bindToController = parseIsolateBindings(directive.scope,
                                                     directiveName, true);
    bindings.isolateScope = {};
  } else {
    bindings.isolateScope = parseIsolateBindings(directive.scope,
                                                 directiveName, false);
  }
}
if (isObject(directive.bindToController)) {
  bindings.bindToController =
      parseIsolateBindings(directive.bindToController, directiveName, true);
}

Source: AngularJS 1.4.0

Original answer:

Hopefully, I can explain you why this behavior you experienced is correct and where you did missunderstand the concept of scope binding there.

Let me explain, what you did in your first code snippet:

.directive('mdAddress', function mdAddress() {
    var directive = {
      restrict: 'EA',
      scope: {},
      bindToController: {
        address: '='
      },
      templateUrl: 'modules/address/address.html',
      controller: AddressController,
      controllerAs: 'dir'
    };

With scope: {}, you created an isolated scope (without any inheritance) for your mdAddress directive. That means: No data is passed between the parent controller and your directive.

Having this in mind, regarding your second code snippet:

<md-address address="vm.address"></md-address>

vm.address from your parent controller/view will be assigned as expression to the address attribute of the directive, but as you defined an isolated scope before, the data is not passed into AddressController and therefore not available in the bindToController value.

Let's think of the scope object definition as the "which data will be passed in" and the bindToController as the "which data will be available in my view's controllerAs object".

So, now let's have a look at the last (and working code snippet):

.directive('mdAddress', function mdAddress() {
    var directive = {
      restrict: 'EA',
      scope: {
        address: '='
      },
      bindToController: true,
      templateUrl: 'modules/address/address.html',
      controller: AddressController,
      controllerAs: 'dir'
    };

There you created an isolated scope, too, but this time you added the address attribute to be passed in as an expression. So now the address you passed in from the view in the second snippet will be available in the controller's scope. Setting bindToController: true now, will bind all the current scope's properties to the controller (or more likely the controllerAs object). And now, it works as you would expect, because data will be passed in to the scope and data will be passed out to the controller's template scope.

Did that brief overview help you to better understand the concept of the scope and bindToController definition objects?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...