The Web Design Group

... Making the Web accessible to all.

Welcome Guest ( Log In | Register )

 
Reply to this topicStart new topic
> JavaScript Object Orientated Validation
Terminator
post May 19 2016, 04:04 PM
Post #1


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



I am validating larger forms with JavaScript and it looks like I need to start using objects. Here is how I currently do it below, this is ok for a few text boxes, but there are going to be too many if else statements in there to validate after more text boxes, drop down boxes, etc get added.


CODE

    var first_name = ($("first_name").value.replace(/[^a-zA-Z]/g, ''));
    var score = ($("score").value.replace(/[^\d]/g, ''));
    validateInput(first_name, score);


CODE

function validateInput(first_name, score){
    if (first_name == "") {
        $("first_name_error").innerHTML = "First Name is required" + "<br><br>";
        $("first_name").focus();
        isValid = false;
    } else {
        $("first_name_error").innerHTML = "";
        return first_name;
    }
    if (score == "" || score < 0 || score > 100) {
        $("score_error").innerHTML = "Score must be between 0 and 100" + "<br><br>";
        $("score").focus();
        isValid = false;
    } else {
        $("score_error").innerHTML = "";
        return score;
    }
};


Any advice on how I go about this with Object Orientated Programming? Do you recommend I use prototypes?

There is going to be other stuff to validate like email address, phone number, date, etc. So there are going to be a lot of different error messages like "Email is not valid, Date is not valid, etc". Or do you know of a good site that gives great object orientated validation examples?

This post has been edited by Terminator: May 19 2016, 04:05 PM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post May 20 2016, 09:36 AM
Post #2


.
********

Group: WDG Moderators
Posts: 9,656
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ May 19 2016, 11:04 PM) *

Any advice on how I go about this with Object Orientated Programming? Do you recommend I use prototypes?

Can't help with any of that I'm afraid.

QUOTE
There is going to be other stuff to validate like email address, phone number, date, etc. So there are going to be a lot of different error messages like "Email is not valid, Date is not valid, etc".

You still need to store the error messages and validation expressions for each form field somewhere. Maybe a 2-dimensional array could be used for that?

User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post May 20 2016, 10:14 AM
Post #3


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ May 20 2016, 09:36 AM) *

You still need to store the error messages and validation expressions for each form field somewhere. Maybe a 2-dimensional array could be used for that?


I found an example, it doesn't explain anything though so I will have to look at the code and figure it out. It puts all the error messages in fields:
("message" is what is displayed automatically when form loads)

CODE

"use strict";
var fields = {
    email: {
        message: "Must be a valid email address.",
        required: "Email is required.",
        isEmail: "Email is not valid."
    },
    password: {
        required: "Password is required."
    },
    verify: {
        required: "Please retype your password.",
        noMatch: ["Passwords do not match.", "password"]
    },
    first_name: {
        required: "First name is required."
    },
    last_name: {
        required: "Last name is required."
    }
}


Then there are separate validation functions like below. That way the isBlank can be used for anything that is left blank. There are more for other validation like isEmail, isPhone, etc.

CODE

Validate.prototype.isBlank = function(text) {
    return (text === "");
};

Validate.prototype.isMatch = function(text1, text2) {
    return (text1 === text2);
};



Then this appears to help throw the error messages when validation fails:
CODE

RegisterForm.prototype.validateField = function(fieldName, text) {
    var field = fields[fieldName];
    if (field.required) {
        if ( this.isBlank(text) ) { throw new Error(field.required); }
    }
    if (field.noMatch) {
        if ( ! this.isMatch(text, $(field.noMatch[1]).value ) ) {
            throw new Error(field.noMatch[0]);
        }
    }
    if (field.isEmail) {
        if ( ! this.isEmail(text) ) { throw new Error(field.isEmail); }
    }
};

RegisterForm.prototype.setError = function( fieldName, message ) {
    $(fieldName + "_error").setAttribute("class", "error");
    $(fieldName + "_error").firstChild.nodeValue = message;
};
RegisterForm.prototype.clearError = function( fieldName, message ) {
    $(fieldName + "_error").setAttribute("class", "");
    $(fieldName + "_error").firstChild.nodeValue = message || "";
};


There is quite a lot of code to go through but I think I am going to try to use this method for when I have a lot of stuff to validate.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post May 20 2016, 08:04 PM
Post #4


