I am trying to create a proxy directive like so:
<x-field x-purpose="choice" x-values="data.countries" ng-model="model.country"/>
Where the field
directive forwards this to another directive, causing the following replacement:
<x-choiceField x-values="data.countries" ng-model="model.country"/>
[note:] the ng-model could be replaced by a reference to some new isolated scope.
The "field purpose" directive decides which implementation to use (e.g. drop-down/listbox/autocomplete?) based on how many values there are to choose from, client device size, etc - ultimately resulting in something like this:
<select ng-model="model.country" ng-options="data.countries">
This design is largely out of curiosity rather than for any practical reason, I am interested in how to achieve it rather than whether it is actually a good idea from a performance/simplicity point of view...
After reading [https://stackoverflow.com/a/18895441/1156377], I have something like this:
function proxyDirective($injector, $parse, element) {
return function (scope, element, attrs) {
var target = element.camelCase(attrs.name + '-field');
var model = attrs.ngModel;
var value = $parse(model);
var directive = $injector.get(target);
/* Bind ngModel to new isolated scope "value" property */
scope.$watch(model, function () {
???
});
/* Generate new directive element */
var pElement = angular.element.html('');
var pAttrs = {
value: ???
};
/* Forward to new directive */
return directive.compile(element, attrs, null)(scope, element, attrs);
};
}
function alphaFieldDirective() {
return {
replace: 'true',
template: '<input type="text" ng-value="forwarded value?">'
};
}
function betaFieldDirective() {
return {
replace: 'true',
template: '<textarea attributes? >{{ value }}</textarea>'
};
}
But I'm not sure how to achieve the forwarding or binding. This is my first forage into Angular directives, and it doesn't seem to be a particularly popular way of using them!
The purpose of this is to separate the purpose of a form field from its appearance/implementation, and to provide one simple directive for instantiating fields.
I implemented this via a service which proxies directives:
Fiddle: http://jsfiddle.net/HB7LU/7779/
HTML:
<body ng-app="myApp">
<h1>Directive proxying</h1>
<proxy target="bold" text="Bold text"></proxy>
<h1>Attribute forwarding</h1>
<proxy target="italic" style="color: red;" text="Red, italic text"></proxy>
</body>
Javascript:
angular.module('myApp', [])
.factory('directiveProxyService', directiveProxyService)
.directive('proxy', dirProxy)
.directive('bold', boldDirective)
.directive('italic', italicDirective)
;
function directiveProxyService($compile) {
return function (target, scope, element, attrs, ignoreAttrs) {
var forward = angular.element('<' + target + '/>');
/* Move attributes over */
_(attrs).chain()
.omit(ignoreAttrs || [])
.omit('class', 'id')
.omit(function (val, key) { return key.charAt(0) === '$'; })
.each(function (val, key) {
element.removeAttr(attrs.$attr[key]);
forward.attr(attrs.$attr[key], val);
});
$compile(forward)(scope);
element.append(forward);
return forward;
};
}
function dirProxy(directiveProxyService) {
return {
restrict: 'E',
terminal: true,
priority: 1000000,
replace: true,
template: '<span></span>',
link: function (scope, element, attrs) {
directiveProxyService(attrs.target, scope, element, attrs, ['target']);
}
};
}
function boldDirective() {
return {
restrict: 'E',
replace: true,
template: '<i>{{ text }}</i>',
scope: { text: '@' }
};
}
function italicDirective() {
return {
restrict: 'E',
replace: true,
template: '<i>{{ text }}</i>',
scope: { text: '@' }
};
}