1

私が取り組んでいるいくつかの Angularjs 機能の問題を解決するために、少し問題が発生しています。

基本的な考え方は、ユーザーがアプリの次のセクションに進む前に、特定の基準を満たす必要があるシステムがあるということです。この 1 つの例は、ユーザーが先に進むために、コメントを追加し、リンク (実際のアプリでは、これはファイルのダウンロード) をクリックする必要があることです。

ここで完全な例を見ることができます: https://jsfiddle.net/d81xxweu/10/

HTML はかなり自明であると想定し、Angular モジュールで行っていることに移ります。私のアプリの宣言と初期化は次のとおりです。

var myApp = angular.module('myApp', ['ngRoute']);

myApp.run(function ($rootScope) {
    // Both of these must be met in order for the user to proceed with 'special-button'
    $rootScope.criteria = {
        criteria1: false,
        criteria2: false
    };
});

これはとても簡単です。ディレクティブとコントローラーからアクセスできるようにするために、アプリケーションのルート スコープに criteria というオブジェクトをアタッチしています。基準が満たされると、ユーザーが先に進むことができるリンクをレンダリングするディレクティブがあります。この例では、リンクのテキストが「待機中...」から「クリックして続行」に変わり、先に進むことができることを示しています。

myApp.directive('specialButton', function ($rootScope) {
    return {
        scope: true,
        template: "<a href='#'>{{ linkText }}</a>",
        replace: true,
        link: function (scope, el, attrs) {
            scope.linkText = 'Waiting...';

            var setLinkState = function(currentCriteria) {
                var criteriaMet = true;

                for(var k in $rootScope.criteria) {
                    if($rootScope.criteria[k] == false) {
                        criteriaMet = false;
                    }
                }

                if(criteriaMet) {
                    scope.linkText = 'Click to proceed';
                }
            };

            // Watch for changes to this object at the root scope level
            $rootScope.$watchCollection('criteria', function(newValues) {
                setLinkState(newValues);
            });
        }
    };
});

したがって、このディレクティブに設定したウォッチ ステートメントをトリガーするために、このコントローラーで許可されているコメントを追加できます。

myApp.controller('comments', function ($scope, $rootScope) {
    $scope.commentText = '';
    $scope.comments = [];

    $scope.addComment = function () {
        $scope.comments.push({ commentText: $scope.commentText });
        $scope.commentText = ''

        // When the user adds a comment they have met the first criteria
        $rootScope.criteria.criteria1 = true;
    };
});

前のものは、コメントを表示/追加するためのコントローラーです。ここでは、ユーザーがコメントを追加したことを示すために、criteria1 を true に設定します。これは実際には問題なく動作し、specialButton ディレクティブの $watchCollection は期待どおりに呼び出されます。

先に進むためにクリックする必要があるリンクから同じアクションを実行しようとすると、問題が発生します。これは、コメントリスト/フォームとは異なり、このような場合、コントローラーよりもディレクティブの方が意味があると理解しているため、ディレクティブでレンダリングされます。

myApp.directive('requiredLink', function($rootScope) {
    return {
        scope: true,
        template: "<a href='#'>Click me!</a>",
        replace: true,
        link: function(scope, el, attrs) {
            el.bind('click', function(evt) {
                evt.preventDefault();

                // When the user clicks this link they have met the second criteria
                $rootScope.criteria.criteria2 = true;
            });
        }
    };
});

ここでわかるように、コントローラーと同じように $rootScope を渡します。ただし、criteria2 を true に設定すると、$watchCollection はトリガーされません。

したがって、最初にコメントを追加してから他のボタンをクリックすると、2番目の変更がウォッチをトリガーしないため、specialButtonがテキストを更新するのが見られません。ただし、最初にリンクをクリックしてからコメントを追加すると、specialButton が期待どおりに更新されます。requiredLink をクリックすると、データが更新されますが、ウォッチはトリガーされません。そのため、コメントを追加して $watch がトリガーされると、両方が true に設定されていることがわかります。

この問題を解決するために提供できるヘルプを事前に感謝します。お時間をいただきありがとうございます。

4

1 に答える 1

1

あなたの実際の問題は$rootScope、角度コンテキストの外側にあるイベントから更新されていることです。その場合、ダイジェスト サイクルが起動されないため、角度バインディングが更新されないことは明らかです。$apply()のメソッドを使用して手動で起動する必要があります$rootScope

el.bind('click', function(evt) {
    evt.preventDefault();
    // When the user clicks this link they have met the second criteria
    $rootScope.criteria.criteria2 = true;
    $rootScope.$apply(); //this will run digest cycle & will fire `watchCollection` `$watcher`
});

デモプランカー

このソリューションは機能しますが、使用する代わりにサービスを使用することをお勧めします$rootScope

サービスを使用して実装するには、以下のことに従う必要があります。

サービスはcriteriaオブジェクト形式で変数を使用する必要がdot ruleあり、それぞれの参照が JavaScript プロトタイプを使用して更新されるようにする必要があります。

サービス

app.service('dataService', function(){
    this.criteria = {
        criteria1: false,
        criteria2: false
    };
    //...here would be other sharable data.
})

コントローラー、ディレクティブ、フィルターの関数に挿入する必要がある場所で使用したいときはいつでも、必要な場所にフィルターを適用します。

そして、ディレクティブからサービス変数を監視しながら、以下のようなことをする必要があります.

指令

myApp.directive('specialButton', function (dataService) {
    return {
        scope: true,
        template: "<a href='#'>{{ linkText }}</a>",
        replace: true,
        link: function (scope, el, attrs) {
            //.. other code

            // deep watch will watch on whole object making last param true
            scope.$watch(function(){ 
                return dataService.criteria //this will get get evaluated on criteria change
            }, function(newValues) {
                setLinkState(newValues);
            }, true);
        }
    };
});
于 2015-06-22T17:51:09.330 に答える