不应该有AngularJS ngWith指令吗?

时间:2013-04-20 20:06:30

标签: angularjs angularjs-scope

也许我疯了,或者过于习惯KnockoutJS,但我一直在文档中寻找一个ngWith指令来定义元素,控制器或包含(ngInclude)部分的范围。

例如:

我想编写一个增强MyItem的控制器,如:

MyModule.controller('MyItemCtrl', function($scope) {
    $scope.doSomethingToItem = function() {
        $scope.name = "bar";
    };
});

或MyItem的视图/模板,如:

<div ng-controller="MyItemCtrl">
    {{name}}
    <button ng-click="doSomethingWithItem()">Do Something</button>
</div>

但在这两种情况下,我都在想我的范围是从我的模型MyItem原型继承。

但范围不会继承模型!!

让我感到困惑。

相反,我的模型是范围内的属性

对于转发器:

<div ng-repeat="item in list">
    <div ng-controller="MyItemCtrl">
        {{item.name}}
        <button ng-click="doSomethingWithItem()">Do Something</button>
    </div>
</div>

这意味着我必须使用item.thisitem.that,而不仅仅是thisthat。我必须记住哪些函数是模型的原生函数,哪些函数由控制器直接应用于作用域。

如果我想部分显示名称​​(愚蠢的例子,我知道,但你明白了)

<h3>{{name}}</h3>

我必须写它

<h3>{{item.name}}</h3>

然后确保模型始终项。通常通过将其包装在指令中来简单地定义具有item属性的范围。

我经常想要做的只是:

<div ng-include="'my/partial.html'" ng-with="item"></div>

<div ng-repeat="list" ng-controller="MyItemCtrl">            
    {{name}}
    <button ng-click="doSomethingWithItem()">Do Something</button>
</div>

那里有一些我没有找到的神奇指令吗?或者我完全错了,只是找麻烦?

感谢。

修改

非常感谢Brandon Tilley解释使用示波器作为模型的危险。但我仍然经常发现需要快速声明范围操作并希望使用ng-with指令。

例如,您有一个项目列表,单击这些项目时,会显示所选项目的展开视图。你可以这样写:

<ul>
    <li ng-repeat="item in items" ng-click="selection = item">{{item.minView}}</li>
</ul>
<div ng-controller="ItemController">
    {{selection.maxView}}
</div>

现在您必须使用selection.property而不是我想要的item.property来获取所选项目的属性。我还必须在selection中使用ItemController!使其完全与这种观点相结合。

我知道,在这个简单的例子中,我可以使用包装控制器来使其工作,但它说明了这一点。

我写了一个非常基本的with指令:

myApp.directive('with', ['$parse', '$log', function(parse, log) {

    return {
        scope: true,
        link: function(scope, el, attr) {
            var expression = attr.with;
            var parts = expression.split(' as ');

            if(parts.length != 2) {
                log.error("`with` directive expects expression in the form `String as String`");
                return;
            }

            scope.$watch(parts[0], function(value) {
                scope[parts[1]] = value;
            }, true);
        }
    }

}]);

只是创建一个新的范围,将一个表达式解析为另一个值,允许:

<ul>
    <li ng-repeat="item in items" ng-click="selection = item">{{item.minView}}</li>
</ul>
<div with="selection as item" ng-controller="ItemController">
    {{item.maxView}}
</div>

这对我来说似乎无限有用。

我在这里遗漏了什么吗?不管怎么说,只为自己制造麻烦?

4 个答案:

答案 0 :(得分:20)

我发现我可以在ng-repeat的源头周围放置一个数组,它在功能上变成了ng-with。 :)

<div ng-repeat="name in [vm.dto.Person.Name]" >
  <input type="text" ng-model="name.First" />
  <input type="text" ng-model="name.Last" />
</div> 

似乎有些纯净的人可能不喜欢这样......也似乎没有一个好的选择。

答案 1 :(得分:10)

这是一个很好的问题。我可以看到这可能会让来自另一个前端框架的混淆,但是在Angular中,范围有对模型的引用,并且您描述的语法是正常的。我个人喜欢将范围描述为更像a view model

AngularJS的作者MiškoHevery在this video, starting at about the 30 minute mark and lasting about 3 minutes中很好地解释了这个概念:

  

人们经常认为范围是模型,而事实并非如此。 Scope具有引用到模型。 [...]因此,在视图中,您说model dot您要访问的任何属性。

虽然有可能编写一个ngWith指令来实现您正在寻找的类型,但由于Angular使用原型继承作为范围,您可能会遇到Miško在其中描述的相同问题。上述视频at 31:10(您认为您正在更新父作用域的值,但实际上并非如此)。有关AngularJS中原型继承的更多详细信息,请查看AngularJS wiki上的优秀文章The Nuances of Scope Prototypal Inheritance

答案 2 :(得分:4)

另一种方法是通过ng-init设置新变量:

<div ng-init="name = vm.dto.Person.Name" >
  <input type="text" ng-model="name.First" />
  <input type="text" ng-model="name.Last" />
</div>

答案 3 :(得分:0)

我认为来自淘汰赛时的一个副作用就是鼓励你创造更多责任较少的组件,当然在一个组件中,这个&#39;这个&#39;是该组件的viewmodel。如果你达到了令你讨厌的程度,那么你可能需要一个新组件。

如果能够在不考虑您所处的范围的情况下仅仅引用组件视图模型中的某些内容,那确实很不错。而且您无法告诉我您错过了$parents[2]和{ {1}}; - )

PS。是的我知道你可以在淘汰赛中使用$root,这意味着更少let个东西。

在做Angular时我绝对会错过淘汰赛(我仍然使用两者)但是确实很好地做某些事情,比如$而不是title="Customer #{{ row.customerId }} - {{ row.fullName }}"下的所有内容。