I'm currently working on a project where we're refactoring a pretty standard MVC3 application to utilize plain HTML for the markup, with Angular JS as the MVVM structure and a Web API RESTful services implementation. Up until the last week or so my part has been mostly limited to the server so I haven't done much with Angular. Today, however, I got the opportunity to create a directive for the first time and it was awesome.
I had cause to apply a max length to a textarea element in IE 8 (yes, we're supporting IE 8, go ahead and feel sorry for us), which doesn't support HTML5. There's a pretty awesome jQuery plug-in called jQuery Max Length that worked really well for us in the MVC3 implementation, but it wasn't the "Angular way" so we tried to port it over to a directive.
It took me about four hours to get it all put together, but I'm really happy with what I ended up with. This directive works in IE 8 (though it could be a lot better to be honest) and it's "fast enough". We didn't spend a ton of time tweaking this for performance in IE 8 because as an organization we're moving away from it in the near future (that's what they tell me anyway). So here it is. I have some modifications I want to make to it, but I'm happy with what I have here.
app.directive('myTextarea', function () {
'use strict';
return {
restrict: 'A',
replace: true,
require: 'ngModel',
template: function (el, attr) {
return '<div><textarea ng-model=' + attr.ngModel +
' name=' + attr.name +
' class="' + attr.parentClass +
'" max=' + attr.max +
'></textarea><br /><span class="' +
attr.childClass + '">0/' + attr.max +
'</span></div>';
},
link: function (scope, el, attr, ctrl) {
scope.$watch(function() { return ctrl.$modelValue; }, function() {
// we have to get value from the DOM because Angular by default trims the input
// the problem with that is if you're typing and backspace over a word, you also
// (unwittingly) backspace over the space before the word
var value = $(el).children('textarea').val();
var resultingValue = value;
if (value.length == attr.max) {
$(el).children('textarea').addClass('maxlength-full');
changeColors($(el).children('span'), 'red');
}
else if (value.length > attr.max) {
$(el).children('textarea').addClass('maxlength-full');
changeColors($(el).children('span'), 'red');
resultingValue = value.substring(0, attr.max);
} else {
$(el).children('textarea').removeClass('maxlength-full');
if (value.length > (attr.max * .9)) {
changeColors($(el).children('span'), 'yellow');
} else {
changeColors($(el).children('span'), 'green');
}
}
ctrl.$viewValue = resultingValue;
$(el).children(1).val(ctrl.$viewValue);
$(el).children('span').text(ctrl.$viewValue.length + '/' + attr.max);
});
}
};
function changeColors(el, color) {
switch(color) {
case 'red':
el.removeClass('maxlength-feedback-green');
el.removeClass('maxlength-feedback-yellow');
el.addClass('maxlength-feedback-red');
break;
case 'yellow':
el.removeClass('maxlength-feedback-green');
el.removeClass('maxlength-feedback-red');
el.addClass('maxlength-feedback-yellow');
break;
case 'green':
el.removeClass('maxlength-feedback-yellow');
el.removeClass('maxlength-feedback-red');
el.addClass('maxlength-feedback-green');
break;
}
}
});
No comments:
Post a Comment