ngChange fires before value makes it out of isolate scope

前端 未结 2 2072
时光说笑
时光说笑 2021-01-05 03:57
//main controller
angular.module(\'myApp\')
.controller(\'mainCtrl\', function ($scope){
    $scope.loadResults = function (){
        console.log($scope.searchFilte         


        
相关标签:
2条回答
  • 2021-01-05 04:46

    You answered your own question in the title! '=' is watched while '&' is not

    • Somewhere outside angular:

      input view value changes

    • next digest cycle:

      ng-model value changes and fires ng-change()

      ng-change adds a $viewChangeListener and is called this same cycle. See: ngModel.js#L714 and ngChange.js implementation.

      At that time $scope.searchFilter hasn't been updated. Console.log's old value

    • next digest cycle: searchFilter is updated by data binding.

    UPDATE: Only as a POC that you need 1 extra cycle for the value to propagate you can do the following. See the other anwser (@NewDev for a cleaner approach).

    .controller('mainCtrl', function ($scope, $timeout){
        $scope.loadResults = function (){
            $timeout(function(){
               console.log($scope.searchFilter);
            });
        };
    });
    
    0 讨论(0)
  • 2021-01-05 04:47

    The reason for the behavior, as rightly pointed out in another answer, is because the two-way binding hasn't had a chance to change the outer searchFilter by the time searchChange(), and consequently, loadResults() was invoked.

    The solution, however, is very hacky for two reasons.

    One, the caller (the user of the directive), should not need to know about these workarounds with $timeout. If nothing else, the $timeout should have been done in the directive rather than in the View controller.

    And two - a mistake also made by the OP - is that using ng-model comes with other "expectations" by users of such directives. Having ng-model means that other directives, like validators, parsers, formatters and view-change-listeners (like ng-change) could be used alongside it. To support it properly, one needs to require: "ngModel", rather than bind to its expression via scope: {}. Otherwise, things would not work as expected.

    Here's how it's done - for another example, see the official documentation for creating a custom input control.

    scope: true, // could also be {}, but I would avoid scope: false here
    template: '<input ng-model="innerModel" ng-change="onChange()">',
    require: "ngModel",
    link: function(scope, element, attrs, ctrls){
      var ngModel = ctrls; // ngModelController
    
      // from model -> view
      ngModel.$render = function(){
        scope.innerModel = ngModel.$viewValue;
      }
    
      // from view -> model
      scope.onChange = function(){
        ngModel.$setViewValue(scope.innerModel);
      }
    }
    

    Then, ng-change just automatically works, and so do other directives that support ngModel, like ng-required.

    0 讨论(0)
提交回复
热议问题