AngularJs Directive: Using TemplateURL. Replace element. Add form input. Getting form.input.$error object - forms

Not sure if this is possible but I'm trying, and keep coming up short.
http://plnkr.co/edit/Gcvm0X?p=info
I want a 'E' (element) directive that is replaced with a more complex nested HTML node using the 'templateUrl' feature of directives.
HTML defining the directive (form tag included for complete mental image):
<form id="frm" name="frm">
<ds-frm-input-container
class="col-md-1"
frm-Name="frm"
frm-obj="frm"
input-name="txtFName"
ds-model="user.firstName"></ds-frm-input-container>
</form>
TemplateUrl contents which 'replaces' the above directive 'ds-frm-input-container' HTML element:
<div>
<input
required
ng-minlength=0
ng-maxlength=50
class="form-control"
ng-model="dsModel"
placeholder="{{dsPlaceHolder}}" />
<span ng-if="showErrs" class="label label-danger">FFFFF: {{dsModel}}</span>
</div>
Controller and Directive:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = "Nacho";
$scope.user = {};
$scope.user.firstName = "";
})
.directive('dsFrmInputContainer', function(){
var ddo = {
priority: 0,
restrict: 'AE',
scope:
{
frmName: '#',
inputName: '#',
dsPlaceHolder: '#',
dsModel: '=',
frmObj: '='
},
templateUrl: 'template1.html',
replace: true,
controller: function($scope)
{
$scope.showErrs = true;
},
compile: function compile(ele, attr) {
return {
pre: function preLink(scope, ele, attr, controller)
{
},
post: function postLink(scope, ele, attr, controller)
{
var txt = ele.find('input');
txt.attr('id', scope.inputName);
txt.attr('name', scope.inputName);
//BLUR
txt.bind('blur', function () {
console.log("BLUR BLUR BLUR");
angular.forEach(scope.frmObj.$error, function(value, key){
var type = scope.frmObj.$error[key];
for(var x=0; x < type.length; x++){
console.log(type[x]);
}
});
event.stopPropagation();
event.preventDefault();
});
}
};
},
};
return ddo;
});
The directive replaces just fine and the input element is named just fine. The form object however doesn't include the input element name in the error information. This makes it impossible for me to single out the input element during a 'blur' event that is setup in the directive.
I am doing this trying to reduce the show/hide logic 'noise' in the html for error messages (spans) and it should be reusable.
UPDATE (2014.01.28):
2014.01.28:
Added promises. There is a service that allows validation on button clicks. NOT USING built in angular validation anymore found some compatibility issues with another library (or viceversa).
ORIGINAL:
Here is my form validation directive vision completed (plnkr link below). Completed in concert with the help of the stack overflow community. It may not be perfect but neither are butterfingers but they taste good.
http://plnkr.co/edit/bek8WR?p=info

So here is a link that has the name variables set as expected on the given input form error object. http://plnkr.co/edit/MruulPncY8Nja1BUfohp?p=preview
The only difference is that the inputName is read from the attrs object and is not part of the scope. This is then read before the link function is returned, in the compile phase, to set the template DOM correctly.

I have just spent quite a while trying to sort this problem out, and while this is not exactly what you were looking for, his is my attempt. It uses bootstrap for all the styling, and allows for required and blur validation, but its definitely not finished yet. Any thoughts or advice much appreciated.
https://github.com/mylescc/angular-super-input

Related

Use mustache for templating smaller parts for use multiple times in a form

