approach for validated form controls in AngularJS - forms

My teammates and I are learning AngularJS, and are currently trying to do some simple form field validation. We realize there are many ways to do this, and we have tried
putting input through validation filters
using a combination of controller and validating service/factory
a validation directive on the input element
a directive comprising the label, input and error output elements
To me, the directive approach seems the most "correct". With #3, we ran into the issue of having to communicate the validation result to the error element (a span sibling). It's simple enough to do some scope juggling, but it seemed "more correct" to put the span in the directive, too, and bundle the whole form control. We ran into a couple of issue, and I would like the StackOverflow community's input on our solution and/or to clarify any misunderstandings.
var PATTERN_NAME = /^[- A-Za-z]{1,30}$/;
module.directive("inputName", [
function () {
return {
restrict: "E",
require: "ngModel",
scope: {
fieldName: "#",
modelName: "=",
labelName: "#",
focus: "#"
},
template: '<div>' +
'<label for="{{fieldName}}">{{labelName}}</label>' +
'<input type="text" ng-model="modelName" id="{{fieldName}}" name="{{fieldName}}" placeholder="{{labelName}}" x-blur="validateName()" ng-change="validateName()" required>' +
'<span class="inputError" ng-show="errorCode">{{ errorCode | errorMsgFltr }}</span>' +
'</div>',
link: function (scope, elem, attrs, ngModel)
{
var errorCode = "";
if (scope.focus == 'yes') {
// set focus
}
scope.validateName = function () {
if (scope.modelName == undefined || scope.modelName == "" || scope.modelName == null) {
scope.errorCode = 10000;
ngModel.$setValidity("name", false);
} else if (! PATTERN_NAME.test(scope.modelName)) {
scope.errorCode = 10001;
ngModel.$setValidity("name", false);
} else {
scope.errorCode = "";
ngModel.$setValidity("name", true);
}
};
}
};
}
]);
used as
<form novalidate name="addUser">
<x-input-name
label-name="First Name"
field-name="firstName"
ng-model="firstName"
focus="yes"
model-name="user.firstName">
</x-input-name>
<x-input-name
label-name="Last Name"
field-name="lastName"
ng-model="lastName"
model-name="user.lastName">
</x-input-name>
...
</form>
First, because both form and input are overridden by AngularJS directives, we needed access to the ngModel API (ngModelController) to allow the now-nested input to be able to communicate validity to the parent FormController. Thus, we had to require: "ngModel", which becomes the ngModel option to the link function.
Secondly, even though fieldName and ngModel are given the same value, we had to use them separately. The one-way-bound (1WB) fieldName is used as an attribute value. We found that we couldn't use the curly braces in an ngModel directive. Further, we couldn't use a 1WB input with ngModel and we couldn't use a two-way-bound (2WB) input with values that should be static. If we use a single, 2WB input, the model works, but attributes like id and name become the values given to the form control.
Finally, because we are sometimes reusing the directive in the same form (e.g., first name and last name), we had to make attributes like focus parameters to be passed in.
Personally, I would also like to see the onblur and onchange events bound using JavaScript in the link function, but I'm not sure how to access the template markup from within link, especially outside/ignorant of the larger DOM.

Related

Customize error message from Abide in foundation 6

