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.
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
ofalse
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 ellink
debe evitar un$scope.$apply()
ciclo, ¿no?– Agustín Riedinger
30 de marzo de 2015 a las 14:38
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 ChatfieldSep 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 ChatfieldSep 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 yourcompile
option. doc here– mneuteJan 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 deattributes.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 sonundefined
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: ...
}
});