.
********

Group: WDG Moderators
Posts: 9,656
Joined: 10-August 06
Member No.: 7



Not that I want to discourage learning about objects and prototypes, but I just found that you can use condition expressions as function parameters, like this:

CODE
function validate(field, field_cond, error_field, error_msg)
{
    if(field_cond==true)
    {
        error_field.innerHTML=error_msg;
        field.focus();
    }
    else
    {
        error_field.innerHTML="";
    }
}

var score=document.getElementById('score');
var score_error_field=document.getElementById('score_error_field');
var score_cond=(score.value == "" || score.value < 0 || score.value > 100);
var score_error_msg="Score must be between 0 and 100";

validate(score, score_cond, score_error_field, score_error_msg);


This post has been edited by Christian J: May 21 2016, 06:09 AM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post May 20 2016, 09:32 PM
Post #5


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ May 20 2016, 08:04 PM) *

Not that I want to discourage learning about objects and prototypes, but I just found that you can use condition expressions as function parameters, like this:

CODE
function validate(field, field_cond, error_field, error_msg)
{
    if(field_cond==true)
    {
        cur_error_field.innerHTML=error_msg;
        cur_field.focus();
    }
    else
    {
        cur_error_field.innerHTML="";
    }
}

var score=document.getElementById('score');
var score_error_field=document.getElementById('score_error_field');
var score_cond=(score.value == "" || score.value < 0 || score.value > 100);
var score_error_msg="Score must be between 0 and 100";

validate(score, score_cond, score_error_field, score_error_msg);



Thanks, I am still going to learn how to do this with objects, but am also trying your method as well.

Shouldn't cur_error_field, etc under the validate function be defined/declared with var?
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post May 21 2016, 06:08 AM
Post #6


.
********

Group: WDG Moderators
Posts: 9,656
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ May 21 2016, 04:32 AM) *

Shouldn't cur_error_field, etc under the validate function be defined/declared with var?

I made a few typos, sorry. I've edited my code example. blush.gif
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post May 21 2016, 11:34 AM
Post #7


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ May 21 2016, 06:08 AM) *

I made a few typos, sorry. I've edited my code example. blush.gif


Thanks, so when adding more fields to validate do I do it like below? Adding a new validate(field, cond, errorfield, errormsg) for each individual field?

CODE

    var score_error_field = $("score_error_field");
    var score_cond = (score.value == "" || score.value < 0 || score.value > 100);
    var score_error_msg = "Score must be between 0 and 100" + "<br><br>";
    
    var first_name_error_field = $("first_name_error_field");
    var first_name_cond = (first_name.value == "");
    var first_name_error_msg = "First Name is required" + "<br><br>";
    
    validate(score, score_cond, score_error_field, score_error_msg);
    validate(first_name, first_name_cond, first_name_error_field, first_name_error_msg);


This post has been edited by Terminator: May 21 2016, 11:35 AM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post May 21 2016, 12:13 PM
Post #8


.
********

Group: WDG Moderators
Posts: 9,656
Joined: 10-August 06
Member No.: 7



Yes that looks correct. You may need to modify it to work with your $()-variables though.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post May 21 2016, 12:25 PM
Post #9


.
********

Group: WDG Moderators
Posts: 9,656
Joined: 10-August 06
Member No.: 7



Seems I forgot to post this before. Here's an even shorter variant of the above:

CODE
<script type="text/javascript">
function validation()
{
    function validate_field(field, field_cond, error_field, error_msg)
    {
        if(field_cond==true)
        {
            error_field.innerHTML=error_msg;
            field.focus();
        }
        else
        {
            error_field.innerHTML="";
        }
    }
    validate_field(first_name, (first_name.value == ""), first_name_error, "First Name is required");
    validate_field(score, (score.value == "" || score.value < 0 || score.value > 100), score_error, "Score must be between 0 and 100");
}
</script>

<input type="text" id="first_name" value="">
<span id="first_name_error"></span>

<input type="text" id="score" value="">
<span id="score_error"></span>

<input type="button" value="Validate" onclick="validation();">

Note that it uses no getElementById. This is because element IDs are available as global variables automatically. This was invented by MSIE a long time ago, and now it's been included in HTML5: https://www.w3.org/TR/2014/REC-html5-201410...e-window-object

