AngularJS directive with default options
Solution 1
You can use compile
function - read attributes if they are not set - fill them with default values.
.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; }
},
...
}
});
Solution 2
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 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';
}
}
});
Solution 3
I'm using AngularJS v1.5.10 and found the preLink
compile function to work rather well for setting default attribute values.
Just a reminder:
-
attrs
holds the raw DOM attribute values which are always eitherundefined
or strings. -
scope
holds (among other things) the DOM attribute values parsed according to the provided isolate scope specification (=
/<
/@
/ etc.).
Abridged snippet:
.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: ...
}
});
Related videos on Youtube
Ken Chatfield
Updated on June 19, 2020Comments
-
Ken Chatfield about 4 years
I'm just starting with angularjs, and am working on converting a few old JQuery plugins to Angular directives. I'd like to define a set of default options for my (element) directive, which can be overridden by specifying the option value in an attribute.
I've had a look around for the way others have done this, and in the angular-ui library the ui.bootstrap.pagination seems to do something similar.
First all default options are defined in a constant object:
.constant('paginationConfig', { itemsPerPage: 10, boundaryLinks: false, ... })
Then a
getAttributeValue
utility function is attached to the directive controller:this.getAttributeValue = function(attribute, defaultValue, interpolate) { return (angular.isDefined(attribute) ? (interpolate ? $interpolate(attribute)($scope.$parent) : $scope.$parent.$eval(attribute)) : defaultValue); };
Finally, this is used in the linking function to read in attributes as
.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); ... } });
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. -
Ken Chatfield almost 11 yearsThanks! 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. -
OZ_ almost 11 years@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). -
Ken Chatfield almost 11 yearsFantastic! 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 -
Michael Radionov over 10 years
=?
is available since 1.1.x -
Joe Dargie over 10 yearsIf your attribute could accept
true
orfalse
as values, you would (I think) want to use e.g.$scope.hasName = angular.isDefined($scope.hasName) ? $scope.hasName : false;
instead. -
Justus Romijn almost 10 yearsWow, this is a really clean solution. Couldn't find such a good example for default value usage on the Angular Docs. +1!
-
Justus Romijn almost 10 yearsNote: it only works with two-way binding, e.g.
=?
, but not with one-way binding,@?
. -
Fredrik Boström almost 10 yearsIs there a better way than doing
scope: { someValueAttr: '@someValue' }
, and in link functionscope.someValue = someValueAttr || 0
? Since you cannot write to a@
scope variable. -
Vildan over 9 yearsalso can be done in template only: template: 'hello {{name || \'default name\'}}'
-
mneute over 9 yearsThanks a lot for that solution. Note that if you need the
link
option, you can still return a function in yourcompile
option. doc here -
Augustin Riedinger over 9 yearsShould the default value be set in the controller or in the
link
function? Based on my understanding, assigning during thelink
should avoid a$scope.$apply()
cycle, shouldn't it? -
Dominik Ehrenberg about 9 yearsRemember, 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"]'
instead ofattributes.foo = ["one", "two", "three"]
-
Eugene Kulabuhov almost 9 years
&?
defaults toundefined
only starting with version 1.4.x. Before that you can use:$attrs.optionalProp === undefined
-
QOI about 7 yearsBut this will only work assuming that
name
doesn't change after the first initialization, ifname
is changed toundefined
later, from outside the directive, the code to assign the default will not be triggered. -
Ivan Prihhodko almost 7 yearsBetter answer here as it adhere's to the Angular API, this should be the accepted answer.
-
PhiLho almost 6 yearsSome notes: Without
?
it is still optional, there is just a subtle difference, defined in docs.angularjs.org/api/ng/service/$compile#-scope- (always hard to find back...). Somehow, $scope.optional === undefined is true in both cases. About later change mentioned by @QOI, target has to be notified, can be done by watching the attribute or the scope value.?
now work on all kinds of binding.