web-dev-qa-db-ja.com

AngularJSディレクティブはスコープ変数の変更時に更新されません

その内容を別のテンプレートファイルでラップするために、小さなディレクティブを作成しようとしました。

このコード:

<layout name="Default">My cool content</layout>

この出力があるはずです:

<div class="layoutDefault">My cool content</div>

レイアウト「デフォルト」には次のコードがあるためです。

<div class="layoutDefault">{{content}}</div>

ここにディレクティブのコード:

app.directive('layout', function($http, $compile){
return {
    restrict: 'E',
    link: function(scope, element, attributes) {
        var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default';
        $http.get(scope.constants.pathLayouts + layoutName + '.html')
            .success(function(layout){
                var regexp = /^([\s\S]*?){{content}}([\s\S]*)$/g;
                var result = regexp.exec(layout);

                var templateWithLayout = result[1] + element.html() + result[2];
                element.html($compile(templateWithLayout)(scope));
            });
    }
}

});

私の問題:

テンプレート(レイアウトテンプレート内またはレイアウトタグ内)でスコープ変数を使用している場合。 {{whatever}}最初は動作します。 whatever変数を更新すると、ディレクティブは更新されなくなります。リンク機能全体が一度だけトリガーされます。

AngularJSはこのディレクティブがスコープ変数を使用するため、更新されないことを知らないと思います。しかし、この動作を修正する方法がわかりません。

65
Armin

バインドされたスコープ変数を作成し、その変更を監視する必要があります。

return {
   restrict: 'E',
   scope: {
     name: '='
   },
   link: function(scope) {
     scope.$watch('name', function() {
        // all the code here...
     });
   }
};
81
QuarK

私もこの問題の解決策を必要としており、このスレッドの回答を使用して次のことを考え出しました。

.directive('tpReport', ['$parse', '$http', '$compile', '$templateCache', function($parse, $http, $compile, $templateCache)
    {
        var getTemplateUrl = function(type)
        {
            var templateUrl = '';

            switch (type)
            {
                case 1: // Table
                    templateUrl = 'modules/tpReport/directives/table-report.tpl.html';
                    break;
                case 0:
                    templateUrl = 'modules/tpReport/directives/default.tpl.html';
                    break;
                default:
                    templateUrl = '';
                    console.log("Type not defined for tpReport");
                    break;
            }

            return templateUrl;
        };

        var linker = function (scope, element, attrs)
        {

            scope.$watch('data', function(){
                var templateUrl = getTemplateUrl(scope.data[0].typeID);
                var data = $templateCache.get(templateUrl);
                element.html(data);
                $compile(element.contents())(scope);

            });



        };

        return {
            controller: 'tpReportCtrl',
            template: '<div>{{data}}</div>',
            // Remove all existing content of the directive.
            transclude: true,
            restrict: "E",
            scope: {
                data: '='
            },
            link: linker
        };
    }])
    ;

HTMLに含める:

<tp-report data='data'></tp-report>

このディレクティブは、サーバーから取得したデータセットに基づいてレポートテンプレートを動的にロードするために使用されます。

Scope.dataプロパティに監視を設定し、これが更新されるたびに(ユーザーがサーバーに新しいデータセットを要求するとき)、対応するディレクティブを読み込んでデータを表示します。

41
Roy Milder

ディレクティブがスコープ変数を使用することをAngularに伝える必要があります。

スコープのプロパティをディレクティブにバインドする必要があります。

return {
    restrict: 'E',
    scope: {
      whatever: '='
    },
   ...
}

それから$watch it:

  $scope.$watch('whatever', function(value) {
    // do something with the new value
  });

詳細については、 ディレクティブの角度に関するドキュメント を参照してください。

16
Paul Mougel

私ははるかに良い解決策を見つけました:

app.directive('layout', function(){
    var settings = {
        restrict: 'E',
        transclude: true,
        templateUrl: function(element, attributes){
            var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default';
            return constants.pathLayouts + layoutName + '.html';
        }
    }
    return settings;
});

私が現在見ている唯一の欠点は、トランスクルードされたテンプレートが独自のスコープを取得したという事実です。親から値を取得しますが、親の値を変更する代わりに、値は独自の新しい子スコープに保存されます。これを避けるために、私はwhateverではなく$parent.whateverを使用しています。

例:

<layout name="Default">
    <layout name="AnotherNestedLayout">
        <label>Whatever:</label>
        <input type="text" ng-model="$parent.whatever">
    </layout>
</layout>
8
Armin

スコープを監視する必要があります。

方法は次のとおりです。

<layout layoutId="myScope"></layout>

ディレクティブは次のようになります

app.directive('layout', function($http, $compile){
    return {
        restrict: 'E',
        scope: {
            layoutId: "=layoutId"
        },
        link: function(scope, element, attributes) {
            var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default';
            $http.get(scope.constants.pathLayouts + layoutName + '.html')
                .success(function(layout){
                    var regexp = /^([\s\S]*?){{content}}([\s\S]*)$/g;
                    var result = regexp.exec(layout);

                    var templateWithLayout = result[1] + element.html() + result[2];
                    element.html($compile(templateWithLayout)(scope));
        });
    }
}

$scope.$watch('myScope',function(){
        //Do Whatever you want
    },true)

同様に、ディレクティブでモデル化できるため、モデルが自動的に更新される場合、監視メソッドはディレクティブを更新します。

2
Aditya Sethi

私はこれを古いテーマを知っていますが、これが自分のように見つけた場合:

「親スコープ」が更新されたときに値を更新するためにディレクティブが必要なときに、次のコードを使用しました。私はまだ角度を学んでいるので、何か間違ったことをしている場合は必ず私を修正してください、しかしこれは私が必要なことをしました。

指令:

directive('dateRangePrint', function(){
    return {
        restrict: 'E',
        scope:{
        //still using the single dir binding
            From: '@rangeFrom',
            To: '@rangeTo',
            format: '@format'
        },
        controller: function($scope, $element){

            $scope.viewFrom = function(){
                    return formatDate($scope.From, $scope.format);
                }

            $scope.viewTo = function(){
                    return formatDate($scope.To, $scope.format);
                }

            function formatDate(date, format){
                format = format || 'DD-MM-YYYY';

                //do stuff to date...

                return date.format(format);
            }

        },
        replace: true,
        // note the parenthesis after scope var
        template: '<span>{{ viewFrom() }} - {{ viewTo() }}</span>'
    }
})
2
slappy-x

これを試すことができます

$scope.$apply(function() {
    $scope.step1 = true;
    //scope.list2.length = 0;
});

http://jsfiddle.net/Etb9d/

0
build-1

簡単な解決策は、スコープ変数objectを作成することです。次に、{{ whatever-object.whatever-property }}を使用してコンテンツにアクセスします。 JavaScriptはPrimitive type by valueを渡すため、変数は更新されません。一方、Objectreferenceによって渡され、問題を解決します。

0
Yohe