0%

Form validation (strategy pattern)

Updated regex pattern, using modifier i to reduce it, looks better now.
This time, it can be considered as Project work round 2, and the mission is do the form valition by JavaScript. Frankly speaking, I don't even have a clear clue about what I am going to do (or if I was able to do anything about that) until some morning - after I read one article from my APP - then the whole thing became clearly enough. And in a awesome and reusable way of course.

UPDATE
Different browsers act differently somehow. eg, input value is “12312” on Chrome and “123 12” on FireFox, so should be more careful! cl cl cl!!!
Got to know that we can use Proxy Pattern instead. article link ⬅ Will read later.

There are actually three parts, and the core is to create encapsulation so that you can reuse this pattern. IIFE is implemented to avoid creating global variables.

1) form-opt.js
2) rule-list.js
3) call these validation patterns

1) create stratege Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
const _validator = (function () {
return function (ruleList) {
return {
strategyFn: [],//Store strategy methods
ruleList: ruleList,//Strategies\' list to check
add: function (dom, rules) {
let that = this
for (let i = 0, len = rules.length; i < len; i++) {
;
(function (i) {
that.strategyFn.push(function () {
/*
* method: name of strategy(including eg. parameter minLength: 6)
* errMsg
* methodName: name of method
* info: store parameter, value, method\'s name and errMsg
* ruleList: use apply to pass parameter to specified method
*/
let info = []
let method = rules[i].strategy.split(":"),
methodName = method[0],
errMsg = rules[i].msg,
val = dom.value
info.push(val)
if (method[1]) {
info.push(method[1])
}
info.push(errMsg)
return that.ruleList[methodName].apply(dom, info)
})
})(i)
}
},
start: function () {
//iterate ruleList to catch error
for (i in this.strategyFn) {
if (this.strategyFn.hasOwnProperty(i)) {
let msg = this.strategyFn[i]()
if (msg) {
return msg
}
}
}
},
}
}
})()

2) write your own stratege methods for validation

In my case, here are the code I used

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/* rule-list */
const _rules = (function () {
const rulelist = {
isBlank: function (value, errorMsg) {
if (value === "") {
return errorMsg
}
},

isName: function (value, errorMsg) {
// uppercase/lowercase/multiple words
// updated, use modifier i in the end
if (!/^[a-z-åöäéáóíøæèüêû\s]+(\.)?/i.test(value)) {
return errorMsg
}
},

minLength: function (value, length, errorMsg) {
if (value.length < length) {
return errorMsg
}
},

maxLength: function (value, length, errorMsg) {
if (value.length > length) {
return errorMsg
}
},

isPhone: function (value, errorMsg) {
const reg1 = /\+?(?:0{0,2}[46]*){1}7{1}[0-9]{8}/
/* Matches 0798789678 */
const reg2 = /^(([+]\d{2}[ ][1-9]\d{0,2}[ ])|([0]\d{1,3}[-]))((\d{2}([ ]\d{2}){2})|(\d{3}([ ]\d{3})*([ ]\d{2})+))$/
/* Matches +46 8 123 456 78 | 08-123 456 78 | 0123-456 78 */
if (!reg1.test(value) && !reg2.test(value)) {
return errorMsg
}
},

isEmail: function (value, errorMsg) {
if (
!/^[A-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?(?:\.[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?)*$/i.test(
value
)
) {
return errorMsg
}
},

isSpace: function (value, errorMsg) {
if (
[...value].every((item) => {
return item === " "
})
) {
return errorMsg
}
},

isAdress: function (value, errorMsg) {
/* Pattern: uppercase/lowercase/multiple words allowed*/
const reg1 = /^[a-z0-9-åöäéáó\s]+(\.)?(\d{1,})(\.)?([a-zA-Z0-9-ÅÖÄåöäéáó\s]{0,})$/i
if (!reg1.test(value)) {
return errorMsg
}
},

isPcode: function (value, errorMsg) {
/* very rough way, not accurate enough */
if (!/^\d{3}\s*\d{2}$/.test(value)) {
return errorMsg
}
},

isCounty: function (value, errorMsg) {
// first letter no longer required to be capitalized
if (!/^[a-zöäåéáó]+/i.test(value)) {
return errorMsg
}
},
}
return {
rulelist: rulelist,
}
})()

3) Create an instance and begin our validation with customized errmsg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/* Create instance and pass ruleList*/
const validator = _validator(_rules.rulelist)

/* Choose form */
const forms = document.querySelector(".checkout-form")

/* Add strategies */
validator.add(forms.email, [{
strategy: "isBlank",
msg: "Var god ange en email-address",
},
{
strategy: "isSpace",
msg: "Var god fyll i uppgiftsfälten",
},
{
strategy: "isEmail",
msg: "Ange en giltig email-address",
},
])

validator.add(forms.fname, [{
strategy: "isBlank",
msg: "Var god ange ett förnamn",
},
{
strategy: "isSpace",
msg: "Var god fyll i uppgiftsfälten",
},
{
strategy: "isName",
msg: "Ange namn med stor bokstav, använd endast bokstäver",
},
{
strategy: "minLength:2",
msg: "Ange minst 2 bokstäver",
},
{
strategy: "maxLength:20",
msg: "Förnamn får ej vara längre än 20 tecken",
},
])

validator.add(forms.lname, [{
strategy: "isBlank",
msg: "Ange efternamn",
},
{
strategy: "isSpace",
msg: "Var god fyll i uppgiftsfälten",
},
{
strategy: "isName",
msg: "Ange namn med stor bokstav, använd endast bokstäver",
},
{
strategy: "minLength:2",
msg: "Ange minst 2 bokstäver",
},
{
strategy: "maxLength:20",
msg: "Förnamn får ej vara längre än 20 tecken",
},
])

validator.add(forms.phone, [{
strategy: "isBlank",
msg: "Ange ett telefonnummer",
},
{
strategy: "isSpace",
msg: "Var god fyll i uppgiftsfälten",
},
{
strategy: "isPhone",
msg: "Ange ett giltigt telefonnummer",
},
])

validator.add(forms.adress, [{
strategy: "isBlank",
msg: "Ange en adress",
},
{
strategy: "isAdress",
msg: "felaktig adress, ange en giltig adress",
},
{
strategy: "isSpace",
msg: "Please input valid text",
},
])

validator.add(forms.pcode, [{
strategy: "isBlank",
msg: "Ange ett postnummer",
},
{
strategy: "isSpace",
msg: "Var god fyll i uppgiftsfälten",
},
{
strategy: "isPcode",
msg: "Ange ett giltigt postnummer",
},
])

validator.add(forms.county, [{
strategy: "isBlank",
msg: "Ange stad",
},
{
strategy: "isSpace",
msg: "Var god fyll i uppgiftsfälten",
},
{
strategy: "isCounty",
msg: "Ange en giltig stad",
},
])

In the end, keep several functions that can also be reused.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function formatName(nameAreaValue) {
return nameAreaValue.split(' ')
.filter(name => {
if (name != '') return name;
})
.map(name => capitalizeFirstLetter(name))
.join(' ')
}

function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1)
}

function removeSpace(string) {
return string.replace(/\s/g, "")
}

function formatZipcode(string) {
return string
.replace(/\s/g, "")
.split(/(\d{3})/)
.join(" ")
.trim()
}