Decorating OOB angularjs directives
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yesterday
A while back someone was wondering on sndevs slack if they could add a seconds picker to the spDatePicker directive using attributes. Turns out the bootstrap datetimepicker that the directive uses in the portal forms for date/time fields has a config option for inputting seconds but it was not surfaced in the service portal wrapper. It was possible to achieve by copying a modified directive into an angular provider and using it as an element directive.
This got me wondering if it was possible to achieve this by somehow overriding the spDatePicker directive. I tried first by adding the directive definition in as an angular provider, but you get the multidir error in the compile phase as you are trying to add multiple directives to one dom element.
Reading on the documentation I found something called decorators in angularjs which look something like this:
angular.module('module').config('$provide', function ($provide) {
$provide.decorator('decorateDirective',
function ($delegate, $log, $parse) {
var originalLinkFn = $delegate[0].link;
$delegate[0].compile = function (tElem, tAttr) {
return function newLinkFn(scope, elem, attr) {
originalLinkFn.apply($delegate[0], arguments);
};
};
delete $delegate[0].link;
return $delegate;
}
);
});Decorators are a design pattern that is used to separate modification or decoration of a class without
modifying the original source code. In AngularJS, decorators are functions that allow a service, directive
or filter to be modified prior to its usage.
As we can't edit the directives that are in the widget table, our only option is to to register a decorator in the module config phase and try to override what we want from there. To do this we need to create a ui script and add it as a js include to the catalog item widget or the portal theme.
In the case of spDatePicker, there is a local object called config that gets used to init the picker. As we can't edit it the only option is to replace the whole link function. This happens by replacing the compile function in the directive in $delegate.
From docs:
Service Type Selector $delegate
| Service | serviceName | The object or function returned by the service |
| Directive | directiveName + 'Directive' | An Array.<DirectiveObject>1 |
| Filter | filterName + 'Filter' | The function returned by the filter |
angular.module('sn.$sp').config(function ($provide) {
$provide.decorator('spDatePickerDirective', function ($delegate, spConf, $rootScope, $document, $window, spAriaUtil, i18n, spDatePickerUtil, select2EventBroker, spUtil) {
var directive = $delegate[0];
//all the local helper functions from the directive factory function added here
//......
directive.compile = function (tElem, tAttrs) {
return function (scope, element, attrs, ngModel) {
//original link function copied in here
//....The link function calls a bunch of local helper functions so they need to be added in as well. I'll add the UI script for the js include as attachment as it ends up being very lengthy.
Conclusion
By decorating the oob and "hidden" spDatePicker, we are able to change how variables function in the portal in the oob forms and add or remove functionality. In this case adding the seconds picker to the oob date/time variable. This would work with other directives as well.