AngularJS директива з опціями за замовчуванням


145

Я лише починаю з angularjs і працюю над перетворенням декількох старих плагінів JQuery в директиви Angular. Я хотів би визначити набір параметрів за замовчуванням для моєї директиви (елемента), яку можна змінити, вказавши значення параметра в атрибуті.

Я роздивився , як це робили інші, і в бібліотеці з кутом ui ui.bootstrap.pagination, здається, робить щось подібне.

Спочатку всі параметри за замовчуванням визначаються в постійному об'єкті:

.constant('paginationConfig', {
  itemsPerPage: 10,
  boundaryLinks: false,
  ...
})

Потім getAttributeValueфункція утиліти приєднується до контролера директиви:

this.getAttributeValue = function(attribute, defaultValue, interpolate) {
    return (angular.isDefined(attribute) ?
            (interpolate ? $interpolate(attribute)($scope.$parent) :
                           $scope.$parent.$eval(attribute)) : defaultValue);
};

Нарешті, це використовується у функції зв’язку для читання в атрибутах як

.directive('pagination', ['$parse', 'paginationConfig', function($parse, config) {
    ...
    controller: 'PaginationController',
    link: function(scope, element, attrs, paginationCtrl) {
        var boundaryLinks = paginationCtrl.getAttributeValue(attrs.boundaryLinks,  config.boundaryLinks);
        var firstText = paginationCtrl.getAttributeValue(attrs.firstText, config.firstText, true);
        ...
    }
});

Це здається досить складним налаштуванням для чогось такого стандартного, як бажання замінити набір значень за замовчуванням. Чи є якісь інші способи зробити це загальними? Або нормально завжди визначати функцію утиліти, наприклад getAttributeValueпараметри розбору та таким чином? Мені цікаво дізнатися, які різні стратегії мають люди для цього спільного завдання.

Також як бонус мені не зрозуміло, для чого interpolateпотрібен параметр.

Відповіді:


108

Ви можете використовувати compileфункції атрибутів для читання, якщо вони не встановлені - заповнити їх значеннями за замовчуванням.

.directive('pagination', ['$parse', 'paginationConfig', function($parse, config) {
    ...
    controller: 'PaginationController',
    compile: function(element, attrs){
       if (!attrs.attrOne) { attrs.attrOne = 'default value'; }
       if (!attrs.attrTwo) { attrs.attrTwo = 42; }
    },
        ...
  }
});

1
Дякую! Тож будь-які думки про те, чому ui.bootstrap.paginationвсе відбувається складніше? Думав, що при використанні функції компіляції будь-які зміни атрибутів, внесені пізніше, не відображатимуться, але це, мабуть, не відповідає дійсності, оскільки на цьому етапі встановлюються лише типові параметри. Зрозуміло, тут повинен бути якийсь компроміс.
Ken Chatfield

3
@KenChatfield у compileвас не можна читати атрибути, які слід інтерполювати, щоб отримати значення (яке містить вираз). Але якщо ви хочете перевірити лише чи атрибут порожній - він буде працювати без будь-яких компромісів для вас (до того, як атрибут інтерполяції буде містити рядок з виразом).
OZ_

1
Фантастичний! Дуже дякую за ваше чітке пояснення. Для майбутніх читачів, хоча і дотичних до початкового запитання, для пояснення того, що робить параметр 'інтерполяція' у ui.bootstrap.paginationприкладі, я знайшов цей дуже корисний приклад: jsfiddle.net/EGfgH
Ken Chatfield

Дякую за рішення. Зауважте, що якщо вам потрібна linkопція, ви все одно можете повернути функцію у своєму compileваріанті. doc тут
mneute

4
Пам’ятайте, що для атрибутів потрібні такі значення, як вони будуть передані з шаблону. Якщо ви передаєте масив fe, він повинен бути attributes.foo = '["one", "two", "three"]'замістьattributes.foo = ["one", "two", "three"]
Домінік Еренберг,

263

Використовуйте =?прапор для властивості у блоці сфери дії директиви.

angular.module('myApp',[])
  .directive('myDirective', function(){
    return {
      template: 'hello {{name}}',
      scope: {
        // use the =? to denote the property as optional
        name: '=?'
      },
      controller: function($scope){
        // check if it was defined.  If not - set a default
        $scope.name = angular.isDefined($scope.name) ? $scope.name : 'default name';
      }
    }
  });

4
=?доступний з 1.1.x
Михайло Радіонов

34
Якби ваш атрибут міг прийняти trueабо falseяк значення, ви (я думаю) хотіли б використовувати, наприклад, $scope.hasName = angular.isDefined($scope.hasName) ? $scope.hasName : false;замість цього.
Пол Д. Уейт

22
Примітка: вона працює лише з двостороннім зв'язуванням, наприклад =?, але не з одностороннім зв'язуванням @?.
Юстус Ромійн

20
також можна виконати лише в шаблоні: template: 'привіт {{name || \ 'Ім'я по замовчуванням \'}} »
Віль

4
Чи слід встановлювати значення за замовчуванням у контролері чи у linkфункції? Виходячи з мого розуміння, призначаючи під час linkслід уникати $scope.$apply()циклу, чи не так?
Августин Рідінгер

1

Я використовую AngularJS v1.5.10 і виявив, що preLinkфункція компіляції працює досить добре для встановлення значень атрибутів за замовчуванням.

Просто нагадування:

  • attrsтримає сирого значення атрибутів DOM , які завжди або undefinedабо рядки.
  • scopeмістить (серед іншого) значення атрибутів DOM, розібрані відповідно до наданої специфікації області ізоляту ( =/ </@ / тощо).

Скорочений фрагмент:

.directive('myCustomToggle', function () {
  return {
    restrict: 'E',
    replace: true,
    require: 'ngModel',
    transclude: true,
    scope: {
      ngModel: '=',
      ngModelOptions: '<?',
      ngTrueValue: '<?',
      ngFalseValue: '<?',
    },
    link: {
      pre: function preLink(scope, element, attrs, ctrl) {
        // defaults for optional attributes
        scope.ngTrueValue = attrs.ngTrueValue !== undefined
          ? scope.ngTrueValue
          : true;
        scope.ngFalseValue = attrs.ngFalseValue !== undefined
          ? scope.ngFalseValue
          : false;
        scope.ngModelOptions = attrs.ngModelOptions !== undefined
          ? scope.ngModelOptions
          : {};
      },
      post: function postLink(scope, element, attrs, ctrl) {
        ...
        function updateModel(disable) {
          // flip model value
          var newValue = disable
            ? scope.ngFalseValue
            : scope.ngTrueValue;
          // assign it to the view
          ctrl.$setViewValue(newValue);
          ctrl.$render();
        }
        ...
    },
    template: ...
  }
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.