2013-06-05 7 views
13

"scope"を使ってバインディングを持つディレクティブを作成しました。場合によっては、定数オブジェクトをバインドする必要があります。 HTMLと、例えば:AngularJS:定数オブジェクトをディレクティブにバインドする方法

<div ng-controller="Ctrl"> 
    <greeting person="{firstName: 'Bob', lastName: 'Jones'}"></greeting> 
</div> 

とJavaScript:

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

app.controller("Ctrl", function($scope) { 

}); 

app.directive("greeting", function() { 
    return { 
     restrict: "E", 
     replace: true, 
     scope: { 
      person: "=" 
     }, 
     template: 
     '<p>Hello {{person.firstName}} {{person.lastName}}</p>' 
    }; 
}); 

これは動作しますが、それはまた、JavaScriptエラーが発生します。

Error: 10 $digest() iterations reached. Aborting! 

(Fiddle demonstrating the problem)

正しい方法は何エラーを引き起こすことなく定数オブジェクトをバインドするには?

答えて

10

ここで私は、@のsh0berの回答に基づいて、思い付いたソリューションです。属性が有効なJSONの場合、それは定数値なので、一度だけ評価します。それ以外の場合は、通常どおり(つまり、=バインディングとして動作するように)値を監視して更新します。 scopetrueに設定する必要があります。割り当てられた値がこの指示文のインスタンスにのみ影響することを確認してください。

(Example on jsFiddle)

HTML:

<div ng-controller="Ctrl"> 
    <greeting person='{"firstName": "Bob", "lastName": "Jones"}'></greeting> 
    <greeting person="jim"></greeting> 
</div> 

はJavaScript:

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

app.controller("Ctrl", function($scope) { 
    $scope.jim = {firstName: 'Jim', lastName: "Bloggs"}; 
}); 

app.directive("greeting", function() { 
    return { 
     restrict: "E", 
     replace: true, 
     scope: true, 
     link: function(scope, elements, attrs) { 
      try { 
       scope.person = JSON.parse(attrs.person); 
      } catch (e) { 
       scope.$watch(function() { 
        return scope.$parent.$eval(attrs.person); 
       }, function(newValue, oldValue) { 
        scope.person = newValue; 
       }); 
      } 
     }, 
     template: '<p>Hello {{person.firstName}} {{person.lastName}}</p>' 
    }; 
}); 
2

=タイプのスコープフィールドリンクを使用すると、属性値が変更されていることがわかりますが、等価性が深くテストされているのではなく、参照の等価性がテストされています(!==)。オブジェクトリテラルをインラインで指定すると、アトリビュートがその値を取得するためにアクセスされるたびにangleが新しいオブジェクトを作成します。つまり、角度がダーティチェックの場合、古い値と現在の値を比較すると常に変化を通知します。それを克服するための

一つの方法は、ここで説明するように、角度のソースを変更することです:

https://github.com/mgonto/angular.js/commit/09d19353a2ba0de8edcf625aa7a21464be830f02

そうでない場合は、あなたがコントローラで、あなたのオブジェクトを作成し、要素の属性に名前によってそれを参照することができます:

HTML

<div ng-controller="Ctrl"> 
    <greeting person="personObj"></greeting> 
</div> 

JS

app.controller("Ctrl", function($scope) 
{ 
    $scope.personObj = { firstName : 'Bob', lastName : 'Jones' }; 
}); 

さらに別の方法は、親要素のでオブジェクトを作成することですディレクティブ以降は名前によってそれを参照(ただし、これは読みにくくです):

<div ng-controller="Ctrl" ng-init="personObj = { firstName : 'Bob', lastName : 'Jones' }"> 
    <greeting person="personObj"></greeting> 
</div> 
6

角度が式を毎回評価されているため、あなたはそのエラーを取得しています。 '='は変数名です。

エラーなしで同じ考えを実現する2つの方法があります。

最初のソリューション:

app.controller("Ctrl", function($scope) { 
    $scope.person = {firstName: 'Bob', lastName: 'Jones'}; 
}); 

app.directive("greeting", function() { 
    return { 
     restrict: "E", 
     replace: true, 
     scope: { 
      person: "=" 
     }, 
     template: 
     '<p>Hello {{person.firstName}} {{person.lastName}}</p>' 
    }; 
}); 

<greeting person="person"></greeting> 

第二の溶液:

app.directive("greeting2", function() { 
    return { 
     restrict: "E", 
     replace: true, 
     scope: { 
      firstName: "@", 
      lastName: "@" 
     }, 
     template: 
     '<p>Hello {{firstName}} {{lastName}}</p>' 
    }; 
}); 

<greeting2 first-name="Bob" last-Name="Jones"></greeting2> 

http://jsfiddle.net/7bNAd/82/

+1

答えをありがとう。残念ながら、私が使用している実際のデータは深くネストされているので、2番目の解決策は不可能です。最初のケースは可能ですが、ディレクティブが定数値とともに使用されるインスタンスが多数あるため(サーバーサイドで生成されているため)、やや乱雑です。 –

0

私は特にeval()を使用して好きではないが、あなたは本当にこれがで動作するように取得したい場合あなたが提供したHTML:

app.directive("greeting", function() { 
    return { 
     restrict: "E", 
     compile: function(element, attrs) { 
      eval("var person = " + attrs.person); 
      var htmlText = '<p>Hello ' + person.firstName + ' ' + person.lastName + '</p>'; 
      element.replaceWith(htmlText); 
     } 
    }; 
}); 
4

別のオプション:

はカスタムlink機能を実装します。

app.directive("greeting", function() { 
    return { 
     restrict: "E", 
     link: function(scope,element,attrs){ 
      scope.person = scope.$eval(attrs.person); 
     }, 
     template: '<p>Hello {{person.firstName}} {{person.lastName}}</p>' 
    }; 
}); 
0

私は同じ問題を抱えていた、私はコンパイルステップでJSONを解析することによってそれを解決:

angular.module('foo', []). 
directive('myDirective', function() { 
    return { 
     scope: { 
      myData: '@' 
     }, 
     controller: function ($scope, $timeout) { 
      $timeout(function() { 
       console.log($scope.myData); 
      }); 
     }, 
     template: "{{myData | json}} a is {{myData.a}} b is {{myData.b}}", 
     compile: function (element, attrs) { 
      attrs['myData'] = angular.fromJson(attrs['myData']); 
     } 
    }; 
}); 

1つの欠点は、コントローラーが最初に実行されたときに、最初に$scopeが入力されていないことです。

このコードはJSFiddleです。

関連する問題