角度指令和范围问题

时间:2015-05-22 14:25:09

标签: javascript angularjs

我正在使用预先存在的模板尝试对其进行Angularize。

我有3个指令,基本上是卡片,卡片头和卡片体:

<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');
    }
}

似乎它应该是一个简单的概念,但是当我拥有自己的范围项目时,我不知道如何解决这个问题,但需要转换并拥有自己的范围项目。

2 个答案:

答案 0 :(得分:1)

首先,我想也许你有一个标记问题。这就是我认为你的意思:

<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时,被转换的内容使用 新范围 ,这是指令的 兄弟范围 即可。因此,如果您要分析范围树,那么您将拥有(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,而是使用手动转换并控制已转换内容使用的范围。

例如,卡片转换将更改为:

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);
    }
}
  

除此之外:ng-transclude基本上可以:

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)以使用范围。$ parent:

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

Yield是以下范围树(现在您的过滤器将起作用{{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>

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

答案 1 :(得分:0)

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

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

现在,当您指定模板时,它会替换指令元素中的html,因此这是模型不显示的原因。

话虽如此,如果 item.name 位于 card-body 指令中,则必须在模板中包含模型。

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

您必须按如下方式更改模板,并且还必须在隔离的范围定义中包含项目。

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

如果您不想修改它并希望使用ng-transclude,有两种方法可以从子范围调用父范围变量,

  1. 改为使用范围对象的属性。
  2. 所以没有     作为$ scope.items的itmes你可以改为使用一个对象并拥有项目     作为财产:$scope.itemModel.items

    现在,如果你修改这些,他们     也将反映在父范围内。它的性质更快     的javascript。

    1. 不推荐,但您始终可以使用调用父作用域变量 $ parent在视图和控制器中。
    2. 检查这些是否适合您。