I have a simple sign up form with one input tag that is set up for an email. I'm using abide to validate the email. That works, but I would like to create my own error message and to style elements on the page of my choosing. Is this possible?
I found this which I know isn't validating an email input ( I can't seem to find the code that abide is using to validate emails)
abide: {
validators: {
myCustomValidator: function (el, required, parent) {
if (el.value.length <= 3) {
document.getElementById('nameError').innerText = "Name must have more than 3 characters";
return false;
} //other rules can go here
return true;
}
}
I would assume if I am able to set up a custom validator that just mimics what abidie already does (email validation) then I could change all the elements on the page that I wanted to when the validation comes back false.
I'm not sure what exactly you're asking for but checkout the docs http://foundation.zurb.com/sites/docs/abide.html
So it is a little different now in Foundation 6. Once you initiate Foundation, you will have to overwrite certain properties in the Foundation.Abide object. Like this:
Foundation.Abide.defaults.patterns['dashes_only'] = /^[0-9-]*$/;
Foundation.Abide.defaults.validators['myCustomValidator'] = function($el,required,parent) {
if ($el.value.length <= 3) {
document.getElementById('nameError').innerText = "Name must have more than 3 characters";
return false;
} //other rules can go here
return true;
};
Then in your markup in your form would something like this:
<input id="phone" type="text" pattern="dashes_only" required ><span class="form-error">Yo, you had better fill this out.</span>
<input id="max" type="number" data-validator="myCustomValidator" required><span class="form-error">Yo, you had better fill this out.</span>
You can customize the error messages in the span tags.

How to add conditional elements in data-sly-list?

I currently have a data-sly-list that populates a JS array like this:
var infoWindowContent = [
<div data-sly-use.ed="Foo"
data-sly-list="${ed.allassets}"
data-sly-unwrap>
['<div class="info_content">' +
'<h3>${item.assettitle # context='unsafe'}</h3> ' +
'<p>${item.assettext # context='unsafe'} </p>' + '</div>'],
</div>
];
I need to add some logic into this array. If the assetFormat property is 'text/html' only then I want to print the <p> tag. If the assetFormat property is image/png then I want to print img tag.
I'm aiming for something like this. Is this possible to achieve?
var infoWindowContent = [
<div data-sly-use.ed="Foo"
data-sly-list="${ed.allassets}"
data-sly-unwrap>
['<div class="info_content">' +
'<h3>${item.assettitle # context='unsafe'}</h3> ' +
if (assetFormat == "image/png")
'<img src="${item.assetImgLink}</img>'
else if (assetFormat == "text/html")
'<p>${item.assettext # context='unsafe'}</p>'
+ '</div>'],
</div>
];
To answer your question quickly, yes you can have a condition (with data-sly-test) in your list as follows:
<div data-sly-list="${ed.allAssets}">
<h3>${item.assettitle # context='html'}</h3>
<img data-sly-test="${item.assetFormat == 'image/png'}" src="${item.assetImgLink}"/>
<p data-sly-test="${item.assetFormat == 'text/html'}">${item. assetText # context='html'}"</p>
</div>
But looking at what you're attempting to do, basically rendering that on the client-side rather than on the server, let me get a step back to find a better solution than using Sightly to generate JS code.
A few rules of thumb for writing good Sightly templates:
Try not to mix HTML, JS and CSS in the template: Sightly is on
purpose limited to HTML and therefore very poor to output JS or CSS.
The logic for generating a JS object should therefore be done in the
Use-API, by using some convenience APIs that are made fore that, like
JSONWriter.
Also avoid as much as possible any #context='unsafe', unless you filter that string somehow yourself. Each string that is not
escaped or filtered could be used in an XSS attack. This
is the case even if only AEM authors could have entered that string,
because they can be victim of an attack too. To be secure, a system
shouldn't hope for none of their users to get hacked. If you want to allow some HTML, use #context='html' instead.
A good way to pass information to JS is usually to use a data attribute.
<div class="info-window"
data-sly-use.foo="Foo"
data-content="${foo.jsonContent}"></div>
For the markup that was in your JS, I'd rather move that to the client-side JS, so that the corresponding Foo.java logic only builds the JSON content, without any markup inside.
package apps.MYSITE.components.MYCOMPONENT;
import com.adobe.cq.sightly.WCMUsePojo;
import org.apache.sling.commons.json.io.JSONStringer;
import com.adobe.granite.xss.XSSAPI;
public class Foo extends WCMUsePojo {
private JSONStringer content;
#Override
public void activate() throws Exception {
XSSAPI xssAPI = getSlingScriptHelper().getService(XSSAPI.class);
content = new JSONStringer();
content.array();
// Your code here to iterate over all assets
for (int i = 1; i <= 3; i++) {
content
.object()
.key("title")
// Your code here to get the title - notice the filterHTML that protects from harmful HTML
.value(xssAPI.filterHTML("title <span>" + i + "</span>"));
// Your code here to detect the media type
if ("text/html".equals("image/png")) {
content
.key("img")
// Your code here to get the asset URL - notice the getValidHref that protects from harmful URLs
.value(xssAPI.getValidHref("/content/dam/geometrixx/icons/diamond.png?i=" + i));
} else {
content
.key("text")
// Your code here to get the text - notice the filterHTML that protects from harmful HTML
.value(xssAPI.filterHTML("text <span>" + i + "</span>"));
}
content.endObject();
}
content.endArray();
}
public String getJsonContent() {
return content.toString();
}
}
A client-side JS located in a corresponding client library would then pick-up the data attribute and write the corresponding markup. Obviously, avoid inlining that JS into the HTML, or we'd be mixing again things that should be kept separated.
jQuery(function($) {
$('.info-window').each(function () {
var infoWindow = $(this);
var infoWindowHtml = '';
$.each(infoWindow.data('content'), function(i, content) {
infoWindowHtml += '<div class="info_content">';
infoWindowHtml += '<h3>' + content.title + '</h3>';
if (content.img) {
infoWindowHtml += '<img alt="' + content.img + '">';
}
if (content.text) {
infoWindowHtml += '<p>' + content.title + '</p>';
}
infoWindowHtml += '</div>';
});
infoWindow.html(infoWindowHtml);
});
});
That way, we moved the full logic of that info window to the client-side, and if it became more complex, we could use some client-side template system, like Handlebars. The server Java code needs to know nothing of the markup and simply outputs the required JSON data, and the Sightly template takes care of outputting the server-side rendered markup only.
Looking the at the example here, I would put this logic inside a JS USe-api to populate this Array.

Is it possible to add angularized html tags and attributes in a directive based on model data in Angularjs?

I want to build a directive that will build form inputs based on a nested object of settings that include the input type, model to bind, and html attributes. I have been pounding my head and, perhaps, am close to concluding that this is something Angular is not equipped to do. I would like to build a directive that could take an array of objects like:
[{
"label":"When did it happen",
"model": $scope.event.date,
"element":"input",
"type": "date",
"options":{
"class":"big",
"required": true,
},
},{
"label":"How did it happen?",
"model": $scope.event.cause,
"element":"textarea",
"options":{
"cols":45,
"rows":55,
},
},{
"label":"How many times did it happen?",
"model": $scope.event.times,
"element":"input",
"options":{},
}],
I have struggled through many different aspects of directives. I keep running across a few issues.
Neither the template nor the controller directive functions have access to any sort of scope that could reach any sort of data--most especially my array I've made. This means that I can't decide how to build my DOM (i.e. tag types and attributes) until later.
All compiling is done before the linking function. I am able to manipulate the DOM in the linking function but none of it is angularized. This means if I add a required attribute angular's ngValidate is not aware of it. If I try and change the tag type, I reset lose my model binding etc.
It seems that this is just the way angular runs. Is there no good way to affect the DOM tag types and attributes based on model data without specifying everything out specifically?
How about something like this:
$scope.event = {
date: new Date(),
cause:'It was the colonel in the kitchen with the revolver',
time:5, //...
$metadata: data
};
Where data here is simply the array you have already shown. (apart from the model property which would just be a string representing the property of the event, like so:
{
"label":"When did it happen",
"model":'date',
"element":"input",
"type": "date",
"options":{
"class":"big",
"required": true,
}
}
Then your directive would simply access the property given it on the parent scope. (event) And process the metadata into a usable template. Which could then get compiled.
Here is such a directive, and the directive...
myApp.directive('contentForm',function($compile,$interpolate){
var template = "<span class='lbl'>{{label}}</span><{{element ||'input'}} {{options}}"+
" ng-model='{{root+'.'+model}}'></{{element}}>";
function linkerFunction(scope,element,attrs){
if(!scope[attrs.contentForm])return;
var metadata = scope[attrs.contentForm].$metadata || false;
if(!metadata)return;
element.html(getHtml(metadata,attrs.contentForm));
$compile(element.contents())(scope);
}
return {
restrict: 'A',
replace: true,
link:linkerFunction
};
//interpolate the template with each set of metadata
function getHtml(metadata,root){
var interpolation = $interpolate(template);
var html = '';
if(angular.isArray(metadata)){
for(var i = 0; i < metadata.length; i++){
metadata[i].root = root;
metadata[i].options = processOptions(metadata[i].options);
html += interpolation(metadata[i]) + '</br>'
}
}else{
html = interpolation(metadata);
metadata.options = processOptions(metadata.options);
}
return html;
}
// parse object into html attributes
function processOptions(options){
var result = '';
for(var key in options){
if(options.hasOwnProperty(key)){
result += ' '+key+"='"+options[key]+"'"
}
}
return result.trim();
}
});
You'd probably want to change the template. I seem to have forgotten to put in a type. But that should be fairly straight forward. I hope this helps!

How to serialize html form in dart as a string for submission

In jQuery, there is a function to serialize a form element so for example I can submit it as an ajax request.
Let's say we have a form such as this:
<form id="form">
<select name="single">
<option>Single</option>
<option selected="selected">Single2</option>
</select>
<input type="checkbox" name="check" value="check1" id="ch1">
<input name="otherName" value="textValue" type="text">
</form>
If I do this with the help of jquery
var str = $( "form" ).serialize();
console.log(str);
the result would be
single=Single2&check=check1&otherName=textValue
Is there such functionality in dart's FormElement or I have to code it myself? Thanks.
I came up with my own simple solution that might not work in all cases (but for me it is workikng). The procedure is this:
First we need to extract all input or select element names and values from the form into Dart's Map, so the element name will be the key and value the value (e.g. {'single': 'Single2'}).
Then we will loop through this Map and manually create the resulting string.
The code might look something like this:
FormElement form = querySelector('#my-form'); // To select the form
Map data = {};
// Form elements to extract {name: value} from
final formElementSelectors = "select, input";
form.querySelectorAll(formElementSelectors).forEach((SelectElement el) {
data[el.name] = el.value;
});
var parameters = "";
for (var key in data.keys) {
if (parameters.isNotEmpty) {
parameters += "&";
}
parameters += '$key=${data[key]}';
}
Parameters should now contain all the {name: value} pairs from the specified form.
I haven't seen anything like that yet.
In this example Seth Ladd uses Polymers template to assign the form field values to a class which get's serialized.

How to use zend Filter to filter particular string from a text box, in zend

I am using zend Form for creating the form.
Now i need to filter a particular value coming from the text boxes.
How can i use zend_filter or any other method for this purpose
For example;
I have text box called 'name' ,with default value 'First Name'.
After form submit, i need to get the value for 'name' field, so that it don't contain 'First Name' string
As you are saying in the question, that you want to filter the text "First Name".
Why are you put that text default? If you want to show that in the textbox then you can use the Placeholder. Here is the full implementation of it.
It will surly solve your problem.
Here is the example.
Here is the mootool code, mootool is the library of JS:
var firstNameBox = $('first_name'),
message = firstNameBox.get('placeholder');
firstNameBox.addEvents({
focus: function() {
if(firstNameBox.value == message) { searchBox.value = ''; }
},
blur: function() {
if(firstNameBox.value == '') { searchBox.value = message; }
}
});