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ています。メインコントローラーの var がディレクティブから新しい値を受け取る直前に実行さloadResultsれているため、実際には 1 文字遅れてログに記録されます。ただし、ディレクティブ内にログインすると、すべてが期待どおりに機能します。なぜこうなった?loadResultssearchFilter

私の解決策

私の単純な例で ngChange で何が起こっているかを理解した後、私が実際に渡している ngModel がオブジェクトであり、そのプロパティが変更されているという事実によって、実際の問題がもう少し複雑であることに気付きました。このディレクティブを入力の 1 つとしてフォーム検証を使用しています。ディレクティブ内で $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時間までにアウターを変更する機会がなくsearchChange()、その結果、loadResults()呼び出されたためです。

ただし、この解決策は 2 つの理由から非常にハックです。

呼び出し元 (ディレクティブのユーザー) は、これらの回避策について知る必要はありません$timeout。他に何もなければ$timeout、ビュー コントローラーではなく、ディレクティブで実行する必要がありました。

そして2つ-OPによっても犯された間違い-は、そのng-modelようなディレクティブのユーザーによる使用には他の「期待」が伴うことです。ng-model持つということは、バリデーター、パーサー、フォーマッター、view-change-listeners ( など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#L714およびngChange.js の実装。

    その時点で$scope.searchFilterは更新されていません。Console.log の古い値

  • 次のダイジェスト サイクル: searchFilterデータ バインディングによって更新されます。

更新:値が伝播するために1サイクル余分に必要なPOCとしてのみ、次のことができます。他の anwser (よりクリーンなアプローチについては @NewDev) を参照してください。

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