But note also that this is considered bad coding practice. Due to its "global scope pollution", W3C considered only allowing it in quirksmode: https://www.w3.org/Bugs/Public/show_bug.cgi?id=11960 (the discussion is a bit revealing in a sad way of how questionable features slip into the spec).
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post May 21 2016, 01:04 PM
Post #10


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ May 21 2016, 12:25 PM) *

Note that it uses no getElementById. This is because element IDs are available as global variables automatically. This was invented by MSIE a long time ago, and now it's been included in HTML5: https://www.w3.org/TR/2014/REC-html5-201410...e-window-object

But note also that this is considered bad coding practice. Due to its "global scope pollution", W3C considered only allowing it in quirksmode: https://www.w3.org/Bugs/Public/show_bug.cgi?id=11960 (the discussion is a bit revealing in a sad way of how questionable features slip into the spec).


Do you mean it is not always the best idea since w3c is considering it bad coding practice? I didn't know about this method till now.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post May 21 2016, 02:33 PM
Post #11


.
********

Group: WDG Moderators
Posts: 9,656
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ May 21 2016, 08:04 PM) *

Do you mean it is not always the best idea since w3c is considering it bad coding practice?

No, it seems a lot of people consider it bad practice. See e.g. https://www.tjvantoll.com/2012/07/19/dom-el...is-behavior-bad for examples on why (a bit down).

Some consider all global variables bad:
http://www.standardista.com/javascript/15-...-gotchas/#scope
http://c2.com/cgi/wiki?GlobalVariablesAreBad

QUOTE
I didn't know about this method till now.

I've seen it in MSIE long time ago, but didn't know that it's made it into HTML5 until I experimented with your script last night.

User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post May 22 2016, 02:39 PM
Post #12


.
********

Group: WDG Moderators
Posts: 9,656
Joined: 10-August 06
Member No.: 7



Here's another idea, that stores the validation rules as array values. Since comparison operators use variables, that don't exist yet in the array, I use function arguments instead of variables. Maybe someone will comment if this is a terrible idea.

CODE
<script type="text/javascript">
function validate_form()
{
    // form field ID, error message, validation rules
    var field_arr=[
        ['first_name', 'First Name is required', function(field_val){return field_val=='';}],
        ['score', 'Score must be between 0 and 100', function(field_val){return field_val==''|| field_val<0 || field_val>100;}]
    ]

    for(var i=0; i<field_arr.length; i++)
    {
        var field=document.getElementById(field_arr[i][0]);
        var error_field=document.getElementById(field_arr[i][0]+'_error'); // current form field ID + '_error'
        error_field.innerHTML="";

        if(field_arr[i][2](field.value)) // calls function in array, with field.value as parameter
        {
                error_field.innerHTML=field_arr[i][1];
                field.focus();
        }
    }
}
</script>

<input type="text" id="first_name" value="">
<span id="first_name_error"></span>

<input type="text" id="score" value="">
<span id="score_error"></span>

<input type="button" value="Validate" onclick="validate_form();">
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post May 22 2016, 02:52 PM
Post #13


.
********

Group: WDG Moderators
Posts: 9,656
Joined: 10-August 06
Member No.: 7



A couple of sidenotes regarding the original script:

QUOTE(Terminator @ May 19 2016, 11:04 PM) *

CODE

if (score == "" || score < 0 || score > 100)
{
        $("score_error").innerHTML = "Score must be between 0 and 100" + "<br><br>";
        $("score").focus();
        isValid = false;
    } else {
        $("score_error").innerHTML = "";
        return score;
    }
};


The IF statement returns true if the form field value is invalid, with "valid" being the default. I think it's more intuitive to assume the form field is invalid (or at least "unchecked") by default, and only change it to valid after successful validation.

It should be enough to test if the field value is a number from 0 to 100, the empty value "" seems unnecessary (especially if you already checked this with regex).

If more than one field is invalid, focus() will be applied to all of them in turn (settling on the last invalid field).

There's no need for the ELSE statement if it's just used for emptying the error message field. You might empty it by default instead, before the IF statement. (Not sure what the "return score" is used for, so maybe you still ned the ELSE.)









User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post May 22 2016, 10:40 PM
Post #14


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ May 22 2016, 02:52 PM) *

