Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Art of Form Validation

Caneco
August 29, 2019

The Art of Form Validation

A form could be simple or exhaustive, can have simple or dynamic values… Either way, all payloads between client and server should be validated. And when errors happen (because they will happen), the user needs help and guidance to solve the problem. How can you achieve the best combination of User Interface (UI), User Experience (UX), and Developer Experience (DX)? Join me and learn all the good tips and tricks using the community's favorite stack: Laravel, Vue.

↓↓↓
Live recording of this talk at Laracon.EU
https://youtube.com/watch?v=rkdlJeHTeCY

Caneco

August 29, 2019
Tweet

More Decks by Caneco

Other Decks in Programming

Transcript

  1. WARNING ALL THE FOLLOWING SLIDES CONTAIN VALUABLE INFORMATION FOR YOUR

    DAILY LIFE… ALL GIFS ARE FROM THE BADASS TV SHOW COBRA KAI THAT YOU ALL MUST WATCH… AND ALL THE SLIDES, AND CONTENT ARE 100% MADE ON KEYNOTE ̈
  2. through APIs [GET] skyscanner-example.dev/v1/flights [PATCH] zomato-example.dev/place/123/book [GET] openweather-example.dev/fatima [GET] musixmatch-example.dev/top-10

    [GET] nasa-example.dev/v9999/planet-x [GET] numbers-example.dev/random-number [GET] urbandictionary-example.dev/tldr [GET] tempmail-example.dev/fresh [GET] moviedatabase-example.dev/v1/top/horror [GET] ip-example.dev/127.0.0.1
  3. SIGN IN Sign in These credentials do not match our

    records. Email address Password
  4. 1 2 3 4 ▶ 9 10 11 12 <input

    name="username" type="text" "# ""$ <input name="password" type="password" "#
  5. 1 2 3 4 5 ▶ 8 9 10 11

    12 <input name="username" type="text" required "# ""$ <input name="password" type="password" required "#
  6. SIGN IN Sign in Email address Password Please fill in

    this field. Please fill in this field.
  7. SIGN IN Sign in These credentials do not match our

    records. Email address example Password
  8. 1 2 3 4 5 ▶ 8 9 10 11

    12 <input name="username" type="text" required "# ""$ <input name="password" type="password" required "#
  9. 1 2 3 4 5 ▶ 8 9 10 11

    12 <input name="username" type="email" required "# ""$ <input name="password" type="password" required "#
  10. SIGN IN Sign in These credentials do not match our

    records. Email address example@email Password ••••••
  11. 1 2 3 4 5 ▶ 8 9 10 11

    12 <input name="username" type="email" required "# ""$ <input name="password" type="password" required "#
  12. 1 2 3 4 5 6 ▶ 8 9 10

    11 12 <input name="username" type="email" pattern=".+@.+\"%+" required "# ""$ <input name="password" type="password" required "#
  13. 1 2 3 4 5 6 ▶ 8 9 10

    11 12 13 <input name="username" type="email" pattern=".+@.+\"%+" required "# ""$ <input name="password" type="password" minlength="6" required "#
  14. 1 2 3 4 5 6 ▶ 8 9 10

    11 12 13 14 <input name="username" type="email" pattern=".+@.+\"%+" required "# ""$ <input name="password" type="password" minlength="6" pattern="^(?=.\d)(?=.[a-z])(?=.*[A-Z]).{8,}$" required "#
  15. 1 2 3 4 5 6 ▶ 8 9 10

    11 12 13 14 <input name="username" type="email" pattern=".+@.+\"%+" required "# ""$ <input name="password" type="password" minlenght="6" pattern="^(?=.\d)(?=.[a-z])(?=.*[A-Z]).{8,}$" required "#
  16. 1 2 3 4 5 6 ▶ 8 9 10

    11 12 13 <input name="username" type="email" pattern=".+@.+\"%+" required "# ""$ <input name="password" type="password" minlenght="6" pattern="^(?=.\d)(?=.[a-z])(?=.*[A-Z]).{8,}$" required "#
  17. 1 2 3 4 5 6 ▶ 20 function "'invoke(Request

    $request) { $request"(validate([ 'username' ") 'required|email', 'password' ") 'required|min:6|strong_password', ]); ""$ }
  18. 1 2 3 4 5 6 7 8 9 10

    var rules = { username: { required: true, email: true, }, password: { required: true, minlength: 6, }, }
  19. 1 2 3 4 5 6 7 8 9 10

    11 12 var rules = { username: { required: true, email: true, }, password: { required: true, minlength: 6, }, } $("#login").validate(rules)
  20. 1 2 3 4 let input = { username: document.getElementById('username').value(),

    password: document.getElementById('password').value(), }
  21. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 15 let input = { username: document.getElementById('username').value(), password: document.getElementById('password').value(), } let rules = { username: { presence: true, email: true }, password: { presence: true, length: { minimum: 6 }, }, }
  22. 1 ▶ 4 7 6 ▶ 15 16 17 18

    19 let input = { !!" } let rules = { ""$ } if (validate(input, rules)) { "+ }
  23. 1 2 3 4 import Vue from 'vue' import VeeValidate

    from 'vee-validate' Vue.use(VeeValidate)
  24. 1 2 3 4 5 6 7 <input name="username" v-model="username"

    v-validate type="email" required "#
  25. 1 2 3 4 5 6 7 <input name="username" v-model="username"

    v—validate type="email" required "#
  26. 1 2 3 4 import Vue from 'vue' import VeeValidate

    from 'vee-validate' Vue.use(VeeValidate)
  27. 1 2 3 4 5 6 import Vue from 'vue'

    import VeeValidate from 'vee-validate' Vue.use(VeeValidate, { useConstraintAttrs: false, })
  28. 1 2 3 4 5 6 7 <input name="username" v-model="username"

    v-validate type="email" required "#
  29. 1 2 3 4 5 6 7 8 9 10

    11 alpha alpha_dash alpha_num alpha_spaces before between confirmed credit_card date_between date_format decimal digits dimensions email image included integer ip is is_not length max_value mimes min max min_value excluded numeric regex required required_if size url
  30. 1 2 3 4 5 6 7 import { Validator

    } from 'vee-validate' import { STRONG_PASS_REGEX } from '@/helpers/regex_rules' const strongPassword = { getMessage: field ") `${field} is weak.`, validate: value ") STRONG_PASS_REGEX.test(value) }
  31. 1 2 3 4 5 6 7 import { Validator

    } from 'vee-validate' import { STRONG_PASS_REGEX } from '@/helpers/regex_rules' const strongPassword = { getMessage: field ") `${field} is weak.`, validate: value ") STRONG_PASS_REGEX.test(value) }
  32. 1 2 3 4 5 6 7 import { Validator

    } from 'vee-validate' import { STRONG_PASS_REGEX } from '@/helpers/regex_rules' const strongPassword = { getMessage: field ") `${field} is weak.`, validate: value ") STRONG_PASS_REGEX.test(value) }
  33. 1 2 3 4 5 6 7 8 9 import

    { Validator } from 'vee-validate' import { STRONG_PASS_REGEX } from '@/helpers/regex-rules' const strongPassword = { getMessage: field ") `${field} is weak.`, validate: value ") STRONG_PASS_REGEX.test(value) } Validator.extend('strong_password', strongPassword)
  34. 1 2 3 4 5 6 7 <input class="field-input" name="username"

    v-model="username" v-validate="'required|email'" type="text" "#
  35. 1 2 3 4 5 6 7 8 9 10

    <label class="field-label"> Email address "*label> <input class="field-input" name="username" v-model="username" v-validate="'required|email'" type="text" "#
  36. 1 2 3 4 5 6 7 8 9 10

    11 12 13 14 <label class="field-input" :class="{ 'has-error': errors.has('username') }" > Email address "*label> <input class="field-input" :class="{ 'has-error': errors.has('username') }" name="username" v-model="username" v-validate="'required|email'" type="text" "#
  37. 1 ▶ 6 7 ▶ 14 15 16 17 <label>

    ""$ "*label> <input ""$ "# <p v-if="errors.has('username')" class="field-help"> The username must be a valid email. "*p>
  38. 1 ▶ 6 7 ▶ 14 15 16 17 <label>

    ""$ "*label> <input ""$ "# <p v-if="errors.has('username')" class="field-help"> {{ errors.first('username') }} "*p>
  39. SIGN IN Sign in The username must be a valid

    email. Email address example Password •••••• Email address example
  40. 1 2 3 4 {{ errors.any() }} {{ errors.all() }}

    {{ errors.count() }} {{ errors.clear() }}
  41. 1 2 3 4 5 6 ▶ 20 function "'invoke(Request

    $request) { $request"(validate([ 'username' ") 'required|email', 'password' ") 'required|min:6|strong_password', ]); ""$ }
  42. 1 2 3 4 5 6 7 8 9 HTTP/1.1

    422 Unprocessable Entity Content-Type: application/json; charset=utf-8 ""$ "data": { "errors": { "email": [ "The email must be a valid email address." ], ""$
  43. 1 2 3 4 5 6 7 8 9 HTTP/1.1

    422 Unprocessable Entity Content-Type: application/json; charset=utf-8 ""$ "data": { "errors": { "email": [ "The email must be a valid email address." ], ""$
  44. 1 2 3 4 5 6 7 8 9 10

    11 12 13 HTTP/1.1 422 Unprocessable Entity Content-Type: application/json; charset=utf-8 ""$ "data": { "errors": { "email": [ "The email must be a valid email address." ], "password": [ "The password must be at least 6 characters", "The password must have 2 uppercase letters 1 special letter 2 digi ] ""$
  45. 1 2 ▶ 8 ▶ 12 13 if (response.data.errors.email) {

    if (/required/i.test(response.data.errors)) { ""$ } else if (/invalid/i.test(response.data.errors)) { ""$ } }
  46. 1 2 3 4 5 6 7 public function rules()

    { return [ 'username' ") 'required|email', 'password' ") 'required|min:6|strong_password', ]; }
  47. 1 2 3 4 5 6 7 8 9 public

    function messages() { return [ 'required' ") ':attribute.required', 'email' ") 'email.invalid', 'password.min' ") 'password:min', 'strong_password' ") 'password_not_strong', ]; }
  48. 1 2 3 4 5 6 7 8 9 public

    function failedValidation(Validator $validator) { throw (new HttpResponseException( response()"(json([ 'message' ") 'The given data was invalid.', 'errors' ") $validator"(errors()"(all(), ], 422) )); }
  49. 1 2 3 4 5 6 7 8 9 public

    function failedValidation(Validator $validator) { throw (new HttpResponseException( response()"(json([ 'message' ") 'The given data was invalid.', 'errors' ") $validator"(errors()"(all(), ], 418) )); }
  50. 1 2 3 4 5 6 7 8 9 HTTP/1.1

    418 I'm a teapot Content-Type: application/json; charset=utf-8 ""$ { "message": "The given data was invalid.", "errors": [ "email.invalid" ] }
  51. 1 2 3 4 5 6 7 8 9 function

    "'invoke(Request $request) { $request"(validate([ 'email' ") [ 'required', 'email', ] ]); }
  52. 1 2 3 4 5 6 7 8 9 function

    "'invoke(Request $request) { $request"(validate([ 'email' ") [ 'required', 'email', "+ [email protected] ] ]); }
  53. 1 2 3 4 5 6 7 8 9 function

    "'invoke(Request $request) { $request"(validate([ 'email' ") [ 'required', 'email', ] ]); }
  54. 1 2 3 4 5 6 7 8 9 function

    "'invoke(Request $request) { $request"(validate([ 'email' ") [ 'required', 'email:rfc,dns', "+ new in Laravel 5.8.33 ] ]); }
  55. 1 2 3 4 5 6 7 8 9 10

    function "'invoke(Request $request) { $request"(validate([ 'email' ") [ 'required', 'email:rfc,dns', 'unique:users.email', ] ]); }
  56. 1 2 3 4 5 6 7 8 9 10

    function "'invoke(Request $request) { $request"(validate([ 'email' ") [ 'required', 'email:rfc,dns', "+ ", ATTENTION 'unique:users.email', "+ ", ATTENTION ] ]); }
  57. 1 2 3 4 5 6 7 8 9 10

    11 function "'invoke(Request $request) { $request"(validate([ 'email' ") [ 'bail', 'required', 'email:rfc,dns', 'unique:users.email', ] ]); }
  58. 1 2 3 4 5 6 7 8 HTTP/1.1 418

    I'm a teapot Content-Type: application/json; charset=utf-8 ""$ "data": { "errors": [ "email.invalid", ] ""$
  59. 1 2 3 4 5 6 7 this.$http.post('/submit’) .then(response ")

    { "+ }) .catch({ response } ") { "+ })
  60. 1 ▶ 5 6 7 8 9 this.$http.post('/submit’) ""$ .catch({

    response } ") { if (response.status ""- 418) { "+ } })
  61. 1 ▶ 5 6 7 8 9 10 11 this.$http.post('/submit’)

    ""$ .catch({ response } ") { if (response.status ""- 418) { if (response.data.errors.includes('email.invalid')) { "+ } } })
  62. 1 ▶ 5 6 7 8 9 10 11 12

    13 14 15 this.$http.post('/submit’) ""$ .catch({ response } ") { if (response.status ""- 418) { let errors = response.data.errors if (response.data.errors.includes('email.invalid')) { this.$validator.errors.add({ field: 'email', msg: 'The email must be a valid email address.', }) } } })
  63. 1 ▶ 5 6 7 8 9 10 11 12

    13 14 15 16 this.$http.post('/submit’) ""$ .catch({ response } ") { if (response.status ""- 418) { let errors = response.data.errors if (errors.includes('email.invalid')) { this.$validator.errors.add({ field: 'email', msg: 'The email must be a valid email address.', }) this.$el.username.focus() } } })
  64. 1 2 3 4 5 6 7 8 import {

    Validator } from 'vee-validate' const phone = { getMessage: field ") `The ${field} must be a valid phone number.`, validate: value ") { return /^[0-9]{9}$/.test(value) } }
  65. 1 2 3 4 5 6 7 8 import {

    Validator } from 'vee-validate' const phone = { getMessage: field ") `The ${field} must be a valid phone number.`, validate: value ") { return /^2[0-9]{8}|9[1236][0-9]{7}$/.test(value) } }
  66. 1 2 3 4 5 6 7 <input name="phone" v-model="phone"

    v-validate="'required|phone'" type="text" maxlength="9" "#
  67. 1 2 3 4 import Vue from 'vue' import VeeMask

    from 'vee-mask' Vue.use(VeeMask)
  68. 1 2 3 4 5 6 7 8 <input name="phone"

    v-model="phone" v-validate="'required|phone'" v-mask="'#########'" type="text" maxlength="9" "#
  69. 1 2 3 4 5 6 7 8 <input name="phone"

    v-model="phone" v-validate="'required|phone'" v-mask="'"". "". "".'" type="text" maxlength="11" "#
  70. Sign up Full name Email address Phone CONTINUE | Done

    Q W E R T Y U I O P space return 123 A S D F G H J K L ⌫ ⇧ Z X C V B N M
  71. 1 2 3 4 5 6 7 8 Vue.directive('autofocus', {

    inserted: (el, binding) ") { if (! isMobile) { el.setAttribute('autofocus', 'autofocus') el.focus() "+ Justin Case } }, })
  72. SUBMIT The Form Email field | Done Q W E

    R T Y U I O P space return 123 A S D F G H J K L ⌫ ⇧ Z X C V B N M Done Q W E R T Y U I O P space return 123 A S D F G H J K L ⌫ ⇧ Z X C V B N M @ .
  73. The Form Phone field SUBMIT | Done ⌫ + ❋

    # 2 1 3 5 4 6 8 7 9 0 ABC GHI JKL MNO DEF TUV PQRS WXYZ
  74. The Form Number field SUBMIT | Done 1 2 3

    4 5 6 7 8 9 0 space return ABC ⌫ #+= . , ? ! ´ - / : ; ( ) $ & @ "
  75. 66 20 76 Nope Chrome Firefox Edge Safari Desktop Mobile

    / Tablet 75 Nope 67 12.2-12.3 Android Chrome Android Firefox Android iOS Safari
  76. 1 2 3 4 5 6 <input name="field" type="text" inputmode="number"

    "+ tel, url, email, … maxlength="9" "#
  77. The Form Decimal field SUBMIT | Done ⌫ . 2

    1 3 5 4 6 8 7 9 0 ABC GHI JKL MNO DEF TUV PQRS WXYZ
  78. REGISTER Sign up Your full name Your email Caneco The

    full name must be a valid name. Caneco2