So first day on the job with angularjs and i'm not quite getting it. I'm trying to mimic an html5 placeholder using an angular directive. It totally works until I add an ng-model to the field and then it only works after a user interacts with the field and also breaks any value the field had.
code up here
the directive
App.directive('placehold', function(){
return {
restrict: 'A',
link: function(scope, element, attrs) {
var insert = function() {
element.val(attrs.placehold);
};
element.bind('blur', function(){
if(element.val() === '')
insert();
});
element.bind('focus', function(){
if(element.val() === attrs.placehold)
element.val('');
});
if(element.val() === '')
insert();
}
}
});
the html
<textarea ng-model="comment" placehold="with a model it doesn't work"></textarea>
seems super simple but i'm lost
So first day on the job with angularjs and i'm not quite getting it. I'm trying to mimic an html5 placeholder using an angular directive. It totally works until I add an ng-model to the field and then it only works after a user interacts with the field and also breaks any value the field had.
code up here http://jsbin.com/esujax/32/edit
the directive
App.directive('placehold', function(){
return {
restrict: 'A',
link: function(scope, element, attrs) {
var insert = function() {
element.val(attrs.placehold);
};
element.bind('blur', function(){
if(element.val() === '')
insert();
});
element.bind('focus', function(){
if(element.val() === attrs.placehold)
element.val('');
});
if(element.val() === '')
insert();
}
}
});
the html
<textarea ng-model="comment" placehold="with a model it doesn't work"></textarea>
seems super simple but i'm lost
Share Improve this question asked Feb 8, 2013 at 17:14 danedane 3981 gold badge2 silver badges8 bronze badges3 Answers
Reset to default 12Just few modifications in your sample:
app.directive('placehold', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attr, ctrl) {
var value;
var placehold = function () {
element.val(attr.placehold)
};
var unplacehold = function () {
element.val('');
};
scope.$watch(attr.ngModel, function (val) {
value = val || '';
});
element.bind('focus', function () {
if(value == '') unplacehold();
});
element.bind('blur', function () {
if (element.val() == '') placehold();
});
ctrl.$formatters.unshift(function (val) {
if (!val) {
placehold();
value = '';
return attr.placehold;
}
return val;
});
}
};
});
You can test it here: http://plnkr.co/edit/8m54JO?p=preview
Not sure, that it is the best solution, anyway it works. Even if you type the same text, that you have in your placehold attribute, cause it checks the model's value on focus.
You can also checkout an Angular.JS module that implements the "placeholder" attribute for older web browsers:
https://github.com/urish/angular-placeholder-shim
I have created a placeholder directive that can take angularjs expressions and also hides the placeholder text on input. You can read about the placeholder at http://blog.f1circle.com/2013/09/supporting-placeholders-in-non-html5.html
Here is the gist.
(function(angular, app) {
"use strict";
app.directive('placeholder',["$document", "$timeout", function($document, $timeout){
var link = function(scope,element,attrs,ctrl){
// if you dont use modernizr library use the solution given at
// http://stackoverflow.com/questions/5536236/javascript-check-for-native-placeholder-support-in-ie8
// to check if placeholder is supported natively
if(Modernizr.input.placeholder){
return;
}
/*
The following keys all cause the caret to jump to the end of the input value
27, Escape
33, Page up
34, Page down
35, End
36, Home
Arrow keys allow you to move the caret manually, which should be prevented when the placeholder is visible
37, Left
38, Up
39, Right
40, Down
The following keys allow you to modify the placeholder text by removing characters, which should be prevented when the placeholder is visible
8, Backspace
46 Delete
*/
var pTxt, modelValue, placeholding = false, badKeys = [27,33,34,35,36,37,38,39,40,8,46];
var unplacehold = function(){
if(!placeholding){
return;
}
placeholding = false;
element.removeClass('placeholder');
element.val('');
};
var placehold = function(){
if(placeholding || modelValue){
return;
}
placeholding = true;
element.addClass('placeholder');
element.val(pTxt);
};
var moveCaret = function(elem, index) {
var range;
if (elem.createTextRange) {
range = elem.createTextRange();
range.move("character", index);
range.select();
} else if (elem.selectionStart) {
elem.focus();
elem.setSelectionRange(index, index);
}
};
attrs.$observe('placeholder',function(value){
pTxt = value;
placeholding = false;
placehold();
});
ctrl.$parsers.unshift(function (value){
modelValue = value;
if(!value){
placehold();
}
if(placeholding){
return '';
}
return value;
});
ctrl.$formatters.unshift(function (value){
if(!value){
placehold();
modelValue = '';
return pTxt;
}
return value;
});
element.on('click focus contextmenu',function(event){
if($document[0].activeElement !== this){
return;
}
if(!modelValue){
moveCaret(this,0);
}
});
element.on('blur',function(){
placehold();
});
element.on('keydown',function(e){
if(!placeholding){
return;
}
if(_.contains(badKeys,e.keyCode)){
if(e.preventDefault){
e.preventDefault();
}
return false;
}
unplacehold();
});
element.on('keyup',function(e){
if(modelValue){
return;
}
placehold();
moveCaret(this,0);
});
element.on('paste',function(e){
$timeout(function(){
modelValue = element.val();
},0);
});
};
return{
restrict: 'A',
require: 'ngModel',
link : link,
priority:3,
};
}]);
})(angular, app);
This works on all cases except when copy and paste the same placeholder text.