A couple of sidenotes regarding the original script:

The IF statement returns true if the form field value is invalid, with "valid" being the default. I think it's more intuitive to assume the form field is invalid (or at least "unchecked") by default, and only change it to valid after successful validation.

It should be enough to test if the field value is a number from 0 to 100, the empty value "" seems unnecessary (especially if you already checked this with regex).

If more than one field is invalid, focus() will be applied to all of them in turn (settling on the last invalid field).

There's no need for the ELSE statement if it's just used for emptying the error message field. You might empty it by default instead, before the IF statement. (Not sure what the "return score" is used for, so maybe you still ned the ELSE.)


It returns name and score back to the addScore function, I am pretty sure I need that in there. If I remove the empty value check from the scores validator it will process without a score showing.

You can see the whole program in action here:

http://examples.mrwdesign.net/js/scores/scores.html

I am having an issue with the focus if you hit the Add Score button without inputting anything, it is setting focus to "score" which is 3rd. I need to figure out how to push it to set focus to the first textbox that shows an error message.

Feel free to look at my other JavaScript stuff I made if you want, FYI the "download code" links to download all files are not set up yet until these are all finalized. But you can view each program by clicking the view links. I am still adding new stuff to this page, trying to make more advanced programs next.

http://examples.mrwdesign.net/js/js.html

Here is the code for the scores array program. There is also a main.js file that codes for the $ getElementById, but all the js code for this program is below:

This is the main scores.js file:

CODE

var scoreList = [];
var scoresString = [];
var    isValid = true;

function addScore() {
    // strip and trim input
    var first_name = ($("first_name").value.replace(/[^a-zA-Z]/g, ''));    
    var last_name = ($("last_name").value.replace(/[^a-zA-Z'-]/g, ''));    
    var score = ($("score").value.replace(/[^\d]/g, ''));
    
    // validate input and confirm first letters are uppercase
    first_name = first_name.charAt(0).toUpperCase() + first_name.slice(1).toLowerCase();
    validateFirstName(first_name);
    
    last_name = last_name.charAt(0).toUpperCase() + last_name.slice(1).toLowerCase();
    validateLastName(last_name);
    
    validateScore(score);
    
    if (isValid) {
        score = parseInt(score);
        var scoreString = last_name + ", " + first_name + ": " + score;
                          
        // store data in array
        scoreList.push(score);
        scoresString.push(scoreString);
        
        // display scores
        displayScores();
        
        // clear input form for next entry
        clearInput();    
    }                  
};

function clearInput() {
    $("first_name").value = "";
    $("last_name").value = "";
    $("score").value = "";
    $("first_name_error").innerHTML = "";
    $("last_name_error").innerHTML = "";
    $("score_error").innerHTML = "";
    $("first_name").focus();
};

function resetForm() {
    $("first_name_error").innerHTML = "";
    $("last_name_error").innerHTML = "";
    $("score_error").innerHTML = "";
    $("avg").value = "";
    $("scoreList").value = "";
    scoreList = [];
    scoresString = [];
    $("first_name").focus();
};

window.onload = function() {
    $("addScore").onclick = addScore;
    $("clearInput").onclick = clearInput;
    $("sortLast").onclick = sortScores;
    $("reset").onclick = resetForm;
    $("first_name").focus();
};


This is library_scores.js:
CODE

function displayScores() {  
    // calculate average score
    var totalScore = 0;
    for (var i in scoreList) {
        totalScore += scoreList[i];
    }
    var numberOfScores = scoreList.length;
    var averageScore = totalScore / numberOfScores;    

    // display scores
    $("scoreList").value = scoresString.join("\n");
    $("avg").value = averageScore.toFixed(1);
    // if sort button is clicked w/out scores in list
    if (scoreList.length == 0) {
        $("avg").value = "";
        $("first_name").focus();
    }
};

function sortScores() {  
    // sort scores
    scoresString.sort();
    $("first_name").focus();
    
    // display scores
    displayScores();    
};


This is validate_scores.js:
CODE

// validate
function validateFirstName(first_name) {
    if (first_name == "") {
        $("first_name_error").innerHTML = "First Name is required" + "<br><br>";
        $("first_name").focus();
        isValid = false;
    } else {
        $("first_name_error").innerHTML = "";
        isValid = true;
        return first_name;
    }
};

function validateLastName(last_name) {
    if (last_name == "") {
        $("last_name_error").innerHTML = "Last Name is required" + "<br><br>";
        $("last_name").focus();
        isValid = false;
    } else {
        $("last_name_error").innerHTML = "";
        isValid = true;
        return last_name;
    }
};

function validateScore(score){
    if (score == "" || score < 0 || score > 100) {
        $("score_error").innerHTML = "Score must be between 0 and 100" + "<br><br>";
        $("score").focus();
        isValid = false;
    } else {
        $("score_error").innerHTML = "";
        isValid = true;
        return score;
    }
};

User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post May 23 2016, 09:05 AM
Post #15


.
********

Group: WDG Moderators
Posts: 9,656
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ May 23 2016, 05:40 AM) *

I am having an issue with the focus if you hit the Add Score button without inputting anything, it is setting focus to "score" which is 3rd. I need to figure out how to push it to set focus to the first textbox that shows an error message.

The script sets focus to each invalid field in turn, and score is simply the last one. Perhaps you could reverse the order you validate the fields, i.e. validate score first and first_name last?
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post May 23 2016, 09:51 AM
Post #16


.
********

Group: WDG Moderators
Posts: 9,656
Joined: 10-August 06
Member No.: 7



BTW you don't need a FORM element if you're not going to submit the form to a server side script.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post May 23 2016, 02:22 PM
Post #17


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ May 23 2016, 09:05 AM) *

The script sets focus to each invalid field in turn, and score is simply the last one. Perhaps you could reverse the order you validate the fields, i.e. validate score first and first_name last?


I had tried that already but it did not work. When I figure out how to validate with objects and prototypes I will be able to always set focus on the first error message.

QUOTE(Christian J @ May 23 2016, 09:51 AM) *

BTW you don't need a FORM element if you're not going to submit the form to a server side script.


I use the form element because I want it to process and submit the calculations or whatever JavaScript needs to do when you hit enter on the keyboard

CODE

<form name="scoresForm" id="scoresForm" method="get" onsubmit="addScore(); return false">


However on the add scores program this doesn't work correctly so I disabled it.

This post has been edited by Terminator: May 23 2016, 02:28 PM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post May 23 2016, 05:01 PM
Post #18


.
********

Group: WDG Moderators
Posts: 9,656
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ May 23 2016, 09:22 PM) *