Okay. So I hope this is specific enough.
I'm, kind of new to mustache, but see it has great potential, so why not use it.
I'm making a quite big form, and want to have the form built with mustache. So i have started to make the form in mustache and, then i realized i want to template the form-elements. One template for how i want every narrow input, wide input, select etc. to look like. because now i'm repeating myself.
My template and partials are provided through $.ajax get, where the main form template are defined as a mustache file, with html content, and the partials are defined as a mustache file with every template inside -tags.
Variables for mustache to use. This object is somewhat subject for change.
var jsonForm = {
oneInputField: {
value:'put your title here',
rule_set: {
required: {
strName: 'required',
strErrorMsg: 'error message'
}
}
},
oneSelect: {
options: [
{value: '- Pick one -', helper: 'helper', select_options: {disable_search: true}},
{value: 'option1', selected: true},
{value: 'option2'},
{value: 'option3'},
{value: 'option4'}
],
rule_set: {
required: {
strName: 'required',
strErrorMsg: 'error message'
}
}
}
};
How i fetch the data
$.ajax({
url: 'myForm.mustache',
type: 'get',
success: function(template) {
$.ajax({
url: 'myFormElements.mustache',
type: 'get',
success: function(partials) {
var $html = Mustache.render(template, jsonForm.fields, partials);
$('div#formContent').html($html);
$('div#formContent select').chosen();
if (jsonForm.fields.title.length > 1) {
$('div#header').html(jsonForm.fields.title);
}
}
});
}
});
Partials. Would actually like to have it all in separate files, but it doesn't seem to be possible without making a ton of ajax calls, so I keep with my current two mustache templates.
<script type="text/html" id="inputNarrow">
<label>{{label}}:</label><input value="{{value}}">
{{#rule_set.required}}<div class="required">*</div>{{/rule_set.required}}
{{^rule_set.required}}<div class="not-required"> </div>{{/rule_set.required}}
<div class="clearfix"></div>
</script>
and my form-template
<div id="formContainer" class="border-box">
<div class="group-box">
<p>Fields marked with <span class="required">*</span> are required to complete the form</p>
</div>
<div class="group-box">
<h2>Partial element test</h2>
<div class="form-container">
{{>partials.inputNarrow}} <-- I have to be able to specify what data I want to enter here.
</div>
<div class="form-container">
{{>partials.selectNarrow}} <-- I have to be able to specify what data I want to enter here.
</div>
</div>
So my question is is it possible for a bigger one big unique mustache-template to use "element"-templates (also in mustache), for rendering?
I am not very open to adding additional libraries to my project, like ICanHaz or similar, since this is for work.
Really sorry if this question is answered before, but I couldn't find it
I found an alternative way of doing what I tried to do.
I'm not sure if it's any good, but it does the job.
I used the possibility of making functions inside the object sent to the main-tempalte "form-template".
wich looks like this:
var smallRender = function(url, containerId, objData) {
$.ajax({
url: url,
type: 'get',
success: function(template) {
var $html = Mustache.render(template, objData);
var $objContainer = $('div#' + containerId);
$objContainer.append($html);
}
});
};
the actual object
oneInputField: {
value:'put your title here',
rule_set: {
required: {
strName: 'required',
strErrorMsg: 'error message'
}
},
getRendered: function() { // I would like to cache this somehow
smallRender(templateUrl.input.narrow, jsonForm.fields.title.containerId, {
label: jsonForm.labels.title,
field: jsonForm.fields.title,
required: isRequired(jsonForm.fields.title)
});
}
},
So basicly i send an ajax call to get the main template, and for each element in the form I want to present, i call on the getRendered in main-tamplate mustache, and it basicly renders the content with the smaller template.
tempalte url is basicly a collections with the urls to all the templates. so if I specify templateUrl.input.narrow i will get the narrow input element and so on.
Not sure as I mentioned if this is the "correct" way of doing it, or how good it will scale in a big environment, but it works.
I will mark my this as the correct answer (if I'm even allowed), since there are no other answers that solves my problem.

How do I get element related to active input in jQuery UI Autocomplete?

I'm trying to pass a custom form attribute (category) through jQuery UI Autocomplete to use in a product search. The form looks like <form id="BulkOrderForm" category="samplecategory"><input></input>...</form> and contains inputs that use the autocomplete script. There can be several forms on each page, so I need to be able to get the category value from the form that contains the active input field.
Here's my source:
function autocomplete() {
$("input.wcbulkorderproduct").autocomplete({
element: function(){
var element = $('form#BulkOrderForm').attr('category');
return element;
},
source: function(request, response, element){
$.ajax({
url: WCBulkOrder.url+'?callback=?&action='+acs_action+'&_wpnonce='+WCBulkOrder.search_products_nonce,
dataType: "json",
data: {
term: request.term,
category: element
},
success: function(data) {
response(data);
}
});
}
});
}
Any thoughts on how this can be acheived?
If I'm understanding correctly, you're trying to use the active input's parent form in the ajax request. Here's a way to achieve that:
Html:
<form data-category="form1">
<input type="text" />
<input type="text" />
</form>
<form data-category="formB">
<input type="text" />
<input type="text" />
</form>
JS:
$('form').each(function () {
var category = $(this).data('category');
$(this).find('input').autocomplete({
source: function (request, response) {
response([category]);
}
});
});
Instead of using autocomplete on a catch-all selector that gets inputs from all forms, first select the forms themselves. For each one, extract the category, then select all child inputs and call autocomplete on that result. Then you can use the category variable in the ajax call - in the example I'm simply passing it to the callback to display.
http://jsfiddle.net/Fw2QA/
I'll give you another solution, you can lookup the parent form of the active input, and extract the attribute from it. Because I don't know if this category in your form is dynamic or no, or either if you can control all of the process involved in your code, I'll give you a more generic solution, although if that attribute is dynamic "Turch" solution is way better than mine, by letting the data functionality of jquery handle the attribute changes, if it's static, than you can just do it like this:
function autocomplete() {
var element = $("input.wcbulkorderproduct").autocomplete({
source: function(request, response){
$.ajax({
url: WCBulkOrder.url+'?callback=?&action='+acs_action+'&_wpnonce='+WCBulkOrder.search_products_nonce,
dataType: "json",
data: {
term: request.term,
category: element
},
success: function(data) {
response(data);
}
});
}
}).parents('form').first().attr('category');
//chained call, sets autocomplete, grabs the parent form and the attribute
//which is saved on the variable element, and is used on every call through
//javascript context inheritance.
}
UPDATE
A little example illustrating my suggestion (provided by #Turch > thanks), can be found here.

Comparing two input values in a form validation with AngularJS

I am trying to do a form validation using AngularJS. I am especially interested in comparing two values. I want the user to confirm some data he entered before he continues. Lets say I have the code below:
<p>
Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2">
<p>
and then I can use validation with:
<span ng-show="registerForm.email1.$error.required">Required!</span>
<span ng-show="registerForm.email1.$error.email">Not valid email!</span>
<span ng-show="emailReg !== emailReg2">Emails have to match!</span> <-- see this line
registerForm.$valid will react correctly as to the text in inputs except I do not know how to use comparison within this validation to force the emails to be the same before allowing the user to submit the form.
I would love to have a solution without custom directives, but if this can't be achieved without it I will deal with it. Here is an answer that addresses similar issue with a custom directive.
Any help appreciated, thank you
You should be able to use ng-pattern/regex for comparing 2 input values
Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">
and validation with:
<span ng-show="registerForm.email2.$error.pattern">Repeat Email should have the same value with email!</span>
One way to achieve this is with a custom directive. Here's an example using a custom directive (ng-match in this case):
<p>Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-match="emailReg"></p>
<span data-ng-show="myForm.emailReg2.$error.match">Emails have to match!</span>
NOTE: It's not generally recommended to use ng- as a prefix for a custom directive because it may conflict with an official AngularJS directive.
Update
It's also possible to get this functionality without using a custom directive:
HTML
<button ng-click="add()></button>
<span ng-show="IsMatch">Emails have to match!</span>
Controller
$scope.add = function() {
if ($scope.emailReg != $scope.emailReg2) {
$scope.IsMatch=true;
return false;
}
$scope.IsMatch=false;
}
trainosais - you are right, validation should be done on a directive level. It's clean, modular and allows for reusability of code. When you have basic validation like that in a controller you have write it over and over again for different forms. That's super anti-dry.
I had a similar problem recently and sorted it out with a simple directive, which plugs in to the parsers pipeline, therefore stays consistent with Angular architecture. Chaining validators makes it very easy to reuse and that should be considered the only solution in my view.
Without further ado, here's the simplified markup:
<form novalidate="novalidate">
<label>email</label>
<input type="text"
ng-model="email"
name="email" />
<label>email repeated</label>
<input ng-model="emailRepeated"
same-as="email"
name="emailRepeated" />
</form>
And the JS code:
angular.module('app', [])
.directive('sameAs', function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
ngModel.$parsers.unshift(validate);
// Force-trigger the parsing pipeline.
scope.$watch(attrs.sameAs, function() {
ngModel.$setViewValue(ngModel.$viewValue);
});
function validate(value) {
var isValid = scope.$eval(attrs.sameAs) == value;
ngModel.$setValidity('same-as', isValid);
return isValid ? value : undefined;
}
}
};
});
The directive hooks into the parsers pipeline in order to get notified of any changes to the view value and set validity based on comparison of the new view value and the value of the reference field. That bit is easy. The tricky bit is sniffing for changes on the reference field. For that the directive sets a watcher on the reference value and force-triggeres the parsing pipeline, in order to get all the validators run again.
If you want to play with it, here is my pen:
http://codepen.io/jciolek/pen/kaKEn
I hope it helps,
Jacek
I recently wrote a custom directive which can be generic enough to do any validation. It take a validation function from the current scope
module.directive('customValidator', [function () {
return {
restrict: 'A',
require: 'ngModel',
scope: { validateFunction: '&' },
link: function (scope, elm, attr, ngModelCtrl) {
ngModelCtrl.$parsers.push(function (value) {
var result = scope.validateFunction({ 'value': value });
if (result || result === false) {
if (result.then) {
result.then(function (data) { //For promise type result object
ngModelCtrl.$setValidity(attr.customValidator, data);
}, function (error) {
ngModelCtrl.$setValidity(attr.customValidator, false);
});
}
else {
ngModelCtrl.$setValidity(attr.customValidator, result);
return result ? value : undefined; //For boolean result return based on boolean value
}
}
return value;
});
}
};
}]);
To use it you do
<input type="email" name="email2" ng-model="emailReg2" custom-validator='emailMatch' data-validate-function='checkEmailMatch(value)'>
<span ng-show="registerForm.email2.$error.emailMatch">Emails have to match!</span>
In you controller then you can implement the method, that should return true or false
$scope.checkEmailMatch=function(value) {
return value===$scope.emailReg;
}
The advantage is that you do not have to write custom directive for each custom validation.
When upgrading angular to 1.3 and above I found an issue using Jacek Ciolek's great answer:
Add data to the reference field
Add the same data to the field with the directive on it (this field is now valid)
Go back to the reference field and change the data (directive field remains valid)
I tested rdukeshier's answer (updating var modelToMatch = element.attr('sameAs') to var modelToMatch = attrs.sameAs to retrieve the reference model correctly) but the same issue occurred.
To fix this (tested in angular 1.3 and 1.4) I adapted rdukeshier's code and added a watcher on the reference field to run all validations when the reference field is changed. The directive now looks like this:
angular.module('app', [])
.directive('sameAs', function () {
return {
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
var modelToMatch = attrs.sameAs;
scope.$watch(attrs.sameAs, function() {
ctrl.$validate();
})
ctrl.$validators.match = function(modelValue, viewValue) {
return viewValue === scope.$eval(modelToMatch);
};
}
};
});
Updated codepen
No need a function or a directive. Just compare their $modelValue from the view:
ng-show="formName.email.$modelValue !== formName.confirmEmail.$modelValue"
More detailed example:
<span ng-show="(formName.email.$modelValue !== formName.confirmEmail.$modelValue)
&& formName.confirmEmail.$touched
&& !formName.confirmEmail.$error.required">Email does not match.</span>
Please note that the ConfirmEmail is outside the ViewModel; it's property of the $scope. It doesn't need to be submitted.
use ng-pattern, so that ng-valid and ng-dirty can act correctly
Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">
<span ng-show="registerForm.email2.$error.pattern">Emails have to match!</span>
#Henry-Neo's method was close, it just needed more strict Regex rules.
<form name="emailForm">
Email: <input type="email" name="email1" ng-model="emailReg">
Repeat Email: <input type="email" name="email2" ng-model="emailReg2" ng-pattern="(emailReg)">
</form>
By including the regex rule inside parentheses, it will match the entire string of emailReg to emailReg2 and will cause the form validation to fail because it doesn't match.
You can then drill into the elements to find out which part is failing.
<p ng-show="emailForm.$valid">Form Valid</p>
<p ng-show="emailForm.email1.$error">Email not valid</p>
<p ng-show="emailForm.email1.$valid && emailForm.email1.$error.pattern">
Emails Do Not Match
</p>
This module works well for comparing two fields. Works great with Angular 1.3+. Simple to use
https://www.npmjs.com/package/angular-password
It also allows saving the module as a generic. Just include them in packages list of your module.
Here is my simple version of the custom validator directive:
angular.module('app')
.directive('equalsTo', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ngModel) {
scope.$watchGroup([attrs.equalsTo, () => ngModel.$modelValue], newVal => {
ngModel.$setValidity('equalsTo', newVal[0] === newVal[1]);
});
}
};
})
Here is an angular 1.3 version of the sameAs directive:
angular.module('app').directive('sameAs', [function() {
'use strict';
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, element, attrs, ctrl) {
var modelToMatch = element.attr('sameAs');
ctrl.$validators.match = function(modelValue, viewValue) {
return viewValue === scope.$eval(modelToMatch);
};
}
};
}]);
Mine is similar to your solution but I got it to work. Only difference is my model. I have the following models in my html input:
ng-model="new.Participant.email"
ng-model="new.Participant.confirmEmail"
and in my controller, I have this in $scope:
$scope.new = {
Participant: {}
};
and this validation line worked:
<label class="help-block" ng-show="new.Participant.email !== new.Participant.confirmEmail">Emails must match! </label>
Thanks for the great example #Jacek Ciolek. For angular 1.3.x this solution breaks when updates are made to the reference input value. Building on this example for angular 1.3.x, this solution works just as well with Angular 1.3.x. It binds and watches for changes to the reference value.
angular.module('app', []).directive('sameAs', function() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
sameAs: '='
},
link: function(scope, elm, attr, ngModel) {
if (!ngModel) return;
attr.$observe('ngModel', function(value) {
// observes changes to this ngModel
ngModel.$validate();
});
scope.$watch('sameAs', function(sameAs) {
// watches for changes from sameAs binding
ngModel.$validate();
});
ngModel.$validators.sameAs = function(value) {
return scope.sameAs == value;
};
}
};
});
Here is my pen: http://codepen.io/kvangrae/pen/BjxMWR
You have to look at the bigger problem. How to write the directives that solve one problem. You should try directive use-form-error. Would it help to solve this problem, and many others.
<form name="ExampleForm">
<label>Password</label>
<input ng-model="password" required />
<br>
<label>Confirm password</label>
<input ng-model="confirmPassword" required />
<div use-form-error="isSame" use-error-expression="password && confirmPassword && password!=confirmPassword" ng-show="ExampleForm.$error.isSame">Passwords Do Not Match!</div>
</form>
Live example jsfiddle
I need to do this just in one form in my entire application and i see a directive like a super complicate for my case, so i use the ng-patter like some has point, but have some problems, when the string has special characters like .[\ this broke, so i create a function for scape special characters.
$scope.escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
and in the view
<form name="ExampleForm">
<label>Password</label>
<input ng-model="password" required />
<br>
<label>Confirm password</label>
<input ng-model="confirmPassword" required ng-pattern="escapeRegExp(password)"/>
</form>
Of course for very simple comparisons you can always use ngMin/ngMax.
Otherwise, you can go with a custom directive, and there is no need of doing any $watch or $observe or $eval or this fancy $setValidity back and forth. Also, there is no need to hook into the postLink function at all. Try to stay out of the DOM as much as possible, as it is against the angular spirit.
Just use the lifecycle hooks the framework gives you. Add a validator and $validate at each change. Simple as that.
app.directive('sameAs', function() {
return {
restrict: 'A',
require: {
ngModelCtrl: 'ngModel'
},
scope: {
reference: '<sameAs'
},
bindToController: true,
controller: function($scope) {
var $ctrl = $scope.$ctrl;
//add the validator to the ngModelController
$ctrl.$onInit = function() {
function sameAsReference (modelValue, viewValue) {
if (!$ctrl.reference || !modelValue) { //nothing to compare
return true;
}
return modelValue === $ctrl.reference;
}
$ctrl.ngModelCtrl.$validators.sameas = sameAsReference;
};
//do the check at each change
$ctrl.$onChanges = function(changesObj) {
$ctrl.ngModelCtrl.$validate();
};
},
controllerAs: '$ctrl'
};
});
Your plunker.
I've modified method of Chandermani to be compatible with Angularjs 1.3 and upper. Migrated from $parsers to $asyncValidators.
module.directive('customValidator', [function () {
return {
restrict: 'A',
require: 'ngModel',
scope: { validateFunction: '&' },
link: function (scope, elm, attr, ngModelCtrl) {
ngModelCtrl.$asyncValidators[attr.customValidator] = function (modelValue, viewValue) {
return new Promise(function (resolve, reject) {
var result = scope.validateFunction({ 'value': viewValue });
if (result || result === false) {
if (result.then) {
result.then(function (data) { //For promise type result object
if (data)
resolve();
else
reject();
}, function (error) {
reject();
});
}
else {
if (result)
resolve();
else
reject();
return;
}
}
reject();
});
}
}
};
}]);
Usage is the same

Enforcing Case Insensitive uniqueness on fields at a given nesting level using angularjs

<FORM>
<DIV class="outer-class">
<INPUT class="toValidate" type = "text"/>
<INPUT class="somethingElse" type= "text"/>
<INPUT class="toValidate" type ="text"/>
</DIV>
<DIV class="outer-class">
<INPUT class="toValidate" type = "text"/>
<INPUT class="somethingElse" type= "text"/>
<INPUT class="toValidate" type ="text"/>
</DIV>
<INPUT type="submit"/>
</FORM>
My question is: How do I ensure that for the form to be valid, the nested toValidates have a unique value but only within the same outer div?
I am guessing this logic should go in an OuterClassDirective, but I can't seem to figure out what the logic should look like?
Any advice would be appreciated.
Thanks!
What about this. Your outerClassDirective should have a controller, which will store used values in an array. It will transclude the input fields in its body. Your toValidate directive requires outerClassDirective and adds the model value to the array, making it invalid if exists.
Here is a try (untested):
app.directive('outerClass', function() {
var values = [];
var valid = true;
return {
template: '<div ng-transclude></div>',
transclude: true,
replace: true,
require: 'ngModel',
controller: function() {
this.addValue: function(value) {
valid = values.indexOf(value) > -1;
values.push(value);
};
},
link: function(scope, elm, attrs, ctrl) {
ctrl.$setValidity('toValidate', valid)
}
};
});
app.directive('toValidate', function() {
return {
require: '^outerClass',
link: function(scope, elm, attrs, ctrl) {
ctrl.addValue(attrs.value);
}
}
};
});
The 'tabs' and 'pane' directives on the Angular home page solve a similar issue -- the child 'pane' directives need to communicate with the parent 'tabs' directive.
Define a controller on the outerclass directive, and then define a method on the controller (use this not $scope). Then require: '^outerclass' in the toValidate directive. In the toValidate link function, you can $watch for value changes and call the method on the outerclass controller to pass the value up. Do the validation in the outerclass directive.
See also 'this' vs $scope in AngularJS controllers.

