Angular指令的作用域问题

Angular directives &scope issues

本文关键字:问题 作用域 指令 Angular      更新时间:2023-09-26

我正在使用一个预先存在的模板,试图将其角化。

我有3个指令,基本上是一个card,一个card-header和card-body:

<card>
    <card-header title="My Card">
        <input type="text" ng-model="userSearch" />
    </card-header>
    <card-body>
        <card ng-repeat="item in object | filter:userSearch">
            <card-body>{{ item.name }}</card-body>
        </card>
    </card-body>
</card>

我相信你能看到这个问题…由于范围问题,我无法让过滤器拾取模型。因为我在指令中有自己的html,所以我需要使用transclude: true,并且根据我的理解,它创建了自己的作用域。

卡:

return {
    restrict: 'AE',
    transclude: true,
    replace: true,
    scope: false,
    template: '<div class="card" ng-transclude></div>',
}

卡头:

return {
    restrict: 'AE',
    requires: 'card',
    transclude: true,
    replace: true,
    scope: false,
    scope: {
        title: '@',
        secondary: '@',
        theme: '@'
    },
    template: '<div class="card-header" ng-class="theme"><h2 ng-if="title">{{ title }}<small>{{ secondary }}</small></h2><div ng-transclude></div></div>',
}

卡身体:

return {
    restrict: 'AE',
    requires: '^card',
    transclude: true,
    replace: true,
    scope: false,
    scope: {
        padding: '@',
        theme: '@'
    },
    template: '<div class="card-body" ng-class="theme" ng-transclude></div>',
    link: function($scope, $element, $attributes) {
        if($scope.padding)
            angular.element($element[0]).addClass('card-padding');
    }
}

似乎应该是一个简单的概念,但我不知道如何才能绕过这个,当我有我自己的作用域项,但需要跨界和有我自己的作用域项。

首先,我认为你可能有标记问题。我想你的意思是:

<card>
    <card-header title="My Card">
        <input type="text" ng-model="userSearch" />
    </card-header>
    <card-body>
        <card ng-repeat="item in object | filter:userSearch">
            <card-body>{{ item.name }}</card-body>
        </card>
    </card-body>  <!--this was card-header, which doesn't make sense -->
</card>

当你在指令中使用ng-transclude时,被transclude的内容会使用一个新的作用域,它是指令作用域的兄弟。因此,如果您要分析您的作用域树,那么您将拥有以下内容(A是整个块的父作用域,()表示隔离作用域):

<card A>
    <card-header A.B.(C)>   
        <input A.B.D ng-model="A.B.D.userSearch"> 
    </card-header>
    <card-body A.E.(F)>
        <card A.E.G.H ng-repeat="A.E.G.H.item in A.E.G.object | filter: A.E.G.userSearch">
             <card-body A.E.I.(J)>{{A.E.I.K.item.name}}
        </card>
    </card-body>
</card>

注意一些事情(除了明显的"这有很多作用域!"):

A.B.D.userSearch是一个与A.E.G.userSearch完全不同的属性。a.e.g.在原型上不继承a.b.d,这就是为什么过滤器不起作用。

还需要注意的是,A.E.G.H.item和A.E.I.K.item是不同的属性——这也行不通。

如何修复:

最简单的修复方法是不使用ng-transclude,而是使用手动transclude,并控制transclude内容所使用的作用域。

例如,卡片传输将更改为:

template: '<div class="card" transclude-target></div>'
link: function(scope, element, attr, ctrl, transclude) {
    transclude(scope, function(clone, scope){
         element.find('[transclude-target]').append(clone);
    }
}

Aside: ng- translude:

link: function(scope, element, attr, ctrl, transclude) {
    transclude(scope.$parent.$new(), function(clone, scope){
         element.find('[ng-transclude]').append(clone);
    }
}

这样做的目的是使透传使用指令作用域而不是指令作用域的兄弟作用域(甚至是一个新作用域)

作用域树变成:

<card A>
    <card-header A.(B)>   
        <input A.(B) ng-model="A.(B).userSearch"> 
    </card-header>
    <card-body A.(C)>
        <card A.(C) ng-repeat="A.(C).D.item in A.(C).object | filter: A.(C).userSearch">
              <card-body A.(C).D>{{A.(C).D.item.name}}</card-body>
        </card>
    </card-body>
</card>

仍然不完全正确(孤立的指令破坏了我们需要的继承链)。

将其他两个指令(card-header和card-body)改为使用scope.$parent:

link: function(scope, element, attr, ctrl, transclude) {
    transclude(scope.$parent, function(clone, scope){
         element.find('[transclude-target]').append(clone);
    }
}

生成下面的作用域树(现在你的过滤器可以工作了{{item.name}}应该会显示正确的版本)

<card A>
    <card-header A.(B)>   
        <input A ng-model="A.userSearch"> 
    </card-header>
    <card-body A.(C)>
        <card A.E ng-repeat="A.E.item in A.object | filter: A.userSearch">
             <card-body A.E.(D)>{{A.E.item.name}}
        </card>
    </card-body>
</card>

我肯定我在某个地方犯了一个错误,但我认为它应该解释发生了什么。我希望我的解释更简单,但这是我能做的最好的了。

这是我对你的问题的理解:

如果检查最后一个指令,我看到的是模板中没有定义模型。

现在当你指定模板时,它会替换指令元素中的html,所以你的模型没有出现的原因。

也就是说,如果item.namecard-body指令中,则必须在模板中包含model。

现在当你使用ng-transclude时,它会把原来的html放回去,这就是为什么你能在某种程度上解决问题(但范围导致问题)。

您必须按照如下方式更改模板,并且必须在隔离作用域定义中也包含item。

template: '<div class="card-body" ng-class="theme">{{item.name}}</div>'

如果你不想修改它,并且想使用ng- translude,有两种方法可以从子作用域调用父作用域变量,

    使用作用域对象的属性。

所以不是项作为$scope。你可以用一个对象来代替它as属性:$scope.itemModel.items

现在如果你修改这些,它们也会反映在父作用域。由于自然,它更快javascript .

  • 不推荐,但是你可以使用$parent在视图和控制器中。
  • 检查这些是否适合你