2

我正在尝试编辑由其他人编写的角度指令,但遇到了一个问题。这是一个属性指令,我想观察传入的值作为属性值的变化。

当我给它一个隔离范围并使用 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>

请帮助我,谢谢!

4

1 回答 1

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 的命名约定意味着this-is-an-attribute在 HTML 中调用的属性将thisIsAnAttribute在 javascript 中作为(蛇形到驼峰式转换)进行访问。

于 2015-06-19T08:02:46.473 回答