I was looking for form validation in CanJS and came to know about can.Map.Validate plugin http://canjs.com/docs/can.Map.validations.prototype.errors.html
In the below example taken from the documentation itself, we need to give the properties of the object like 'dueDate' before the creating of the object itself
Task = can.Map.extend({
init : function(){
this.validatePresenceOf("dueDate")
}
},{});
var task = new Task(),
errors = task.errors()
Now my requirement is I want to build a Map which checks whether my field attribute is empty and returns me a suitable error. But the problem is, there are multiple forms on my page and formObject gets generated dynamically. Now in the above scenario before the creation of object itself, you need to allot the property in init method on which validation needs to be performed. It is not good for my requirement as the object is getting build and properties are unknown before the creation of the object. I searched over the web a lot but unable to crack it.
Providing the code for better understanding
EJS File: login.ejs
<form id="registrationForm" name="registrationForm" method="POST">
<input type="text" name="userName" placeholder="UserName" class="inputFieldStyle"></input>
<input type="password" name="password" placeholder="Password" class="inputFieldStyle"></input>
<input type="password" name="confirmPassword" placeholder="Confirm Password" class="inputFieldStyle"></input>
<input type="email" name="email" placeholder="Email" class="inputFieldStyle"></input>
<input type="button" name="registrationSubmit" value="Register" class="registrationLoginButton"></input>
<input type="hidden" name="formType" value="registration"></input>
</form>
<form id="loginForm" name="loginForm" method="POST">
<input type="userName" name="userName" placeholder="UserName or Email" class="inputFieldStyle"></input>
<input type="password" name="password" placeholder="Password" class="inputFieldStyle"></input>
<input type="button" name="loginSubmit" value="Login" class="registrationLoginButton"></input>
<input type="hidden" name="formType" value="login"></input>
</form>
Controller:
var LoginController=can.Control({
defaults: {view:'login.ejs'}
},
{
init:function(element,options)
{
var fragment=can.view(this.options.view);
this.element.html(fragment)
},
'input[name="registrationSubmit"],input[name="loginSubmit"] click':function(el,ev)
{
ev.preventDefault();
var formDOMObject=el.parent('form');
var formValueObject=can.deparam(formDOMObject.serialize());
/*Need to validate formValueObject. Problem is either formDOMObject can be resgitrationForm or LoginForm. Now both have different properties, So i cannot provide predefined properties in init method */
}
});
Is there any way of validating the dynamic object properties using ca.Map.Validate plugin?
How can I access the instance object passed inside the init method?
Thanks in advance :)
Seems like what you want is to make temporary, throwaway classes with validations that are built from an existing object, which can then be observed for errors.
Here's an example showing that the validations are observable and change dynamically with the source can.Map
can.Map("DynamicFormValidator", {
newInstance : function(data) {
var cls = this.extend();
can.each(data.serialize(), function(val, key) {
cls.validatePresenceOf(key);
});
//cls.prototypes.data = data;
return can.Map.newInstance.call(cls, data);
}
}, {
init : function(data) {
var that = this;
this.attr("data", data);
this.bind("change", function(ev, attr, how, newVal) {
if(attr.indexOf('data.') === 0) {
that.attr(attr.substr(5), newVal);
}
})
}
, computed_errors : can.compute(function() { return this.errors(); })
});
See it in action at
http://jsfiddle.net/air_hadoken/8rK2n/2/
You need to make ,can.map (or compute) for the state and mustache helpers to reflect the state in the dom for this I use an approach from sebastion proto http://sporto.github.io/blog/2014/03/04/form-validations-with-canjs/
and here is the jsfiddle
can.fixture({
"GET contacts":function(){
return [{'fname':'Cherif','lname':'BOUCHELAGHEM'}]
}
})
can.Model.extend('Contact',{
init:function(){
this.validatePresenceOf(['fname','lname']);
},
findAll: 'GET contacts'
},{});
can.Control.extend('ContactForm',{
init:function(el,options){
this.contact=new Contact();
this.errors=new can.Map({});
this.element.html(can.view('validation',contact:this.contact, errors:this.errors}));
},
'form submit': function () {
var errors = this.contact.errors();
this.errors.attr(errors, true);
return false;
}
});
Mustache.registerHelper('showErrors', function (errors, prop) {
var attr = errors.attr(prop);
if (attr) {
return prop + ' ' + attr[0];
} else {
return '';
}
});
new ContactForm('#val');
Related
I am trying to change the default error message "setCustomValidity" throws on email being invalid.
I cannot access the source code. My assumption is that the source somehow invokes setCustomValidity; just because of the look of the error message. This is the source element:
<input type="email" value="" name="customer[email]" id="email" class="large" size="30">
I can only inject any change using external JavaScript/css file.
I could think of two solutions.
Solution 1: I am trying to inject inline HTML element using JS which would result in something like this.
<input type="email" value="" name="customer[email]" class="large" size="30" oninvalid="this.setCustomValidity('Please Enter valid email')" oninput="setCustomValidity('')">
I am new to JS and I am having a hard time figuring how to implement HTML in an external JS file.
Solution 2: Invoke the oninvalid and setCustomValidity DOM methods in error_message.js like so:
function emailValidity(){
var myInput = document.getElementByName("customer[email]");
myInput.oninvalid = function() {
(errorMessage)
};
function errorMessage(){
myInput.setCustomValidity("hey! the email isn't right");
}
}
But this file even after being included somehow fails to override the default message!
Any help is appreciated. Thank you in advance.
Additionally you must call the reportValidity method on the same element or nothing will happen.
HTMLObjectElement.setCustomValidity
function validate(input) {
var validityState_object = input.validity;
console.log(validityState_object)
if (validityState_object.typeMismatch) {
input.setCustomValidity('Thats not an email!');
input.reportValidity();
} else {
input.setCustomValidity('');
input.reportValidity();
}
}
document.querySelector('#email').addEventListener('blur', e =>
validate(e.target)
)
<input type="email" value="" id="email">
Just set the input and invalid event listeners in much the same way.
const input = document.querySelector('input');
input.oninput = () => input.setCustomValidity('');
input.oninvalid = () => input.setCustomValidity('Please Enter valid email');
<form>
<input type="email" value="" name="customer[email]" class="large" size="30">
</form>
Your code wasn't working because it wasn't in a form. A form needs a submit button to check the inputs and display an error message if necessary, so I added that.
Also, you have getElementByName when it should be getElementsByName, with a later on [0] indexing to select the element.
In addition to that, you were trying to set the validity every time the user tried to submit, when it only needed to be set once.
Try this:
var myInput = document.getElementsByName("customer[email]")[0];
myInput.oninvalid = function() {
myInput.setCustomValidity("Hey! the email isn't right")
};
<form>
<input type="email" name="customer[email]" id="email" class="large" size="30">
<input type="submit" onsubmit="emailValidity()">
</form>
This is how I solved it:
customAnswer.js
document.addEventListener("DOMContentLoaded", function(event) {
// Source text: "Fill out this field."
var validateElements1 = document.querySelectorAll('input#username, input#password');
if (validateElements1.length > 0) {
for (var x = 0; x < validateElements1.length; x++) {
validateElements1[x].setAttribute("oninvalid","this.setCustomValidity('Complete este campo.')");
validateElements1[x].setAttribute("oninput","this.setCustomValidity('')");
}
}
});
I have form in which i am submitting multiple inputs containing name attributes as array like name="array[key]"
<form onsubmit="callback($(this));">
<input type="text" name="stock[quantity]">
<input type="text" name="stock[old]">
<input type="text" name="form[mrp]">
<input type="text" name="form[price]">
</form>
I have tried new formData($("form")[0]) and jQuery $("form").serializeArray() both returning name="array[key]" as string.
I want this data as multidimensional object like we got this in php when submit this form like.
<script>
function callback($form){
/*
here i want form data in js object like
{
stock : {quantity : ... , old : ....},
form : {mrp : ... , price : ....}
}
or something like this
*/
}
</script>
I am using JS, jQuery, and Vue.js in this project, actually i want to put this form data to indexedDB after successful save on server. i have different tables/objectStore for stock and form
Try this..
function callback($form) {
let result = {};
let formData = $form.serializeArray();
formData.forEach(obj => {
let inputValue = obj.name.split('[');
if (!result[inputValue[0]]) {
result[inputValue[0]] = {};
}
let innerText = inputValue[1].replace(']','');
result[inputValue[0]][innerText] = obj.value;
});
console.log(result);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form onsubmit="callback($(this)); return false;">
<input type="text" name="stock[quantity]">
<input type="text" name="stock[old]">
<input type="text" name="form[mrp]">
<input type="text" name="form[price]">
<input type="submit" value="submit">
</form>
I am trying to pass value like this from view to controller in angular js of this form. I do not wish to hardcode it in this way. How could it be done in proper manner?
angular.module('user').controller('UsersController', ['$scope', '$stateParams', 'Users',
function($scope, $stateParams, Orders) {
$scope.create = function() {
var user = new Users({
child: [
{ columnA: child[0].columnA, columnB: child[0].columnB, columnC: child[0].columnC },
{ columnB: child[1].columnA, columnB: child[1].columnB, columnC: child[1].columnC },
...
{ columnC: child[10].columnA, columnB: child[10].columnB, columnC: child[10].columnC }
]
});
}
}
});
<form data-ng-submit="create()">
<input type="text" data-ng-model="child[0].columnA">
<input type="text" data-ng-model="child[0].columnB">
<input type="text" data-ng-model="child[0].columnC">
<input type="text" data-ng-model="child[1].columnA">
<input type="text" data-ng-model="child[1].columnB">
<input type="text" data-ng-model="child[1].columnC">
......
<input type="text" data-ng-model="child[10].columnA">
<input type="text" data-ng-model="child[10].columnB">
<input type="text" data-ng-model="child[10].columnC">
</form>
It would be better if an reusable directive that may perform above function.
$scope.create = function() {
child: toJSON(child);
}
function toJSON(var a) {
//automatically search through the view for ng-model with child[index].columnName and change to the form above.
}
I wrote out a plunker that demonstrates one way to do something similar to what you are trying to do using angular practices.
You'll note that I eliminated all the duplication in the view by using ng-repeat, and have made the number of child elements dynamic. I initialized the users object with an empty array, but you could easily initialize the object with data from the server.
Note also that changes to the form are immediately reflected in the object, meaning in the create() function, you can serialize the users object, not the form values. In practice, this is probably not necessary, however, since if you use an angular library like $http, serialization to and from JSON is performed automatically.
$scope.users = {
child: [{}]
};
$scope.create = function() {
var data = angular.toJson($scope.users);
alert(data);
};
$scope.addUser = function() {
$scope.users.child.push({});
};
<form ng-submit="create()">
<div ng-repeat="user in users.child">
<input type="text" ng-model="user.columnA">
<input type="text" ng-model="user.columnB">
<input type="text" ng-model="user.columnC">
</div>
<button type="submit">Submit</button>
</form>
<button ng-click="addUser()">Add New User</button>
<pre> {{users}}</pre>
The main takeaway from this, however, should be that the view and the controller work together to eliminate duplication and unnecessary references. we are no longer referring to child[0] in the HTML, making the HTML more readable and maintainable.
I am trying to retrieve two strings from my Parse Customers class. I want to compare these strings (usernameAdmin, and passwordAdmin respectively) with the input field entered by the user, and if they matched it will take them to a specific page.
I am taking this approach for a particular reason, and would appreciate feedback.
$scope.logIn = function(form) {
var Customer = Parse.Object.extend("Customers");
parseAdminUsername = Customer.get('usernameAdmin');
parseAdminPassword = Customer.get('passwordAdmin');
if (form.lusername == parseAdminUsername && form.lpassword == parseAdminPassword) {
window.location = 'adminSelect.php'
} else {
alert('error');
}
};
The html code looks as follow:
<form role="form" ng-show="scenario == 'Sign up'">
<h4 id="wrongCredentials"></h4>
<input id="signupformItem" ng-model="user.lusername" type="email" name="email" placeholder="Email Address"> <br>
<input id="signupformItem" ng-model="user.lpassword" type="password" name="password" placeholder=" Password"> <br>
<br>
<button id="signupbuttonFinal" ng-click="logIn(user)" class="btn btn-danger"> Sign In</button>
</form>
I receive the following error on console:
undefined is not a function
at Object.$scope.logIn
below is the line
parseAdminUsername = Customer.get('usernameAdmin');
You are better off using Parse in built users instead of creating a separate object and then you would use Parse.User.logIn.
If you were to use your table instead. Customer.get('usernameAdmin') is used to return a field after you have performed a query to return records/s. You are best to perform a find query to check the username and password like so:
var Customer = Parse.Object.extend("Customers");
var query = new Parse.Query(Customer);
query.equalTo("usernameAdmin", form.lusername);
query.equalTo("passwordAdmin", form.lpassword);
query.find({
success: function(results) {
if (results.length > 0) {
window.location = 'adminSelect.php';
}
},
error: function(error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and message.
}
});
I'd recommend not to use window and instead $window so that you can unit test your code. Better yet use angular routing and create a Single page app.
I'm new to js. trying to create mini validation function which will check fields if they're empty or not.
What i wanna do is, to call func like that checkIfEmpty("fullname, email,..."), then inside function, check each field seperated by comma, collect empty fields to one array, and check at the end if this array is empty or not. Tried something like following func, but don't know all alternatives of php functions in js. Please help me to realize my idea..
function checkIfEmpty(fields)
{
var emptyFields=new Array();
fields=fields.split(',');
foreach(fields as field)
{
if (!field.val()) {
field.attr('class', 'invalid');
emptyFields[] = field;
}
}
if(emptyFields.length()==0){return true;}
else {return false;}
}
Seems like you want something like this:
$("input:text").each(function(i, field) {
if (!field.val()) {
field.addClass('invalid');
}
});
return ($("input.invald").length > 0); // return true if invalid fields
You could also set a class on each input that not suppose to be empty, then on form submission check each input that has this class.
$('#form_id').submit(function() {
$('.required').each(function() {
// if a input field that's required is empty
// we add the class '.invalid'
if(!$(this).val()) {
$(this).addClass('invalid');
}
});
// prevent the submission if number
// there is required fields still empty
return ($('input.invalid').length == 0);
});
This is an example form with one required field called email:
<form method="POST">
<input type="text" name="email" class="required" />
<input type="text" name="firstname" />
<input type="text" name="lastname" />
<input type="submit" value="SEND" />
</form>