Directiva AngularJS con opciones predeterminadas

4 minutos de lectura

avatar de usuario
ken chatfield

Estoy empezando con AngularJS y estoy trabajando para convertir algunos complementos antiguos de jQuery en directivas angulares. Me gustaría definir un conjunto de opciones predeterminadas para mi directiva (elemento), que se puede anular especificando el valor de la opción en un atributo.

He echado un vistazo a la forma en que otros han hecho esto, y en el interfaz de usuario angular biblioteca la ui.bootstrap.paginación parece hacer algo similar.

Primero, todas las opciones predeterminadas se definen en un objeto constante:

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

Entonces un getAttributeValue La función de utilidad se adjunta al controlador de directivas:

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

Finalmente, esto se usa en la función de enlace para leer atributos como

.directive('paginación', ['$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);
        ...
    }
});

This seems like a rather complicated setup for something as standard as wanting to replace a set of default values. Are there any other ways to do this that are common? Or is it normal to always define a utility function such as getAttributeValue and parse options in this way? I’m interested to find out what different strategies people have for this common task.

Also, as a bonus, I’m not clear why the interpolate parameter is required.

user avatar
Hunter

Use the =? flag for the property in the scope block of the directive.

angular.module('myApp',[]) .directive('myDirective', function(){ return { template: 'hello {{name}}', scope: { // use =? para indicar la propiedad como nombre opcional: '=?' }, controller: function ($scope){ // comprueba si se definió. Si no, establece un $scope.name predeterminado = angular.isDefined($scope.name) ? $scope.name : 'default name'; } } });

  • =? está disponible desde 1.1.x

    – Michael Radionov

    13 de febrero de 2014 a las 7:10

  • Si tu atributo pudiera aceptar true o false como valores, usted (creo) querría usar, por ejemplo $scope.hasName = angular.isDefined($scope.hasName) ? $scope.hasName : false; en cambio.

    –Paul D. Waite

    27 de febrero de 2014 a las 17:13

  • Nota: solo funciona con enlace bidireccional, por ejemplo =?pero no con enlace unidireccional, @?.

    – Justus Romijn

    15 de julio de 2014 a las 10:59

  • también se puede hacer solo en plantilla: plantilla: ‘hola {{nombre || \’nombre predeterminado\’}}’

    – Vildán

    7 de octubre de 2014 a las 1:28


  • Si el valor predeterminado se establece en el controlador o en el link ¿función? Basado en mi entendimiento, asignando durante el link debe evitar un $scope.$apply() ciclo, ¿no?

    – Agustín Riedinger

    30 de marzo de 2015 a las 14:38

avatar de usuario
ONZ_

Puedes usar compile función – leer atributos si no están configurados – llenarlos con valores predeterminados.

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

  • Thanks! So any thoughts on why ui.bootstrap.pagination does things in a more complicated way? Was thinking that if using the compile function any attribute changes made later would not be reflected, but this doesn’t appear to be true as only the defaults are set at this stage. Guess there must be some tradeoff being made here.

    – Ken Chatfield

    Sep 13, 2013 at 11:01


  • @KenChatfield in compile you can’t read attributes, which should be interpolated to get value (which contains expression). But if you want to check only if attribute is empty – it will work without any tradeoffs for you (before interpolation attribute will contain string with expression).

    – OZ_

    Sep 13, 2013 at 11:12


  • Fantastic! Thanks very much for your clear explanation. For future readers, although tangential to the original question, for an explanation of what the ‘interpolate’ parameter does in the ui.bootstrap.pagination example I found this very useful example: jsfiddle.net/EGfgH

    – Ken Chatfield

    Sep 13, 2013 at 11:22

  • Thanks a lot for that solution. Note that if you need the link option, you can still return a function in your compile option. doc here

    – mneute

    Jan 16, 2015 at 14:47

  • Remember, that attributes needs the values as they would be passed from the template. If you’re passing an array f.e. it should be attributes.foo = '["one", "two", "three"]' en vez de attributes.foo = ["one", "two", "three"]

    –Dominik Ehrenberg

    22 de abril de 2015 a las 7:39


Estoy usando AngularJS v1.5.10 y encontré el preLink función de compilación funcionar bastante bien para establecer valores de atributo predeterminados.

Simplemente un recordatorio:

  • attrs sostiene el crudo Valores de atributo DOM que siempre son undefined o cuerdas.
  • scope contiene (entre otras cosas) los valores de los atributos DOM analizado de acuerdo con la especificación de alcance aislada proporcionada (= / < / @ / etc.).

Fragmento abreviado:

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

¿Ha sido útil esta solución?