Mootools stop form submit method

I don't want to use an <input type=submit /> button to submit a form and I am instead using an <a> element. This is due to styling requirements. So I have this code:
myButton.addEvent('click', function() {
document.id('myForm').submit();
});
However, I have also written a class that improves and implements the placeholder attribute on inputs and textareas:
var FDPlaceholderText = new Class({
Implements: Events,
initialize: function() {
var _self = this;
var forms = document.getElements('form');
forms.each(function(form) { // All forms
var performInit = false;
var i = 0;
var ph = [];
form.getElements('input, textarea').each(function(el) { // Get form inputs and textareas
if (el.getProperty('placeholder') != null) { // Check for placeholder attribute
performInit = true;
ph[i] = _self.initPlaceholder(el); // Assign the placeholder replacement to the elements
}
i ++;
});
if (performInit) {
_self.clearOnSubmit(form, ph);
}
});
},
clearOnSubmit: function(form, ph) {
form.addEvent('submit', function(e) {
ph.each(function(el) {
if (el.value == el.defaultValue) {
el.value = '';
}
});
});
},
initPlaceholder: function(el) {
el.defaultValue = el.getProperty('placeholder');
el.value = el.getProperty('placeholder');
el.addEvents({
'focus': function() {
if (el.value == el.defaultValue) el.value = '';
},
'blur': function() {
if(el.value.clean() == ''){
el.value = el.defaultValue;
}
}
});
return el;
}
});
window.addEvent('domready', function() {
new FDPlaceholderText();
});
The above class works great if a form is submitted using an actual <input type=submit /> button: it listens for a submit and clears the inputs values if they are still the default ones therefore validating that they are essentially empty.
However, it seems that because I am submitting one of my forms by listening to a click event on an <a> tag the form.addEvent('submit', function(e) { isn't getting fired.
Any help is appreciated.
well you can change the click handler to fireEvent() instead of call the .submit() directly:
myButton.addEvent('click', function() {
document.id('myForm').fireEvent('submit');
});
keep in mind a couple of things (or more).
placeholder values to elements that lack placeholder= attribute is pointless
if you detect placeholder support, do so once and not on every element, it won't change suddenly midway through the loop. you can go something like var supportsPlaceholder = !!('placeholder' in document.createElement('input')); - remember, there is no need to do anything if the browser supports it and currently, near enough 60% do.
you can otherwise do !supportsPlaceholder && el.get('placeholder') && self.initPlaceholder(el); - which avoids checking attributes when no need
when the form is being submitted you really need to clear placeholder= values in older browser or validation for 'required' etc will fail. if validation still fails, you have to reinstate the placeholder, so you need a more flexible event pattern
avoid using direct references to object properties like el.value - use the accessors like el.get('value') instead (for 1.12 it's getProperty)
for more complex examples of how to deal with this in mootools, see my repo here: https://github.com/DimitarChristoff/mooPlaceholder
This is because the submit() method is not from MooTools but a native one.
Maybe you can use a <button type="submit"> for your styling requirements instead.