8
//main controller
angular.module('myApp')
.controller('mainCtrl', function ($scope){
    $scope.loadResults = function (){
        console.log($scope.searchFilter);
    };
});

// directive
angular.module('myApp')
.directive('customSearch', function () {
    return {
        scope: {
            searchModel: '=ngModel',
            searchChange: '&ngChange',
        },
        require: 'ngModel',
        template: '<input type="text" ng-model="searchModel" ng-change="searchChange()"/>',
        restrict: 'E'
    };
});

// html
<custom-search ng-model="searchFilter" ng-change="loadResults()"></custom-search>

这是一个简化的指令来说明。当我输入输入时,我希望输入console.log完全loadResults注销我已经输入的内容。它实际上记录了一个字符,因为它正在主控制器中loadResults的 var 接收来自指令的新值之前运行。searchFilter然而,在指令中登录,一切都按预期工作。为什么会这样?

我的解决方案

在我的简单示例中了解了 ngChange 发生的情况后,我意识到我的实际问题更加复杂,因为我实际传入的 ngModel 是一个对象,我正在更改其属性,而且我我正在使用带有此指令的表单验证作为输入之一。我发现在指令中使用 $timeout 和 $eval 解决了我所有的问题:

//main controller
angular.module('myApp')
.controller('mainCtrl', function ($scope){
    $scope.loadResults = function (){
        console.log($scope.searchFilter);
    };
});

// directive
angular.module('myApp')
.directive('customSearch', function ($timeout) {
    return {
        scope: {
            searchModel: '=ngModel'
        },
        require: 'ngModel',
        template: '<input type="text" ng-model="searchModel.subProp" ng-change="valueChange()"/>',
        restrict: 'E',
        link: function ($scope, $element, $attrs, ngModel)
        {
            $scope.valueChange = function()
            {
                $timeout(function()
                {
                    if ($attrs.ngChange) $scope.$parent.$eval($attrs.ngChange);
                }, 0);
            };
        }
    };
});

// html
<custom-search ng-model="searchFilter" ng-change="loadResults()"></custom-search>
4

2 回答 2

11

正如在另一个答案中正确指出的那样,这种行为的原因是因为双向绑定没有机会searchFilter在 time之前更改外部searchChange(),因此loadResults()被调用。

然而,由于两个原因,该解决方案非常棘手。

一,调用者(指令的用户)不需要知道这些变通方法$timeout。如果没有别的,$timeout应该在指令中而不是在视图控制器中完成。

第二个 - OP也犯了一个错误 - 是 usingng-model伴随着此类指令的用户的其他“期望”。拥有ng-model意味着其他指令,如验证器、解析器、格式化程序和视图更改侦听器(如ng-change)可以与它一起使用。为了正确地支持它,需要require: "ngModel",而不是通过绑定到它的表达式scope: {}。否则,事情将无法按预期进行。

下面是它的完成方式 - 另一个例子,请参阅创建自定义输入控件的官方文档

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);
  }
}

然后,ng-change它会自动运行,其他支持 . 的指令也是如此ngModel,例如ng-required.

于 2015-10-16T23:46:38.853 回答
4

你在标题中回答了你自己的问题!'='被观看而不被'&'观看

  • 某处外角:

    输入视图值更改

  • 下一个消化周期:

    ng-model价值变化和火灾ng-change()

    ng-change 添加了一个 $viewChangeListener 并被称为同一个循环。请参阅: ngModel.js#L714ngChange.js实现。

    那个时候$scope.searchFilter还没有更新。Console.log 的旧值

  • 下一个摘要周期: searchFilter由数据绑定更新。

更新:仅作为 POC,您需要 1 个额外的周期才能传播值,您可以执行以下操作。请参阅其他 anwser(@NewDev 以获得更清洁的方法)。

.controller('mainCtrl', function ($scope, $timeout){
    $scope.loadResults = function (){
        $timeout(function(){
           console.log($scope.searchFilter);
        });
    };
});
于 2015-10-16T20:36:26.667 回答