LifeRay creating a new type of aui validator - liferay

I want to create a new type of aui validator.
For example :
<aui:input name="firstName" type="text" maxlength="40">
<aui:validator name="required" />
<aui:validator name="alpha" />
</aui:input>
the alpha validator does not accept the space character, i want to use a type that accepts alpha characters plus the space character, i have already a solution using javascript to use a custom validator, but i want to define a new validator type to use like the others by invoking the validator tag e.g :
<aui:validator name="myValidator" />
Is that possible ?! and how can i do it ;)

Try your luck on it :)
<aui:input name="firstName" type="text">
<aui:validator name="myValidator" errorMessage="numbers-not-allowed">
function(val, fieldNode, ruleValue){
var matches = val.match(/\d+/g);
if(matches != null)
return false;
else
return true;
}
</aui:validator>
</aui:input>

As an alternative to Parkash answer, you can make a separate jspf file and write your validations like this:
<aui:script use="liferay-form">
var ns = '<portlet:namespace/>';
window.onload = function() {
var form = Liferay.Form.get(ns+'form_add_user');
form.set(
'fieldRules',
[
{
fieldName: ns+'middlename',
validatorName: 'custom_middlename',
custom: true,
errorMessage: 'Only text and spaces here!',
body: function(value, field, rule)
{
return /^[a-zA-Z\s\.\u00E0-\u00FC]+$/.test(value);
}
},
...
]
);
}
</aui:script>
Found in Liferay 7.0 documentation

Related

Populate and edit seleted country code and flag on details page after creation using jQuery intl-tel-input plugin

I am using intl-tel-input jQuery plugin for phone number fields on new entity creation. Now I have a details form where I can view all entered details for newly created entity. How do I populate selected country code and flag on phone fields there? Also I should be allowed to edit my details on that page(this means we can edit our phone number also). I am using JSF+Primefaces for frontend.
Form where phone input is taken:
<input id="adminPhone" name="adminPhone" type="tel" />
<h:outputScript>
$("#adminPhone").intlTelInput({
initialCountry: "auto",
geoIpLookup: function(callback) {
$.get("http://ipinfo.io", function() {}, "jsonp").always(function(resp)
{
var countryCode = (resp && resp.country) ? resp.country : "";
callback(countryCode);
});
},
separateDialCode: true,
utilsScript: "../resources/js/utils.js"
});
</h:outputScript>
Updated backing bean adminPhone property using getRequestParameterMap().get("adminPhone")value
Update/View details form as of now:
<p:outputLabel id="engagementbillingPhoneLabel" value="Phone:" for="adminPhone" />
<p:inputText id="adminPhone" value="#{dashboard.selectedEntity.adminPhone}"/>
I used two fields to capture and store the country code and admin phone number on entity creation using getRequestParameterMap() in backing bean.
<input id="adminPhone" name="adminPhone" type="tel" />
<input id="adminCountryCode" name="adminCountryCode" type="hidden" />
Backing bean
Map<String, String> requestParams = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
this.selectedEntity.setAdminCountryCode(requestParams.get("adminCountryCode"));
this.selectedEntity.setAdminPhone(requestParams.get("adminPhone"));
Used above stored values to populate it on View details/Edit page using jQuery built-in options provided in plugin (initialCountry and entered phone number).
<input id="adminPhone" name="adminPhone" type="tel"/>
<input id="adminCountryCode" name="adminCountryCode" type="hidden" />
<h:outputScript>
var adminCodeHidden = $("#adminCountryCode"),
adminTelInput = $("#adminPhone");
adminTelInput.attr("value", "#{dashboard.selectedEntity.adminPhone}");
adminTelInput.intlTelInput({
initialCountry: "#{dashboard.selectedEntity.adminCountryCode}",
geoIpLookup: function(callback) {
$.get("http://ipinfo.io", function() {}, "jsonp").always(function(resp) {
var countryCode = (resp && resp.country) ? resp.country : "";
callback(countryCode);
});
},
separateDialCode: true,
utilsScript: "../resources/js/utils.js"
});
</h:outputScript>

Upload a file with ServiceStack ss-utils.js

