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: {},您创建了一个新的、隔离的范围来作用于该元素。隔离范围不会从父范围继承。具有隔离范围的指令上的所有属性和内容都在隔离范围的上下文中进行评估。gumball不存在于隔离范围内,所以一切都是未定义的。

您有两种选择来解决这个问题:(1)删除隔离范围(例如scope: true创建子范围);或 (2) 绑定隔离范围内的值。

要将属性绑定到范围变量,您只需指定范围和所需的绑定类型:

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

这表示属性idcolor将在父作用域的上下文中插入,然后添加到作用域。您可以删除link函数中的所有逻辑 - 这将为您完成。

但这仍然留下了指令内部内容的问题。要在父范围的上下文中插入它,您需要嵌入:

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

嵌入获取元素的内容并相对于父作用域的新子代进行插值,例如gumball仍将定义的位置。

通过这两个更改,您的指令将按需要工作。

如果您对使用哪个范围感到困惑,这里有另一个可能会有所帮助的 SO 问题:在编写指令时,我如何确定是否需要新的范围、新的子范围或新的隔离范围?


旁注:即使没有隔离范围,link函数中确定属性值的逻辑也不起作用。执行顺序是这里的重要部分,大致是:编译器 -> 控制器 -> 链接 -> 插值。在插值完成之前,您的属性没有任何价值。所以你的支票是行不通的。

也就是说,您可以设置一个$observe插值属性;$observe即使没有传递任何值,也总是会在第一次触发。您可以使用它来设置默认值。$observe效率也很高。

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