【问题标题】:AngularJS : How to update controller scope associated to directive scope's object as it changes?AngularJS:如何在指令范围的对象更改时更新与指令范围的对象关联的控制器范围?
【发布时间】:2015-09-29 20:25:10
【问题描述】:

解释如下:

我的当前控制器创建了一个 $scope.plan.steps 数组,用于存储每个步骤:

.controller('PlanCtrl', function ($scope, $http) {
    $scope.plan = {
        steps: [{}]
    };

    $scope.addStep = function () {
        $scope.tutorial.steps.push({});
    }
}

然后我有以下指令,它有一个独立的范围,并且与 $scope.plan.steps 数组的索引相关联:

.directive('planStep', function () {
    return {
        template: '<input type="text" ng-model="step.name" />{{step}}',
        restrict: 'E',
        scope: {
            index: '=index'
        },
        transclude: true,
        controller: function($scope, $element, $transclude) {

            $scope.removeStep = function() {
                $scope.$emit('removeStep', $scope.index);
                $element.remove();
                $scope.$destroy();
            }

        }
    };
});

这两个在控制器范围内通信、创建和删除对象,但是,我怎样才能允许指令实时更新控制器的范围数组?

我尝试对指令的隔离范围更改执行 $watch,$emit 对控制器的更改,并指定 $index...但没有运气。

我创建了一个 plunker 来重现我目前拥有的东西:

Link

到目前为止,我可以在数组内创建和删除对象,但我无法获取单个对象来根据 $index 更新控制器的对象。

如果解释不清楚,一定要告诉我,我会详细说明。

谢谢

【问题讨论】:

    标签: javascript angularjs angularjs-directive angularjs-scope angularjs-ng-repeat


    【解决方案1】:

    当您在 ng-repeat 内执行此类操作时,您可以利用 ng-repeat 创建的子作用域并在没有隔离作用域的情况下工作。

    这是相同的指令,不需要任何角度事件

    .directive('planStep', function() {
        return {
          template: '<button  ng-click="removeStep(step)">Delete step</button><br><input type="text" ng-model="step.name" />{{step}}<br><br>',
          restrict: 'E',          
          transclude: true,
          controller: function($scope, $element, $transclude) {
           var steps =  $scope.plan.steps// in scope from main controller
            /* can do the splicing here if we want*/
            $scope.removeStep = function(step) {
              var idx =steps.indexOf(step) 
               steps.splice(idx, 1);
            }
          }
        };
      });
    

    还要注意,使用 element.remove() 删除元素是多余的,因为它会在数组拼接时自动被 angular 删除

    至于更新,会实时更新item

    DEMO

    【讨论】:

    • 是的,我们可以这样做,但可能会违反关注点分离。 :)
    • 想听听你的意思
    • 这个指令的可重用性会降低,因为它知道的太多了,它知道 parent 有一个叫做 plan 的属性,它有 steps 属性等等。指令不是管理步骤的控制器,而是直接针对父集属性并对其执行某些操作,这可能是无意的,并且可重用性和可维护性较差。这个指令有很多假设(如果我在父节点上重命名我的属性,我需要知道有一个指令也是紧密耦合的)。但是是的,这将工作
    • 好的,有道理。我已经做了很多了。最近有很多关于最小化隔离范围使用的讨论。我一直秉持这样的理念:除非迫不得已,否则不要孤立,并且喜欢使用 parent 的简单性
    • 哦,不,你是对的。事实上,隔离并不意味着那只是可重用的东西。但是在尝试直接访问其父级上的属性之前,我总是三思而后行,特别是如果它修改它,而不是依赖与此一起使用的任何其他指令(例如:ng-repeat)。
    【解决方案2】:

    为索引设置 2 路绑定的方式也可以为 step 设置一个?而且您确实不需要索引来删除该项目,即使您的指令是孤立的,它也依赖于来自 ng-repeat 的索引,这可能不是一个好主意。

    <plan-step ng-repeat="step in plan.steps" index="$index" step="step"></plan-step>
    

    在你的指令中:

    scope: {
        index: '=index',
        step:'='
     },
    

    Demo

    移除 $index 依赖和冗余元素 remove() 和作用域销毁(当从数组中移除项目时 angular 将自行管理它):

     return {
      template: '<button  ng-click="removeStep()">Delete step</button><br><input type="text" ng-model="step.name" />{{step}}<br><br>',
      restrict: 'E',
      scope: {
        step:'='
      },
      transclude: true,
      controller: function($scope, $element, $transclude) {
        $scope.removeStep = function() {
          $scope.$emit('removeStep', $scope.step);
        }
      }
    

    在你的控制器中:

     $scope.$on('removeStep', function(event, data) {
      var steps = $scope.plan.steps;
      steps.splice(steps.indexOf(data), 1);
    });
    

    Demo

    如果您想摆脱 $emit,您甚至可以使用带有函数绑定 (&) 的隔离作用域指令公开一个 api。

    return {
      template: '<button  ng-click="onDelete({step:step})">Delete step</button><br><input type="text" ng-model="step.name" />{{step}}<br><br>',
      restrict: 'E',
      scope: {
        step:'=',
        onDelete:'&' //Set up function binding
      },
      transclude: true
    };
    

    并在视图上注册它:

    <plan-step ng-repeat="step in plan.steps"  step="step" on-delete="removeStep(step)"></plan-step>
    

    Demo

    【讨论】:

    • @TiffanyLowe 假设这是你想要的(我理解你的问题),是的,你也有很多选择。顺便说一句,您处于一个非常古老的角度世界.. :)
    • 我从 Plunkr 获取了那个模板,我总是最新的。 :)
    猜你喜欢
    • 1970-01-01
    • 2013-05-28
    • 1970-01-01
    • 2015-04-30
    • 1970-01-01
    • 2014-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多