22

也许我疯了,或者太习惯于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>

但是在这两种情况下,我都在想象我的 $scope 原型继承自我的模型,MyItem.

但是范围不继承自模型!

这让我很困惑。

相反,我的模型是作用域上的一个属性。

在中继器的情况下:

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

这意味着我必须在任何地方使用item.thisoritem.that而不是thisand that。我必须记住哪些函数是模型的原生函数,哪些函数是由控制器直接应用于范围的。

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

<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。我也必须使用selectionin 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

4 回答 4

21

我发现我可以在 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> 

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

于 2014-07-18T07:33:17.717 回答
10

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

AngularJS 的作者 Miško Hevery 在这个视频中很好地解释了这个概念,从大约 30 分钟开始,持续大约 3 分钟

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

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

于 2013-04-20T20:37:13.273 回答
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>
于 2016-07-06T08:53:49.407 回答
0

我认为来自淘汰赛的一个副作用是鼓励您以更少的责任创建更多的组件,当然在一个组件中,“this”是该组件的视图模型。如果你真的很烦人,那么这可能表明你需要一个新组件。

能够只引用组件视图模型中的某些内容而无需考虑您所在的范围,这确实很好。而且您不能告诉我您错过$parents[2]$root;-)

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

我在做 Angular 的东西时肯定会错过淘汰赛(我仍然使用两者),但做某些事情确实很好,比如title="Customer #{{ row.customerId }} - {{ row.fullName }}"而不是把所有东西都放在data-bind.

于 2018-01-08T01:52:11.067 回答