I have a job application form as part of a site built on ServiceStack. I am attempting to use the included ss-utils.js to leverage built-in Fluent Validation, but my form doesn't post the user's file upload. Here's the relevant snippet of the form:
<form id="form-careerapplication" action="#(new CreateCareerApplication().ToPostUrl())" method="post">
<div class="error-summary"></div>
<div class="form-group">
<label>
Upload Resume
</label>
<input type="file" id="Resume" name="Resume" />
<span class="help-block"></span>
</div>
<input type="hidden" name="Id" value="#Model.Id" />
<input type="submit" value="Submit Application" />
</form>
<div id="success" class="hidden">
Thank you for your application.
</div>
$("#form-careerapplication").bindForm({
success: function (careerApplicationResponse) {
$("#form-careerapplication").addClass("hidden");
$("#success").removeClass("hidden");
},
...
Is there something I'm missing in ss-utils.js? Or is there a way of overriding / supplementing the submit behavior to use FormData?
Uploading files via a HTML FORM requires a enctype="multipart/form-data", e.g:
<form id="form-careerapplication" action="#(new CreateCareerApplication().ToPostUrl())"
method="post" enctype="multipart/form-data">
...
</form>
If you want to change support multiple file uploads or change the appearance of the UI Form I recommend the Fine Uploader, there's an example showing how to use Fine Uploader on the HTTP Benchmarks Example.
Whilst Imgur has a simple client HTML and Server example.
Turned out I can use the beforeSend option as part of the configuration passed into bindForm to override the data being sent. Its a bit of a hack, but it worked and I keep the original ss-utils.js fluent validation!
$("#form-careerapplication").bindForm({
success: function (careerApplicationResponse) {
....
},
error: function (error) {
....
},
contentType: false,
processData: false,
beforeSend: function (x, settings) {
var fd = new FormData();
// Tweaked library from https://github.com/kflorence/jquery-deserialize/blob/master/src/jquery.deserialize.js
// Used to translate the serialized form data back into
// key-value pairs acceptable by `FormData`
var data = $.fn.deserialize(settings.data);
$.each(data, function (i, item) {
fd.append(item.name, item.value);
});
var files = $('#form-careerapplication').find("input:file");
$.each(files, function (i, file) {
fd.append('file', file.files[0], file.files[0].name);
});
settings.data = fd;
}
});

Unobtrusive validation with Jquery Steps Wizard

Recently I asked a question for how to customize the JQuery Steps as I wanted to use partial views instead of static content. I have partially solved that problem by using the following code supported by jquery-steps,
<h3>Step 1</h3>
<section data-mode="async" data-url="/Formation/RenderStep1"></section>
<h3>Step 2</h3>
<section data-mode="async" data-url="/Formation/RenderStep2"></section>
Now the big problem I am facing right now is how to use unobtrusive validation. I don't want to use JQuery custom validation and there must be some way of using Obtrusive with it.
Each partial view that is rendered has its own form. I want to validate the form in the onStepChanging function of jquery-steps,
$("#my-steps").steps({
headerTag: "h3",
bodyTag: "section",
contentMode: "async",
transitionEffect: "fade",
stepsOrientation: "vertical",
onStepChanging: function (event, currentIndex, newIndex) {
return true;
}
});
I have tried calling $.validator.unobtrusvie.parse('#myform'); in the onStepChanging function but ('#myform') is undefined and still I don't know that whether this is the right way to call the unobtrusive validation manually. Kindly guide me and show me the direction to achieve this. Any help will be highly appreciated.
It sounds like your trying manage multiple forms within the JQuery Steps library and I don't think that is what its intended for.
When you configure JQuery Steps, you set it up against the form in your view.
Unobtrusive JQuery Validation is looking at the model in your view and automatically configuring the HTML with the relevant data attributes for error handling.
This validation should be firing at the client side automatically.
There shouldn't be a problem with using Partial View's, as long as there encapsulated within the same form element.
What is the requirement to have each partial view wrapped in its own form? If your trying to make multiple posts throughout the JQuery Steps form wizard, your defeating the object.
At each step in the JQuery Steps form, your only validating the one form like this :-
onStepChanging: function (event, currentIndex, newIndex) {
//Allways allow user to move backwards.
if (currentIndex > newIndex) {
return true;
}
// Remove the validation errors from the next step, incase user has previously visited it.
var form = $(this);
if (currentIndex < newIndex) {
// remove error styles
$(".body:eq(" + newIndex + ") label.error", form).remove();
$(".body:eq(" + newIndex + ") .error", form).removeClass("error");
}
//disable validation on fields that are disabled or hidden.
form.validate().settings.ignore = ":disabled,:hidden";
return form.valid();
}
Once the user has finished entering data, and the client side validation has been met, you hook into the onFinished method and post the form :-
onFinished: function (event, currentIndex) {
var form = $(this);
form.submit();
}
The purpose of JQuery Steps is to allow the user to have a fluid experience of filling out a form and to not be overwhelmed with the number of questions been asked.
From the developers perspective, it enables us to split up the form into nice size-able chunks without having to worry about saving progress between screens or losing the state of the form data and allows us to capture all of the required data with only having to make that one post once all validation criteria has been met.
I tried the formvalidation plugin, it will relax your mind from searching in validation without form tag or validation without submit the form that's the issue I solved when I tried it.
I know it's not free but you can try it from here, personally I like it
First update height after validation
<style type="text/css">
/* Adjust the height of section */
#profileForm .content {
min-height: 100px;
}
#profileForm .content > .body {
width: 100%;
height: auto;
padding: 15px;
position: relative;
}
Second, add data-steps index to your section*
<form id="profileForm" method="post" class="form-horizontal">
<h2>Account</h2>
<section data-step="0">
<div class="form-group">
<label class="col-xs-3 control-label">Username</label>
<div class="col-xs-5">
<input type="text" class="form-control" name="username" />
</div>
</div>
<div class="form-group">
<label class="col-xs-3 control-label">Email</label>
<div class="col-xs-5">
<input type="text" class="form-control" name="email" />
</div>
</div>
<div class="form-group">
<label class="col-xs-3 control-label">Password</label>
<div class="col-xs-5">
<input type="password" class="form-control" name="password" />
</div>
</div>
<div class="form-group">
<label class="col-xs-3 control-label">Retype password</label>
<div class="col-xs-5">
<input type="password" class="form-control" name="confirmPassword" />
</div>
</div>
</section>
Third, javascript code
<script>
## // to adjust step height to fit frame after showing validation messages##
$(document).ready(function() {
function adjustIframeHeight() {
var $body = $('body'),
$iframe = $body.data('iframe.fv');
if ($iframe) {
// Adjust the height of iframe
$iframe.height($body.height());
}
}
// IMPORTANT: You must call .steps() before calling .formValidation()
$('#profileForm')
// setps setup
.steps({
headerTag: 'h2',
bodyTag: 'section',
onStepChanged: function(e, currentIndex, priorIndex) {
// You don't need to care about it
// It is for the specific demo
adjustIframeHeight();
},
// Triggered when clicking the Previous/Next buttons
// to apply validation to your section
onStepChanging: function(e, currentIndex, newIndex) {
var fv = $('#profileForm').data('formValidation'), // FormValidation instance
// The current step container
$container = $('#profileForm').find('section[data-step="' + currentIndex +'"]');
// Validate the container
fv.validateContainer($container);
var isValidStep = fv.isValidContainer($container);
if (isValidStep === false || isValidStep === null) {
// Do not jump to the next step
return false;
}
return true;
},
// Triggered when clicking the Finish button
onFinishing: function(e, currentIndex) {
var fv = $('#profileForm').data('formValidation'),
$container = $('#profileForm').find('section[data-step="' + currentIndex +'"]');
// Validate the last step container
fv.validateContainer($container);
var isValidStep = fv.isValidContainer($container);
if (isValidStep === false || isValidStep === null) {
return false;
}
return true;
},
onFinished: function(e, currentIndex) {
// Uncomment the following line to submit the form using the defaultSubmit() method
// $('#profileForm').formValidation('defaultSubmit');
// For testing purpose
$('#welcomeModal').modal();
}
})
.formValidation({
framework: 'bootstrap',
icon: {
valid: 'glyphicon glyphicon-ok',
invalid: 'glyphicon glyphicon-remove',
validating: 'glyphicon glyphicon-refresh'
},
// This option will not ignore invisible fields which belong to inactive panels
excluded: ':disabled',
fields: {
username: {
validators: {
notEmpty: {
// for asp.net i used element attribute to integerated with unobtrusive validation
// message :$('username').attr('data-val-required')
message: 'The username is required'
},
stringLength: {
min: 6,
max: 30,
message: 'The username must be more than 6 and less than 30 characters long'
},
regexp: {
regexp: /^[a-zA-Z0-9_\.]+$/,
message: 'The username can only consist of alphabetical, number, dot and underscore'
}
}
},
email: {
validators: {
notEmpty: {
message: 'The email address is required'
},
emailAddress: {
message: 'The input is not a valid email address'
}
}
},
password: {
validators: {
notEmpty: {
message: 'The password is required'
},
different: {
field: 'username',
message: 'The password cannot be the same as username'
}
}
},
confirmPassword: {
validators: {
notEmpty: {
message: 'The confirm password is required'
},
identical: {
field: 'password',
message: 'The confirm password must be the same as original one'
}
}
}
}
});

jQuery validation engine using the “ajax[selector]”

I have the following (multistep form) HTML:
<form id="myform">
<fieldset id="first_step">
<input type="text" name="register" placeholder="Αρ. Μητρώου" id="register" class="validate[required,custom[integer]]" />
<input type="text" name="email" placeholder="Πόλη" id="city" class="validate[required,ajax[email]]" />
<input type="button" name="next" class="next action-button" value="Επόμενο" id="next1" />
</fieldset>
<fieldset id="second_step">
....
....
....
</fieldset>
</form>
The instantiation js code plus logic (myform.js)
$(document).ready(function(){
var first_step = $("#first_step");
var second_step = $("#second_step");
$("#myform").validationEngine('attach', {
promptPosition : "centerRight",
scroll: false
});
$("#next1").on('click', function (e) {
e.preventDefault();
var valid = $("#myform").validationEngine('validate');
if (valid == true) {
second_step.show();
} else {
$("#ithemiscustomerform").validationEngine();
}
});
});
The rules “validationEngine-el.js”
(function($){
$.fn.validationEngineLanguage = function(){
};
$.validationEngineLanguage = {
newLang: function(){
$.validationEngineLanguage.allRules = {
"required": { // Add your regex rules here, you can take telephone as an example
"regex": "none",
"alertText": "* Υποχρεωτικό πεδίο",
"alertTextCheckboxMultiple": "* Παρακαλώ επιλέξτε",
"alertTextCheckboxe": "* Υποχρεωτικό πεδίο",
"alertTextDateRange": "* Και τα δύο πεδία ημ/νίας είναι υποχρεωτικά"
},
// --- CUSTOM RULES -- Those are specific to the demos, they can be removed or changed to your likings
"email": {
"url": "http://localhost/userformServer/validator.cfc?method=valEmail",
// you may want to pass extra data on the ajax call
//"extraData": "name=eric",
"alertText": "* Μη έγκυρο email",
"alertTextLoad": "* Παρακαλώ περιμένετε...",
"alertTextOk": "* Το eimail είναι διαθέσιμο"
},
"integer": {
"regex": /^[\-\+]?\d+$/,
"alertText": "* Μη έγκυρος ακέραιος"
}
};
}
};
$.validationEngineLanguage.newLang();
})(jQuery);
The problem
Remote validation works great for the “email” field “on blur” –while user completing the form. But when I click the “next1” button to move on the next fieldset (“second_step”) even if the email field is valid (remotely valid) the form doesn’t move to the next fieldset (see “myform.js” onclick). Specifically when I call “$("#myform").validationEngine('validate');” inside the click event it returns false. I think this occurs due the asynchronous nature of validation. Is there any workaround for this?

Date is not showing up properly in Liferay portlet

In my portlet I have one form where I am showing dates through following code
JSP:
<aui:input type="text" name="createdDate" size="10" value="" id="createdDate" label="" />
<div class="calendar-icon" id="imageDiv">
<span class="aui-icon aui-icon-calendar"></span>
</div>
Script in JSP
renderCalendar('#imageDiv','#<portlet:namespace/>createdDate','#calendarDiv');
function renderCalendar(imageDiv,inputDisplay,calendarDiv) {
AUI().ready('aui-calendar', function(A) {
var inputField1 = A.one(imageDiv);
var inputField2 = A.one(inputDisplay);
var calendar1 = new A.Calendar({
dates: [ new Date() ],
dateFormat: '%d/%m/%Y',
selectMultipleDates: false,
after: {
datesChange: function(event) {
var formatted = event.target.getFormattedSelectedDates();
inputField2.val(formatted);
calendar1.toggle(); // hide after a date was selected
}
}
}).render(calendarDiv);
var boundingBoxCal1 = calendar1.get('boundingBox');
boundingBoxCal1.setX(inputField1.getX());
boundingBoxCal1.setY(inputField1.getY() + 25);
calendar1.hide();
inputField1.on('click', function() { calendar1.toggle(); });
});
}
It is showing date on page but the problem is with layout Date text field and calender icon both not coming in same line.Please help me out
use <span> instead of <div>. <div> opens a new block while <span> will remain in the same line
Strange that <span> didn't fix your problem. Try floating your <div> to the left, as in,
<div class="calendar-icon" id="imageDiv" style="float: left;">

Resources