2017-01-27 7 views
2

問題を解決する方法がわかりません。角度の問題が発生しました。AngularJS指令コールバック関数のタイミング問題

私は何かが行われた後に指示が呼び出すコントローラにコールバック関数を配置したいと思っています。このディレクティブは、最初にスコープ変数を変更してコールバックを実行します。コールバック関数は、同じスコープ変数で何かを実行します。なぜこれが欲しいのか分かりにくいかもしれませんが、このディレクティブは汎用的であり、コールバックは非常に特殊な関数ですが、多くの異なるケースを扱うと考えています。他のコードでもこのスコープ変数を使用する必要があるため、この変数を引数として渡すことはできません。

問題が発生しました。私は、コールバックが実行されるまですべてを処理しています。コールバック内にあるようですが、変数はまだ変更されていません。しかし、タイムアウトが設定されている場合(私の例では1秒間)、変数は変更されたものとして認識されます。なぜこの動きがあるのですか?コールバックが呼び出される前のディレクティブでも、変数が実際に変更されたことを示します。私はこの問題を示すCodepenを作成しました。ボタンのラベルを 'A'から 'B'に切り替えるだけです。コンソールログには、変数がコールバックに出力されたときに、私が待つまで「古い」値であることがわかります。

すべての情報はすばらしいでしょう、ありがとう!

注:スコープ変数のコピーをディレクティブのローカル変数として保存し、そのローカル変数をコールバックへの引数として送信するのは、コールバックが現在私が気にしている唯一の関数なので変数の変更をすぐに知り、それに作用します。

Codepen:http://codepen.io/anon/pen/KayaRW

HTML:

<div ng-app="myApp" ng-controller="myCtrl"> 
    <div ng-test="" button-label="myLabel" callback="cbFunc()"> 
    </div> 
</div> 

JS:

angular 
    .module('myApp', []) 
    .controller('myCtrl', function($scope) { 

     $scope.myLabel = "A"; 

     $scope.cbFunc = function() { 
      console.log("myLabel in callback: " + $scope.myLabel); 
      setTimeout(function() { 
      console.log("myLabel in callback, after 1 sec: " + $scope.myLabel); 
      console.log(""); 
     }, 1000); 
     } 

    }).directive('ngTest', function() { 
     return { 
     scope: { 
      buttonLabel: '=', 
      callback: '&' 
     }, 
     template: function(element, attrs) { 
      element.html("<button class=\"btn\" ng-click=\"changeLabel()\">Button Label: {{buttonLabel}}</button>"); 
     }, 
     link: function($scope, $element, $attrs) { 
      $scope.changeLabel = function() { 
       if ($scope.buttonLabel == "A") { 
        $scope.buttonLabel = "B"; 
       } else { 
        $scope.buttonLabel = "A"; 
       } 
       console.log("myLabel before callback: "+ $scope.buttonLabel); 
       $scope.callback(); 
      } 
     } 
     } 
    }); 

答えて

1

あなたはいくつかのオプションがあります。私はあなたの問題は、ディレクティブが独立したスコープを作成しているので起こっていると思います。 また、この指示文を可読性の要素に変更しました。

1)コールバックは使用しないでください。$ scope.myLabelは既に2ウェイ境界です。

2)タイムアウトを0に設定することはできますが、それでも動作するようです。

<div ng-app="myApp" ng-controller="myCtrl"> 
    <ng-test button-label="myLabel" callback="cbFunc()"> </ng-test> 
    <p> 
Here is the updated label in the timeout::::: {{updatedLabel}} 
    </p> 
</div> 
angular 
    .module('myApp', []) 
    .controller('myCtrl', function($scope, $timeout) { 
    $scope.myLabel = "A"; //inital value 
    $scope.cbFunc = function() { 
    $timeout(function() { 
    //$scope.myLabel is updated, $timeout is like an $apply but better 
    }, 0); 
    } 
}).directive('ngTest', function() { 
    return { 
    restrict: 'E', 
    scope: { 
     buttonLabel: '=', 
     callback: '&' 
    }, 
    template: function(element, attrs) { 
     element.html("<button class=\"btn\" ng-click=\"changeLabel()\">Button Label: {{buttonLabel}}</button>"); 
    }, 
    link: function($scope, $element, $attrs) { 
     $scope.changeLabel = function() { 
     if ($scope.buttonLabel == "A") { 
      $scope.buttonLabel = "B"; 
     } else { 
      $scope.buttonLabel = "A"; 
     } 

     $scope.callback(); 
     } 
    } 
    } 
}); 

3)あなたのコールバックは、パラメータを取ることができるとディレクティブは戻ってそれを渡すだろう、あなたは本当にオプションではありません言った:私は、$ TIMOUTサービスを使用していました。

<div ng-app="myApp" ng-controller="myCtrl"> 
    <ng-test button-label="myLabel" callback="cbFunc(data)"> </ng-test> 
    <p> 
Here is the updated label in the timeout::::: {{updatedLabel}} 
    </p> 
</div> 

angular 
    .module('myApp', []) 
    .controller('myCtrl', function($scope,$timeout) { 
$scope.myLabel = 'A'; 

$scope.cbFunc = function(data){ 
    $scope.updatedLabel = data; 
} 

}).directive('ngTest', function() { 
    return { 
    restrict: 'E', 
    scope: { 
     buttonLabel: '=', 
     callback: '&' 
    }, 
    template: function(element, attrs) { 
     element.html("<button class=\"btn\" ng-click=\"changeLabel()\">Button Label: {{buttonLabel}}</button>"); 
    }, 
    link: function($scope, $element, $attrs) { 
     $scope.changeLabel = function() { 
     if ($scope.buttonLabel == "A") { 
      $scope.buttonLabel = "B"; 
     } else { 
      $scope.buttonLabel = "A"; 
     } 

     $scope.callback({data: $scope.buttonLabel}); 
     } 
    } 
    } 
}); 

4)$はオブジェクトが更新されるたびにバックアップし、コントローラはそれをリッスンできます。スコープ階層が問題を引き起こす可能性があるので、代わりにコールバックを呼び出すあなたのディレクティブでは、

$scope.$emit('sudoCallback', $scope.buttonLabel); 

とあなたのコントローラの代わりに、コールバック関数で呼び出すことはあなたが持っている

$scope.$on('sudoCallback', function(event, data) {$scope.updatedLabel = data }); 

私は、このオプションを好きではない

5)あなたのコントローラーで$ scopeのために$ watchを使用してください。myLabelとコールバックを完全に取り除く。

$scope.$watch('myLabel', function(newVal){ 
    $scope.updatedLabel = newVal; 
    }); 

私は腕時計の束を追加するのが好きではありませんが動作します。

githubには、メッセージハブとして機能する非常にクールなライブラリがあり、スコープ階層を気にする必要はなく、単に購読して公開することもできます。 GitHub Angular Message Bus

+0

ありがとう@rleffler、これらは多くの良い提案です。私はそれぞれを考え、私が一番うまくいくと思うものに戻ってきます。 –