7

問題は、サービスから取得したガムボールのリストを管理する必要があることです。私が作成したディレクティブは、HTML で要素をハードコードするときに機能するように見えますが、ng-repeat を使用してガムボールを動的に割り当てようとすると機能します。

HTML

<div ng-controller="GumballsCtrl">

<h1>Working</h1> 
    <ul>
        <li ng-repeat="gumball in Gumballs">
            <div class="gumballColor{{gumball.color}}">{{gumball.color}}</div>
        </li>
    </ul>

<h1>Problem - Expecting the same result at the work version</h1>

    <ul>
        <li ng-repeat="gumball in Gumballs">
            <mygumball id={{gumball.id}} color="{{gumball.color}}">{{gumball.color}}</mygumball>
        </li>
    </ul>
</div>

JavaScript

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

function GumballsCtrl($scope, Gumballs) {
    $scope.Gumballs = Gumballs;
}

myApp.factory('Gumballs', function () {
    return [{
        id: '1',
        color: 'R'
    }, {
        id: '2',
        color: 'G'
    }, {
        id: '3',
        color: 'B'
    }, {
        id: '4',
        color: 'Y'
    }, {
        id: '5',
        color: 'G'
    }];
});

myApp.directive('mygumball', function ($scope) {
    return {
        restrict: 'E',

        scope: {},

        link: function (scope, element, attrs) {
            if (attrs.color !== '' && attrs.color !== undefined) {
                scope.color = attrs.color;
            } else {
                scope.color = 'U';
            }
        },

        replace: true,

        template: "<div class='gumballColor{{color}}'>{{color}}</div>"
    };
});

CSS

.gumballColorR {
    font-size: 12px;
    text-align: center;
    padding: 2px;
    -moz-border-radius: 10px;
    -webkit-border-radius: 10px;
    border-radius: 10px;
    border: solid 1px #CC0000;
    background-color: #FF0000;
    width: 15px;
    height: 15px;
    margin-left: 5px;
    margin-top: 5px;
}
.gumballColorG {
    font-size: 12px;
    text-align: center;
    padding: 2px;
    -moz-border-radius: 10px;
    -webkit-border-radius: 10px;
    border-radius: 10px;
    border: solid 1px #00CC00;
    background-color: #00FF00;
    width: 15px;
    height: 15px;
    margin-left: 5px;
    margin-top: 5px;
}
.gumballColorB {
    font-size: 12px;
    text-align: center;
    padding: 2px;
    color: #FFFFFF;
    -moz-border-radius: 10px;
    -webkit-border-radius: 10px;
    border-radius: 10px;
    border: solid 1px #0000CC;
    background-color: #0000FF;
    width: 15px;
    height: 15px;
    margin-left: 5px;
    margin-top: 5px;
}
.gumballColorY {
    font-size: 12px;
    text-align: center;
    padding: 2px;
    -moz-border-radius: 10px;
    -webkit-border-radius: 10px;
    border-radius: 10px;
    border: solid 1px #CCCC00;
    background-color: #FFFF00;
    width: 15px;
    height: 15px;
    margin-left: 5px;
    margin-top: 5px;
}
.gumballColorU {
    font-size: 12px;
    text-align: center;
    padding: 2px;
    -moz-border-radius: 10px;
    -webkit-border-radius: 10px;
    border-radius: 10px;
    border: solid 1px #CCCCCC;
    background-color: #DDDDDD;
    width: 15px;
    height: 15px;
    margin-left: 5px;
    margin-top: 5px;
}

http://jsfiddle.net/i3sik/NGB9v/22/

ディレクティブに渡されたときの id および color 属性は、ng-repeat を使用して渡されたときに未定義になりますが、HTML でハードコードされている場合は機能します。

4

1 に答える 1

10

ここでの問題は、分離スコープです。を使用しscope: {}て、その要素に作用する新しい分離スコープを作成しました。Isolate スコープは、親スコープから継承しません。分離スコープを持つディレクティブのすべての属性とコンテンツは、分離スコープのコンテキスト内で評価されます。gumball分離スコープには存在しないため、すべてが未定義として表示されます。

これを修正するには、次の 2 つの選択肢があります。(1) 分離スコープを削除します (たとえばscope: true、子スコープを作成するため)。または(2)分離スコープで値をバインドします。

属性をスコープ変数にバインドするには、スコープと必要なバインドの種類を指定するだけです。

scope: {
  id: '@',
  color: '@'
},

これは、属性idcolorが親スコープのコンテキストで補間され、スコープに追加されることを示しています。関数内のそのすべてのロジックを削除できますlink-これはあなたのためにそれを行います.

しかし、これでもディレクティブのコンテンツの問題が残ります。親スコープのコンテキストでそれを補間するには、トランスクルージョンが必要です。

transclude: true,
template: "<div class='gumballColor{{color}}' ng-transclude></div>"

Transclusion は要素の内容を取得し、親スコープの新しい子に対して相対的に補間します。たとえば、gumballまだ定義されている場所です。

これら 2 つの変更により、ディレクティブは期待どおりに機能します。

どのスコープを使用するかについて混乱している場合は、別の SO の質問が役立つ可能性があります:ディレクティブを作成するとき、新しいスコープ、新しい子スコープ、または新しい分離スコープが必要ないかどうかをどのように判断しますか?


補足:アイソレート スコープがなくても、link属性値を決定する関数内のロジックは機能しません。ここで重要なのが実行順序です。大まかに言えば、コンパイラ -> コントローラ -> リンク -> 補間です。補間が完了するまで、属性の値はありません。したがって、チェックは機能しません。

$observeつまり、補間された属性を設定できます。$observe値が渡されなかった場合でも、常に最初に起動します。これを使用してデフォルトを設定できます。$observeも非常に効率的です。

attrs.$observe( 'attr1', function(val) {
  if ( !angular.isDefined( val ) ) {
    scope.attr1 = 'defaultValue';
  }
});
于 2013-04-05T06:38:43.783 に答える