Angular Directive - 我可以从父母那里继承一个隔离范围吗?

时间:2015-06-19 07:54:25

标签: javascript angularjs scope angularjs-scope isolate-scope

我正在尝试编辑由其他人编写的角度指令,我遇到了一个问题。它是一个属性指令,我想看到传入的值作为属性的更改值。

当我给它一个隔离范围并使用范围观察属性时,这是有效的。$ watch。但是,使用隔离范围会破坏指令,因为它最初将范围设置为true,而其他东西需要它(因为我理解它意味着它继承了父范围)。

所以我的问题是,如何继承父范围,还要观察属性的值?

为了帮助澄清,这是该指令的基本示例:

return {
            scope: {
                myGridsterItem: "=gridsterItem"
            },
            restrict: 'EA',
            controller: 'GridsterItemCtrl',
            controllerAs: 'gridsterItem',
            require: ['^gridster', 'gridsterItem'],
            link: function(scope, $el, attrs, controllers) {

                .....

                // need to dynamically update draggable/resizable of individuaol gridster item if properties change
                scope.$watch("myGridsterItem.draggable", function(newVal) {
                    draggable.updateDraggable(newVal);
                }, true);
                scope.$watch("myGridsterItem.resizable", function(newVal) {
                    resizable.updateResizable(newVal);
                }, true);

                .....
            }
    }

由于它没有继承父作用域,因此会引发错误。如果我设置

scope: true

我的错误已经解决,但手表处理程序从未运行过。

HTML的大致概述:

<div gridster="gridsterOpts">
    <ul>
        <li gridster-item="getPrefs(portlet.id)", ng-repeat="portlet in getPortlets()">
            .....
        </li>
    </ul>
</div>

请帮助我,谢谢!

1 个答案:

答案 0 :(得分:1)

更新回答

我有更多的游戏,并在下面的代码片段中展示了一个可能的解决方案。

不使用隔离范围(因为这会导致其他问题),基本上我们需要的是getPrefs(portlet.id)在指令的链接函数尝试将其分配到范围时进行插值或解析。如果不这样做,如果您将attrs.gridsterItem的值输出到控制台,您将看到它是getPrefs(portlet.id),而不是调用该函数的结果。

我遇到的最清晰的解决方案是使用$parse。在您的链接函数中,它允许我们在将它分配给范围时评估属性中包含的函数。这样,它就是被分配的函数的结果,这允许观察者函数正确触发。

请注意,$parse必须作为依赖项添加到指令声明中,并作为参数传递。

(function() {
  "use strict";

  var myApp = angular.module("myApp", [])
    .directive("gridsterItem", ["$parse", GridsterItemDirective]); // Make sure to DI '$parse'

  function GridsterItemDirective($parse) {
    return {
      restrict: 'EA',
      controller: 'GridsterItemCtrl',
      controllerAs: 'gridsterItem',
      require: ['^gridster', 'gridsterItem'],
      link: function(scope, $el, attrs, controllers) {
        scope.output = ""; //for demo'ing
        scope.myGridsterItem = $parse(attrs.gridsterItem)(scope); //evaluating the value of attrs.gridsterItem in the context of 'scope'

        // need to dynamically update draggable/resizable of individuaol gridster item if properties change
        scope.$watch("myGridsterItem.draggable", function(newVal) {
          console.log("myGridsterItem.draggable: ", newVal);
          scope.output += "Draggable updated (" + newVal + ")... ";
        }, true);
        scope.$watch("myGridsterItem.resizable", function(newVal) {
          console.log("myGridsterItem.resizable: ", newVal);
          scope.output += "Resizable updated (" + newVal + ")... ";
        }, true);
      }
    };
  }

  //-------------------------------
  //Supporting mockups...
  myApp.controller("myController", ["$scope", MyController]) //supporting mocks
    .directive("gridster", [GridsterDirective])
    .controller("GridsterCtrl", ["$scope", GridsterCtrl])
    .controller("GridsterItemCtrl", ["$scope", GridsterItemCtrl]);

  function MyController($scope) {
    var _portlets = [{
      id: "1"
    }, {
      id: "2"
    }, {
      id: "3"
    }];

    $scope.getPortlets = function getPortlets() {
      return _portlets;
    };

    var _prefs = {
      "1": {
        draggable: true,
        resizable: true
      },
      "2": {
        draggable: true,
        resizable: true
      },
      "3": {
        draggable: true,
        resizable: true
      },
    };
    $scope.getPrefs = function getPrefs(id) {
      return _prefs[id];
    }
    $scope.setPrefs = function setPrefs(id, prefName, value) {
      _prefs[id][prefName] = value;
    }
  }

  function GridsterDirective() {
    return {
      restrict: 'EA',
      controller: 'GridsterCtrl',
      controllerAs: 'gridster'
    };
  }

  function GridsterCtrl($scope) {
    //mock
  }

  function GridsterItemCtrl($scope) {
    //mock
  }
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.min.js"></script>
<style type="text/css">
  li {
    border: 1px solid #ccc;
    padding: 10px;
    margin: 10px;
    font: 0.8em Arial, sans-serif;
  }
</style>
<div ng-app="myApp">
  <div ng-controller="myController">
    <div gridster>
      <ul>
        <li gridster-item="getPrefs(portlet.id)" ng-repeat="portlet in getPortlets()">
          <p><strong>portlet.id:</strong> {{portlet.id}}</p>
          <p>{{getPrefs(portlet.id)}}</p>
          <p><strong>Output:</strong> {{output}}</p>
          <button ng-click="setPrefs(portlet.id, 'draggable', !getPrefs(portlet.id).draggable)">Toggle Draggable</button>
          <button ng-click="setPrefs(portlet.id, 'resizable', !getPrefs(portlet.id).resizable)">Toggle Resizable</button>
        </li>
      </ul>
    </div>
  </div>
</div>

旧答案:

在你的链接功能(具有非隔离范围)中,试试这个:

scope.myGridsterItem = attrs.gridsterItem;

attrs是一个自动提供给您的链接函数的参数,并且应该在其中包含您的指令所使用的元素上的属性集合。获取所需的属性,并将其分配给与$ watch表达式中相关的范围变量匹配的范围变量(在这种情况下为myGridsterItem,看起来像)。

请记住,angular的命名约定意味着HTML中的一个名为this-is-an-attribute的属性可以在javascript中以thisIsAnAttribute(snake-to camel-case conversion)的形式访问。

相关问题