简体   繁体   中英

How can I manually propagate a value to the directive's parent scope in AngularJS?

My AngularJS typeahead FaveDirective needs to bind a single value to the parent scope, and call an update function when that value changes:

Parent html:

<div class="parent-controller-scope ng-scope">
  <my-fave-picker favorite="parent.favorite" on-change="parent.update()">
</div>

Fave picker directive template:

<input 
    type="text" 
    ng-model="vm.favorite" 
    typeahead-on-select="vm.onChange()"
    ng-init="vm.loadTypeaheadValues()"
    placeholder="Pick your favorite Swift"
    uib-typeahead="name for name in ::vm.TypeaheadValues | filter:$viewValue"
    class="form-control">     

Fave picker directive code:

(function (angular, _) {
    'use strict';

    angular
        .module('favorite')
        .directive('MyFavePicker', function() {
            return {
                restrict: 'E',
                templateUrl: 'fave-picker-template.html',
                scope: {
                    favorite: '=',
                    onChange: '&'
                },
                controllerAs: 'vm',
                bindToController: true,
                controller: 'FavePickerController'
            };
        })
        .controller('FavePickerController', function() {
             // etc.
        });

}(angular, _));

This works almost correctly; when the typeahead input is committed, it calls update() on the parent scope as intended. The problem is that this happens before the latest value of favorite is propagated to the parent scope. In other words, if the typeahead has possible values ["Taylor Swift", "Jonathan Swift"] and I type "Tay" and then hit enter to select the value from the dropdown, then at the time the typeahead-on-select callback is executed, I have the following values:

vm.favorite = "Taylor Swift"    
parent.favorite = "Tay"

The parent.update() function therefore operates with the wrong value of parent.favorite ("Tay" instead of "Taylor Swift").

I can think of some bad ways, but what's the right way to do this so that the change to vm.favorite gets propagated back to the parent scope before calling parent.favorite() ?

Note that the following things are not possible in my circumstances:

  • inheriting parent scope instead of using isolate scope
  • passing favorite as an argument to update ( on-change="parent.update(favorite)" )
  • setting a timeout in dir.onChange() before calling parent.update()

Avoid using two-way, '=' , binding in components to propagate values. Instead use one-way, '<' , binding for inputs and expression, '&' , binding for outputs:

<my-fave-picker  favorite="parent.favorite"
    on-change="parent.favorite=$value; parent.update($value)">
</my-fave-picker> 
app.directive('MyFavePicker', function() {
    return {
        restrict: 'E',
        templateUrl: 'fave-picker-template.html',
        scope: {
            ̶f̶a̶v̶o̶r̶i̶t̶e̶:̶ ̶'̶=̶'̶,̶ 
            favorite: '<',
            onChange: '&'
        },
        controllerAs: 'vm',
        bindToController: true,
        controller: 'FavePickerController'
    };
})

In the component template:

<input 
    type="text" 
    ng-model="vm.favorite"
    ng-change="vm.onChange({$value: vm.favorite})"
    typeahead-on-select="vm.onChange({$value: vm.favorite})"
    ng-init="vm.loadTypeaheadValues()"
    placeholder="Pick your favorite Swift"
    uib-typeahead="name for name in ::vm.TypeaheadValues | filter:$viewValue"
    class="form-control"
/>     

By applying the value in the expression, '&' , binding, the value is propagated immediately. With two-way, '=' , binding, the value is propagated after a digest cycle.

For more information, see

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM