Как передать аргумент директиве без чрезмерной записи родительской области?

Мне нужно создать директиву, действующую на ячейки таблицы, где строки таблицы отображаются с помощью ng-repeat - с этой целью я частично полагался на этот ответ на вопрос "Вызов функции, когда ng-repeat завершен". В отличие от этого Q & A, мне нужно передать аргумент моей директиве, и для этого я частично воспользовался этим ответом (к вопросу под названием "Angularjs - передать аргумент директиве" ).

Итак, в моем случае я добавил fixed-column-tooltip для моей директивы и columnselector в качестве аргумента для <tr> следующим образом:

<tr fixed-column-tooltip columnselector=".td-keyField" ng-repeat="trData in trDataWatch">

Но когда на второй ответ я добавил, что то, что я узнал, является "областью выделения" моей директивы, я больше не имел доступа к исходной области, необходимой в соответствии с первым ответом:

'use strict';

angular.module('cmt.cases.directives')

.directive('fixedColumnTooltip', function ($timeout) {
    return {
        restrict: 'A',
        scope: {
            columnselector: '@'
        },
        link: function (scope, element, attr) {
            if (scope.$last === true) { //undefined because not operating on original scope
        ...

Есть ли способ сохранить доступ к исходной области, но также иметь доступ к аргументу columnselector?

Ответ 1

Несмотря на то, что я почти полностью новичок в Angular, я отвечаю на свой вопрос, но все еще хочу получить дополнительные ответы, если я решил свою проблему, не считается "идиоматическим" Angular.

В частности, вместо использования области выделения я использовал третий аргумент link/function attrs (attributes) в моем коде ниже, чтобы в противном случае получить новый атрибут columnselector в html вместе с моей директивой. Это общепринятая практика?

'use strict';

angular.module('cmt.cases.directives')

.directive('fixedColumnTooltip', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            if (scope.$last === true) {
                $timeout(function () {
                    element.parent().find(attrs.columnselector).each(function() {
                        var td = $(this);
                        var span = td.find('span');

                        if (span.width() > td.width()){
                            span.attr('data-toggle','tooltip');
                            span.attr('data-placement','right');
                            span.attr('title', span.html());
                        }
                    });
                });
            }
        }
    }
});

ДОПОЛНЕНИЕ: Как вы можете видеть из комментариев, мне не удалось получить код из этого ответа, чтобы работать, несмотря на то, что вы пытаетесь это сделать несколькими разными способами. Если я делаю что-то неправильно в отношении включения этого ответа, пожалуйста, дайте мне знать.

Тем временем я нашел другой способ сделать это, но это почти наверняка больше "запаха кода", чем использование аргумента attrs. В частности, я обнаружил, что могу использовать область изоляции, а затем получить доступ к этому атрибуту scope $parent. Тогда я бы начал свой код следующим образом, но я не сторонник этого, но я просто отмечаю это, поскольку кажется, что TMTOWTDI (там более одного способа сделать это), безусловно, относится к Angular:

'use strict';

angular.module('cmt.cases.directives')

.directive('fixedColumnTooltip', function ($timeout) {
    return {
        restrict: 'A',
        scope: {
            columnselector: '@'
        },
        link: function (scope, element, attrs) {
            if (scope.$parent.$last === true) {
                $timeout(function () {
                    element.parent().find(scope.columnselector).each(function() {
                    ...

Ответ 2

Вы можете использовать

'use strict';

angular.module('cmt.cases.directives')

.directive('fixedColumnTooltip', function ($timeout) {
    return {
        restrict: 'A',
        scope: {
            columnselector: '@',
            $last: '=$last',
        },
        link: function (scope, element, attr) {
            if (scope.$last === true) {
            ....

второй параметр для области будет передавать $последний параметр по ссылке.

EDIT:

Так как $last доступен только в области повторного элемента, вы можете получить его из области видимости элемента, например

'use strict';

angular.module('cmt.cases.directives')

.directive('fixedColumnTooltip', function ($timeout) {
return {
    srestrict: 'A',
    scope: {
        columnselector: '@',
    },
    link: function (scope, element, attrs) {
      var elemScope = element.scope();
      if (elemScope.$last){
             ......
      }          
    }
}

Ответ 3

Хорошо, прежде всего, потому, что вы используете область изоляции, не означает, что вы не можете получить доступ к чему-либо в родительской области. Область изоляции предназначена для ограничения того, что вы получаете по умолчанию, но вы можете указать, что хотите от родительской области. Правильный способ сделать это - установить двустороннюю привязку в вашей директиве, используя "parentScopeVariable:" = '". Простите ужасное форматирование, я нахожусь на мобильном телефоне, и я хочу ложиться спать:-).

Итак, как вы сказали, вы тоже можете использовать параметр "attrs". Есть даже сложные методы $eval по настройке объектов в родительском пространстве, которые передаются только как attrs. В любом случае вы не можете иметь более одной директивы с областью выделения для данного элемента/компонента, поэтому вам действительно нужно быть осторожным при использовании области выделения. Это определенно поддается чистому дизайну, потому что вы должны быть преднамеренными в отношении того, что вы используете в своей директиве. Точка, полагающаяся на attrs, является прекрасной и необходимой иногда, на мой взгляд. По общему признанию, он чувствует себя немного взломанным или каким-либо другим (думая о запахе кода), но я не думаю, что для этого есть веский аргумент.

Наконец, я потратил много времени на сайт документа Angular API doc, и там было много хороших вещей. Там довольно хорошая директивная ссылка на странице компиляции $compile. Опять же, мобильный, извините. Если бы я был на полном компьютере, я бы сделал красивые блоки кода и связал директиву ref, извините:-). Быстрый google, и вы его найдете.

Таким образом, вы определенно можете использовать область изоляции и есть способы передать обратные вызовы функций в директиву, передать директивные функции ссылки из директивы, вернуться к контроллеру, двухсторонняя привязка данных и т.д. Изолировать область отлично для всего этого, но это звучит не так, как будто вы пытаетесь сделать что-то слишком сложное.

Ответ 4

В структуре Angular в шаблоне HTML вы можете получить доступ к родительской области.

Например:

<div ng-model="$parent.$parent.theModel"></div>

Это работает при создании новых областей внутри шаблона, например, ng-repeat и т.п. В теории вы можете использовать это для доступа к родительской области, которую вы хотите использовать.

Ответ 5

может быть немного уродливым, но работает: получите элемент DOM вашей текущей директивы, перейдите назад к родительскому объекту, сделайте его angular -элементом, вызовите встроенную функцию() на нем, например.

link: function (scope, elem) {
  var parentScope = angular.element ($(elem).parent()).scope();
  console.log (parentScope)
}

Ответ 6

Прикосновение к родительской области может быть не лучшей идеей (я имею в виду, что это не angular способ доступа к другому слою), лучше иметь некоторые дополнительные scope.models. Во всяком случае здесь простая рабочая демонстрация.

angular.module('app', [])
.controller('ctrl', function($scope){
  $scope.trDataWatch = ['item1', 'item2', 'item3'];
  $scope.state = 'unrendered';
  $scope.$on('ngRepeatFinished', function(){
     $scope.state = 'ngRepeatFinished';
  });
})
.directive('fixedColumnTooltip', function ($timeout) {
    return {
        restrict: 'A',
        scope: {
          columnselector: '@',
          first: '=?',
          middle: '=?',
          last: '=?',
          index: '=?',
          odd: '=?',
          even: '=?',
          
        },
        link: function (scope, element, attr) {
          if(scope.last){
              scope.$emit('ngRepeatFinished');
          }
        }
    };
});
td {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
  <h4>{{state}}</h4>
  
<table>
  <tr fixed-column-tooltip columnselector=".td-keyField"
      ng-repeat="trData in trDataWatch"
      index="$index"
      odd="$odd"
      even="$even"
      first="$first"
      middle="$middle"
      last="$last">
    <td>{{trData}}</td>
  </tr>
</table>
</div>

Ответ 7

Если вы хотите использовать область управления с директивой, вы должны сделать следующее

app.directive('fixedColumnTooltip', function ($timeout) {
return {
    restrict: 'A',

    link: function (scope, element, attr) {
      var columnselector = attr.columnselector;
      console.log(scope[columnselector]);
    }
}});

Это не создаст какой-либо области для директивы, и вы все равно сможете получить доступ к значению columnselector. Если вы хотите передать функцию в columselector, тогда вы можете сделать $parse (attr.columnselector). Если это значение, тогда $parse не требуется.

Ответ 8

Когда вы определяете область действия в директиве, вы создаете область выделения. Самый простой способ передать переменную $last будет следующим атрибутом:

<tr fixed-column-tooltip columnselector=".td-keyField" ng-repeat="trData in trDataWatch" last="$last">

Ваша область действия будет выглядеть так:

scope: {
    columnselector: '@',
    $last: '=last'
}

ИЛИ вы можете просто получить доступ к родительской области в своей функции ссылок:

link: function (scope, element, attr) {
    if (scope.$parent.$last === true) { // Will evaluate true one time
    }
}

В этом случае вам не понадобится другой атрибут, и вам не нужно будет определять $last в своей области действия. JSFiddle

Ответ 9

Просто. Используйте свой аргумент атрибута для своей функции ссылок...

link: function(scope, element, attributes, ctrl) {
  var selector = attributes.columnselector;
}