I don't know if this is good form or not but I am rewriting the original post as I think it was a little confusing (I am not much of a writer). While this issue was encountered in an effort to port a Metro UI CSS behavior to an AngularJS app I can imagine it has much broader relevance. I apologize in advance if this is too verbose. I am trying to provide all the context and information I feel that I would need if I were trying to understand the problem.
I am trying to create an AngularJS directive to port a Metro UI CSS behavior, written as a jQuery 'widget', to an AngularJS application.
The Metro UI CSS behavior I am trying to port is 'data-transform' (more info here). This is used to simplify the addition of various INPUT elements to the web page. When applied to an INPUT element it handles manipulating the DOM by wrapping the INPUT element in the HTML structure required by Metro UI CSS to apply it's styling.
So ...
<!-- this ... -->
<input type="text" data-transform="input-control" />
<!-- turns into this ... -->
<div class="input-control file">
<input type="file" />
<button class="btn-file"></button>
</div>
Simply adding the 'ng-model' attribute "out of the box" obviously won't work.
<input type="text" data-transform="input-control" ng-model="page.data.productName"/>
The data successfully binds but the Metro UI data-transform is never fired off, therefore never creating the structure and and no styling is applied. After doing some research I learned that I need to wrap Metro UI CSS functionality in custom directives, to which I am a complete newbie. I took a stab at it and arrived at the following directive definition ...
(function () {
var metroInputTransformFn = function ($compile) {
return {
priority: 1000,
scope: { text: '=' },
compile: function (scope, element, attrs) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
iElement.inputTransform(); // apply Metro UI behavior here
},
post: function postLink(scope, iElement, iAttrs, controller) {
}
};
}
};
};
var metroDirectives = angular.module('metroDirectives', []);
metroDirectives.directive('metroInputTransform', ['$compile', metroInputTransformFn]);
})();
I put the call to apply the 'inputTransform' into the pre-link function since it was actually maniulating the DOM, and my understanding was that this would be executed BEFORE the DOM was walked by Angular looking for further directives.
I applied it to the HTML Input element ...
<input type="text" text="page.data.productName"
data-metro-input-transform=""
placeholder="Display Name/Label" />
This resolved one problem ... the Metro UI function successfully fires and fills out the surrounding HTML ...
<div class="input-control text">
<input type="text"
text="page.data.productName"
data-metro-input-transform=""
placeholder="Display Name/Label"
class="ng-isolate-scope">
<button class="btn-clear" tabindex="-1" type="button"></button>
</div>
The problem is that the model data does not bind to the underlying data source (in this case, a JSON object in an AngularJS service). After looking into the underlying Metro UI implementation of the "inputTransform" function I learned that in order to perform the DOM manipulation the original INPUT text-box is being cloned then ultimately removed from the DOM while the clone is what is wrapped in the DIV tag and rendered to the screen.
function(name, buttonName) {
var element = this.element;
var wrapper = $("<div/>").addClass("input-control").addClass(name);
var button = $("<button/>").addClass(buttonName);
var clone = element.clone(true); // clone original element
var parent = element.parent();
clone.appendTo(wrapper);
button.appendTo(wrapper);
wrapper.insertBefore(element);
element.remove(); // remove original element
return wrapper;
}
I am still confused why the data is not being bound though. Since I ran this in the 'pre-link' function I would have thought that the original element being removed would not have been a problem since the compile and linking had not yet been performed.
Thanks again and as always for your help,
G