Direttiva AngularJS con opzioni predefinite


145

Ho appena iniziato con angularjs e sto lavorando alla conversione di alcuni vecchi plugin JQuery in direttive angolari. Vorrei definire un set di opzioni predefinite per la mia direttiva (elemento), che può essere sovrascritto specificando il valore dell'opzione in un attributo.

Ho dato un'occhiata al modo in cui altri lo hanno fatto, e nella libreria angular -ui la ui.bootstrap.pagination sembra fare qualcosa di simile.

Innanzitutto tutte le opzioni predefinite sono definite in un oggetto costante:

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

Quindi una getAttributeValuefunzione di utilità è collegata al controller della direttiva:

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

Infine, questo viene utilizzato nella funzione di collegamento per leggere gli attributi come

.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);
        ...
    }
});

Sembra una configurazione piuttosto complicata per qualcosa di standard come voler sostituire un set di valori predefiniti. Ci sono altri modi per farlo che sono comuni? Oppure è normale definire sempre una funzione di utilità come getAttributeValuee analizzare le opzioni in questo modo? Sono interessato a scoprire quali diverse strategie hanno le persone per questo compito comune.

Inoltre, come bonus, non sono chiaro il motivo per cui il interpolateparametro è richiesto.

Risposte:


108

Puoi usare la compilefunzione - leggi gli attributi se non sono impostati - riempili con i valori predefiniti.

.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
Grazie! Quindi qualche idea sul perché le ui.bootstrap.paginationcose in un modo più complicato? Stavo pensando che se si utilizza la funzione di compilazione le modifiche agli attributi apportate in un secondo momento non verrebbero riflesse, ma ciò non sembra essere vero poiché solo i valori predefiniti sono impostati in questa fase. Immagino che qui ci sia un certo compromesso.
Ken Chatfield,

3
@KenChatfield in compilete non può leggere gli attributi, che dovrebbero essere interpolati per ottenere valore (che contiene espressione). Ma se vuoi controllare solo se l'attributo è vuoto, funzionerà senza alcun compromesso (prima che l'attributo di interpolazione conterrà una stringa con espressione).
OZ_

1
Fantastico! Grazie mille per la tua chiara spiegazione. Per i futuri lettori, sebbene tangente alla domanda originale, per una spiegazione di ciò che fa il parametro 'interpolate' ui.bootstrap.paginationnell'esempio, ho trovato questo esempio molto utile: jsfiddle.net/EGfgH
Ken Chatfield,

Grazie mille per questa soluzione. Nota che se hai bisogno linkdell'opzione, puoi comunque restituire una funzione nella tua compileopzione. doc qui
mneute il

4
Ricorda, che gli attributi hanno bisogno dei valori in quanto verrebbero passati dal modello. Se stai passando un array per questo dovrebbe essere attributes.foo = '["one", "two", "three"]'invece diattributes.foo = ["one", "two", "three"]
Dominik Ehrenberg il

263

Utilizzare il =?flag per la proprietà nel blocco ambito della direttiva.

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
=?è disponibile dall'1.1.x
Michael Radionov,

34
Se il tuo attributo potesse accettare trueo falsecome valori, (penso) vorresti usare ad esempio $scope.hasName = angular.isDefined($scope.hasName) ? $scope.hasName : false;invece.
Paul D. Waite,

22
Nota: funziona solo con il legame a due vie, ad esempio =?, ma non con il legame a senso unico, @?.
Justus Romijn,

20
inoltre può essere eseguito solo in template: template: 'hello {{name || \ 'nome predefinito \'}} '
Vil

4
Il valore predefinito deve essere impostato nel controller o nella linkfunzione? In base alla mia comprensione, l'assegnazione durante linkdovrebbe evitare un $scope.$apply()ciclo, non è vero?
Augustin Riedinger,

1

Sto usando AngularJS v1.5.10 e ho trovato che la preLinkfunzione di compilazione funziona piuttosto bene per impostare i valori degli attributi predefiniti.

Solo un promemoria:

  • attrsdetiene i grezzi valori di attributo DOM che sono sempre o undefinedo stringhe.
  • scopecontiene (tra le altre cose) i valori dell'attributo DOM analizzati secondo la specifica dell'ambito di isolamento fornita ( =/ </ @/ ecc.).

Snippet abbreviato:

.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: ...
  }
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.