Угловая директива - могу ли я иметь изолированную область, наследуемую от родителя?

Я пытаюсь отредактировать директиву angular, написанную кем-то другим, и столкнулся с проблемой. Это директива атрибута, и я хочу наблюдать за значением, переданным в качестве значения атрибута для изменений.

Это работает, когда я даю ему изолированную область и наблюдаю за свойством, используя 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>

Пожалуйста, помогите мне, спасибо!


person Heather Roberts    schedule 19.06.2015    source источник


Ответы (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.

person JcT    schedule 19.06.2015
comment
Спасибо, но это не работает, так как обработчики часов по-прежнему не работают. Я также пытался использовать scope.myGridsterItem = scope.$eval(attrs.gridsterItem) - person Heather Roberts; 22.06.2015
comment
На самом деле, в итоге я рекомендовал использовать $parse, что, по сути, вы и пытались использовать с scope.$eval; на самом деле, если вы отредактируете мой фрагмент и замените строку $parse на ту, которую вы упомянули в своем комментарии выше, это тоже сработает. Так что я думаю, что может быть больше к этому. Возможно, все же стоит попробовать $parse и сообщить о результатах? - person JcT; 22.06.2015
comment
Я скопировал свой тестовый код в Plnkr, чтобы его было легче разветвить и поиграть, если вы хотите поэкспериментировать: plnkr.co/edit/UmdPrvmHr90cd4wE9ctu?p=preview - person JcT; 22.06.2015
comment
Могу ли я подтвердить - угловая версия? - person JcT; 22.06.2015
comment
Это 1.2.28 - в итоге я реализовал обходной путь, но когда у меня будет время, я вернусь и проверю, работает ли использование $parse, спасибо. Часть моей проблемы может заключаться в том, что ng-repeat работал с возвращаемым значением функции, но я не уверен, что изменение было просто совпадением. - person Heather Roberts; 25.06.2015