AngularJS-Direktive mit Standardoptionen

145

Ich fange gerade mit anglejs an und arbeite daran, einige alte JQuery-Plugins in Angular-Direktiven zu konvertieren. Ich möchte eine Reihe von Standardoptionen für meine (Element-) Direktive definieren, die durch Angabe des Optionswerts in einem Attribut überschrieben werden können.

Ich habe mich nach der Art und Weise umgesehen, wie andere dies getan haben, und in der Angular-UI - Bibliothek scheint die ui.bootstrap.pagination etwas Ähnliches zu tun.

Zunächst werden alle Standardoptionen in einem konstanten Objekt definiert:

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

Dann wird eine getAttributeValueDienstprogrammfunktion an die Direktivensteuerung angehängt:

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

Schließlich wird dies in der Verknüpfungsfunktion verwendet, um Attribute als einzulesen

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

Dies scheint eine ziemlich komplizierte Einrichtung für etwas zu sein, das so Standard ist wie das Ersetzen einer Reihe von Standardwerten. Gibt es andere übliche Möglichkeiten, dies zu tun? Oder ist es normal, immer eine Dienstprogrammfunktion wie getAttributeValueund Analyseoptionen auf diese Weise zu definieren? Ich bin daran interessiert herauszufinden, welche unterschiedlichen Strategien Menschen für diese gemeinsame Aufgabe haben.

Als Bonus ist mir auch nicht klar, warum der interpolateParameter erforderlich ist.

Ken Chatfield
quelle

Antworten:

108

Sie können die compileFunktion verwenden - Attribute lesen, wenn sie nicht festgelegt sind - sie mit Standardwerten füllen.

.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; }
    },
        ...
  }
});
OZ_
quelle
1
Vielen Dank! Also irgendwelche Gedanken darüber, warum ui.bootstrap.paginationdie Dinge komplizierter werden? Dachte, dass bei Verwendung der Kompilierungsfunktion später vorgenommene Attributänderungen nicht berücksichtigt würden, dies scheint jedoch nicht der Fall zu sein, da zu diesem Zeitpunkt nur die Standardeinstellungen festgelegt werden. Vermutlich muss hier ein Kompromiss geschlossen werden.
Ken Chatfield
3
@ KenChatfield in compileSie können keine Attribute lesen, die interpoliert werden sollten, um einen Wert zu erhalten (der Ausdruck enthält). Wenn Sie jedoch nur überprüfen möchten, ob das Attribut leer ist, funktioniert es ohne Kompromisse für Sie (bevor das Interpolationsattribut eine Zeichenfolge mit Ausdruck enthält).
OZ_
1
Fantastisch! Vielen Dank für Ihre klare Erklärung. Für zukünftige Leser, obwohl tangential zur ursprünglichen Frage, für eine Erklärung, was der 'Interpolate'-Parameter im ui.bootstrap.paginationBeispiel bewirkt, fand ich dieses sehr nützliche Beispiel: jsfiddle.net/EGfgH
Ken Chatfield
Vielen Dank für diese Lösung. Beachten Sie, dass Sie bei Bedarf linkeine Funktion in Ihrer compileOption zurückgeben können. doc hier
mneute
4
Denken Sie daran, dass Attribute die Werte so benötigen, wie sie von der Vorlage übergeben werden. Wenn Sie ein Array übergeben, z. B. sollte es attributes.foo = '["one", "two", "three"]'stattattributes.foo = ["one", "two", "three"]
Dominik Ehrenberg
263

Verwenden Sie das =?Flag für die Eigenschaft im Bereichsblock der Direktive.

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';
      }
    }
  });
Jäger
quelle
4
=?ist verfügbar seit 1.1.x
Michael Radionov
34
Wenn Ihr Attribut trueoder falseals Werte akzeptieren könnte, würden Sie (glaube ich) $scope.hasName = angular.isDefined($scope.hasName) ? $scope.hasName : false;stattdessen zB verwenden wollen.
Paul D. Waite
22
Hinweis: Es funktioniert nur mit bidirektionaler Bindung, z. B. =?nicht mit einseitiger Bindung @?.
Justus Romijn
20
kann auch nur in Vorlage ausgeführt werden: Vorlage: 'Hallo {{Name || \ 'Standardname \'}} '
Vil
4
Sollte der Standardwert in der Steuerung oder in der linkFunktion eingestellt werden? Nach meinem Verständnis sollte das Zuweisen während des Zyklus linkeinen $scope.$apply()Zyklus vermeiden , nicht wahr?
Augustin Riedinger
1

Ich verwende AngularJS v1.5.10 und habe festgestellt, dass die preLinkKompilierungsfunktion zum Festlegen von Standardattributwerten ziemlich gut funktioniert .

Nur eine Erinnerung:

  • attrsEnthält die unformatierten DOM-Attributwerte, die immer entweder undefinedoder Zeichenfolgen sind.
  • scopeEnthält (unter anderem) die DOM-Attributwerte, die gemäß der bereitgestellten Isolationsbereichsspezifikation ( / / / usw.) analysiert wurden .=<@

Kurzer Ausschnitt:

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