I had tried that already but it did not work.

Seems the current script doesn't work either, unless the user trigs each validation error in order. To reproduce this bug, try e.g. adding a score only (no names), then click the Add Score button.

This happens because the final value of isValid is set by the last validation function, overwriting any previous isValid value (so if the last field is valid, isValid becomes true even if the previous fields were not).

Instead you might write the script so that once the default value of isValid has been changed to false, it can't be changed back to true until the user calls function addScore again. This means removing the "isValid = true" parts in the ELSE statements of the validation functions, but that in turn means there's no way to make isValid true the next time function addScore is called (since the default value of isValid is given when the script first runs, not when function addScore runs). So you must declare isValid inside function addScore, and also make it global (by removing its "var" keyword) so it becomes available for the validation functions too.

Hope the above makes sense (and is correct, don't trust me on that blink.gif ). Once you fix that I think you can change the order of the focus too.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post May 23 2016, 08:22 PM
Post #19


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ May 23 2016, 05:01 PM) *

Seems the current script doesn't work either, unless the user trigs each validation error in order. To reproduce this bug, try e.g. adding a score only (no names), then click the Add Score button.


Thanks, I fixed it to where it sets focus in correct order, and wont submit until all fields are filled out. I put the validation within the addScore function. I don't like doing that when there are a few fields to validate but its ok for now until I learn how to validate with objects correctly.

I am going to have to work on JavaScript a little slower now though because I have a summer Java class starting, and have also been working on C# a lot more lately too. I think C#, SQL, and Java should probably be a top priority for me, but I still want to learn JavaScript when I have time.

This post has been edited by Terminator: May 23 2016, 08:30 PM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post

Reply to this topicStart new topic
2 User(s) are reading this topic (2 Guests and 0 Anonymous Users)
0 Members:

 



- Lo-Fi Version Time is now: 25th April 2024 - 07:58 PM