Պետք չէ օգտագործել վերահսկվող մուտքեր կամ ռեֆերատներ

Երբ կարդում եք React-ում ձևերի մշակման վերաբերյալ պաշտոնական փաստաթղթերը, հանդիպում եք վերահսկվող մուտքերի օգտագործման օրինակների: Սա մարդկանց մեծամասնությանը տալիս է պատկերացում, որ դա այն ձևն է, որով դուք պետք է աշխատեք React-ի ձևերի հետ: Այնուամենայնիվ, եթե էջը շրջում եք նույն փաստաթղթի Չվերահսկվող բաղադրիչներ բաժնում, էջի ենթավերնագրում մեծ տառերով հանդիպում եք այս հայտարարությանը.

Շատ դեպքերում մենք խորհուրդ ենք տալիս օգտագործել վերահսկվող բաղադրիչներ՝ ձևաթղթերի իրականացման համար:

Եվ ես նույնպես: Ես շարունակում եմ տեսնել, թե ինչպես են մարդիկ ընկնում, նորից ու նորից, առաջին բանը, որին նրանք հանդիպում են React-ի փաստաթղթերը կարդալիս: Այնուամենայնիվ, փաստաթղթերի երկրորդ մասը, չնայած մեծ բարելավմանը, նույնպես այնքան էլ հիանալի չէ: Օրինակում ցույց տրված ձևը ամենևին էլ ամենապարզը չէ, և մի փոքր հնաոճ վեբ API-ի լավ իմացության շնորհիվ այն կարելի է դարձնել էլ ավելի պարզ (և հեշտ):

Այս հոդվածում ես պատրաստվում եմ ցույց տալ, թե ինչու ձեզ հարկավոր չեն վերահսկվող մուտքեր կամ ռեֆերատներ՝ ձևերի հետ գործ ունենալու համար, և ես նույնիսկ կներկայացնեմ որոշ հավելյալներ՝ ձևերի մշակման որոշ այլ ասպեկտներ ավելի պարզեցնելու և գուցե նաև մի փոքր ավելի հեշտ դարձնելու համար: նույնպես։

Նշենք, որ այս տեխնիկան կարող է օգտագործվել ցանկացած շրջանակում կամ նույնիսկ առանց դրա: Ես այստեղ ներկայացնում եմ միայն React օրինակ, քանի որ դա այն է, ինչին շատերը ծանոթ կլինեն:

Այսպիսով, եկեք սկսենք:

Ձևաթղթի մատուցում

Եկեք սկսենք մեր ձևը ներկայացնելով: Դա կլինի պարզ ձև երեք դաշտերով: Տիպիկ գրանցման ձև՝ անուն, էլ. փոստ և գաղտնաբառ դաշտերով: Մենք չենք ներառի «կրկնել գաղտնաբառի» դաշտը, այլ փոխարենը կներդրենք գաղտնաբառի ցուցադրման կոճակը, որը լուծում է այն խնդիրը, որն ի սկզբանե լուծվել է կրկնվող գաղտնաբառի դաշտում. օգտվողը սխալ է մուտքագրում գաղտնաբառը: Ձեզ երկուսն էլ պետք չեն, ուշադրություն դարձրեք:

import React, { Component } from 'react'
import * as ReactDOM from 'react-dom/client'

let
  App = class extends Component {
    render() {
      return (
        <form>
          <label>
            Name:
            <input type="text" name="name" required />
          </label>
          <label>
            Email:
            <input type="email" name="email" required />
          </label>
          <label>
            Password:
            <input type="pasword" name="password" minLength="8" required />
          </label>
          <button>
            Sign up
          </button>
        </form>
      )
    }
  }

ReactDOM.createRoot(document.getElementById('app')).render(<App />)

Մենք այստեղ օգտագործում ենք դասի բաղադրիչ, բայց այն աշխատում է ճիշտ նույն կերպ ֆունկցիայի բաղադրիչների դեպքում: Ես կթողնեմ ձեզ, իմ սիրելի ընթերցող, կատարել անհրաժեշտ փոխարկումները:

Նշումը բավականին պարզ է: Մենք օգտագործում ենք նորմալ <form> թեգը՝ ձևի վերահսկիչները խմբավորելու համար: Ձևի շրջանակներում մենք ունենք երեք դաշտ. Յուրաքանչյուր դաշտ տեքստի հետ կցվում է <label> պիտակի մեջ: (Եթե չգիտեիք, <input><label>-ում կցելով պիտակը կապում է մուտքագրման հետ՝ առանց հստակորեն դա անելու՝ օգտագործելով id և for ատրիբուտները:) Նկատի ունեցեք, որ յուրաքանչյուր մուտքագրին տրվում է name հատկանիշ, որպեսզի մենք կարողանանք դրանք ավելի ուշ ճանաչել:

Քանի որ դրանք վերահսկվող բաղադրիչներ չեն, մենք չունենք value հատկանիշը (prop) ոչ մի դաշտում: DOM-ն ինքը կօգտագործվի որպես ճշմարտության միակ աղբյուր:

Ներկայացման գործիքավորումը

Այժմ եկեք գործիքավորենք ձևի ներկայացումը տվյալների արդյունահանման համար:

let
  App = class extends Component {
    handleSubmit = ev => {
      let $form = ev.target

      ev.preventDefault()

      fetch('/api/v1/signup/', {
        method: 'POST',
        headers: {'content-type': 'application/json'},
        body: JSON.stringify(
          Object.fromEntries(new FormData($form).entries())
        ),
      })
        .then(() => $form.reset())
    }

    render() {
      return (
        <form onSubmit={this.handleSubmit}>
          {/* no changes here */}
        </form>
      )
    }
  }

Մենք ստեղծում ենք ավտոմատ կապի մեթոդ handleSubmit: Սա համեմատաբար նոր շարահյուսություն է, որը հասնում է նույն բանին, ինչ մենք անում էինք անցյալում.

let
  App = class extends Component {
    constructor(props) {
      super(props)
      this.handleSubmit = this.handleSubmit.bind(this)
    }

    handleSubmit(ev) {
      // ...
    }
  }

Մի շփոթվեք դոլարի նշանով $ փոփոխականի անվան տակ: Այդ նիշը վավեր նիշ է փոփոխականների անուններում: Ես պարզապես սիրում եմ DOM հանգույցի հղումներ պահող փոփոխականները նախածանց դնել դոլարի նշանով, որպեսզի դրանք տարբերեմ այլ փոփոխականներից, դա բոլորն է:

Մեր նշագրման հիման վրա ձևը կկատարի GET հարցում ընթացիկ URL-ին (URL հասցեի տողում) և մուտքագրված արժեքները կվերափոխի հարցման տողի: Օրինակ՝ /?name=test&email=test&password=test: Սա կանխելու համար զանգում ենք ev.preventDefault()ին: Դուք միայն դա պետք է անեք, եթե դուք պատրաստվում եք ձևը ներկայացնել՝ օգտագործելով fetch(), ինչպես այս օրինակում:

Իրադարձության օբյեկտը (ev այս օրինակում) պարունակում է target հատկությունը, որը ցույց է տալիս այն տարրը, որի վրա գործարկվել է իրադարձությունը (ձևը): Այսպիսով, ձևի տվյալները ստանալու համար ձևի հղումներ պահելու կարիք չկա: Մենք կարող ենք հանել բոլոր մուտքերի արժեքները՝ օգտագործելով FormData կոնստրուկտորը: Այս կոնստրուկտորը կընդունի ձևի տարրը որպես մուտքագրում և կվերադարձնի օբյեկտ, որը կարող է օգտագործվել ձևի ցանկացած դաշտի արժեքներին մուտք գործելու համար: entries() մեթոդը մեզ կտա մուտքագրվող անունների և դրանց արժեքների կրկնվող ցուցակը նույն ձևաչափով, ինչ Object.entries(): Այսպիսով, մենք կարող ենք օգտագործել Object.fromEntries()-ը՝ դրանից օբյեկտ կառուցելու համար:

Մնացած մեթոդը ինքնին բացատրելի է, ուստի ես դրան չեմ խորանա: Ես պարզապես նշեմ, որ մենք կանչում ենք $form.reset()-ը հաջող ներկայացումից հետո (դե, մենք իսկապես չենք ստուգում, թե արդյոք այն հաջող է, բայց դա դուրս է այս հոդվածի շրջանակներից): Մենք կարող ենք դա անել անվտանգ այստեղ, քանի որ մենք գործ չունենք վերահսկվող մուտքերի հետ: Դրանով մենք վերականգնում ենք ձևի դաշտերը իրենց սկզբնական վիճակին (դատարկ): Լավ գաղափար է զրոյացնել ձևը ներկայացնելուց հետո, եթե ձեր ձևը միշտ DOM-ում է և միայն թաքնված կամ ցուցադրվում է անհրաժեշտության դեպքում: Մենք չենք ցանկանում, որ հնացած տեղեկատվությունը մնա մուտքերում:

Որպես կողմնակի նշում, այստեղ պետք է դիտարկել մի բան այն է, թե որքան ենք մենք հասել միայն HTML-ով (լավ, դա բառացիորենկլինի պարզապես HTML, եթե մենք չօգտագործեինք React, բայց դուք հասկանում եք իմ տեսակետը): Այն ամենը, ինչ JavaScript-ը իրականում պետք է աներ՝ կցել իրադարձությունների մշակիչ և ձևի տվյալները փոխակերպել JSON-ի` օգտագործելով կիսագծեր: Սա Նվազագույն իշխանության կանոնի էությունն է, և լավ պատճառ կա, որ այն ներկայացված է W3C կայքում: (Մենք կձևացնենք, որ React-ը, լավ, տեխնիկապես, նրա օգտվողները, սկզբից չի խախտում այս կանոնը, շշշշ 🤐):

Ինչ վերաբերում է մյուս իրադարձություններին:

Մինչ ավելի հեռուն գնալը, ես կանդրադառնամ սովորական սխալին, որը մարդիկ թույլ են տալիս ձևաթղթերը կիրառելիս:

Կարիք չկա ստեղնաշարի իրադարձությունների ունկնդիրներին կցել մուտքագրմանը կամ սեղմել իրադարձությունների ունկնդիրներին ուղարկել կոճակը: Եվ նաև կարիք չկա օգտագործել որևէ այլ բան, բացի <button>-ից, որպես ուղարկելու կոճակ, խնդրում ենք մի օգտագործեք <div>-ը:

Եթե ​​դուք օգտագործում եք <form> պիտակը ձեր մուտքերի շուրջ և այնտեղ կա <button> (իսկական, խնդրում եմ), դուք անվճար կստանաք հետևյալը.

  • Եթե ​​սեղմեք Մուտքագրեք որևէ մուտքագրում, ձևը կներկայացվի:
  • Եթե ​​սեղմեք կամ այլ կերպ ակտիվացնեք ուղարկել կոճակը, ձևը կներկայացվի:
  • Հեշտ ռեժիմը միացված է ձևի տվյալների որոնման համար:

Կրկին, դուք ստանում եք այս բաները անվճար: Ոչ մի տող կցված չէ: Ձեզ անհրաժեշտ է միայն ուղարկել ներկայացման մշակիչը <form> տարրին կցել, և ներկայացման մանրակրկիտ աշխատանքը դիտարկվում է ձեր փոխարեն:

Մենք նաև կարիք չունենք փոփոխության իրադարձության ունկնդիրներին կցել առանձին մուտքերի կամ նույնիսկ ձևի վրա և հետևել մուտքային արժեքների փոփոխություններին: Հիշեք, որ մենք խոսում ենք չվերահսկվողմուտքերի մասին: Կա մեկ բացառություն, և մենք դրան կանդրադառնանք հաջորդ բաժնում:

Ձևի վավերացում

Մեկ այլ հատկանիշ, որը մենք ստացել ենք անվճար, ձևի վավերացումն էր: Օգտագործելով այնպիսի ատրիբուտներ, ինչպիսիք են required և minLength, մենք կանխում ենք ձևի նույնիսկ ներկայացումը, եթե մուտքերը չեն բավարարում այդ սահմանափակումները: Մինչև մեր handleSubmit կոդը կանչելը, մենք կարող ենք վստահ լինել, որ բոլոր դաշտերը ճիշտ են լրացված:

Սա կոչվում է սահմանափակման վավերացում, և դրա հետ մենք կարող ենք շատ ավելին անել: Բացի մի շարք ներկառուցված սահմանափակումներից, մենք կարող ենք – օգտագործելով մի փոքր JavaScript – հարմարեցնել սխալի հաղորդագրությունները և նույնիսկ ավելացնել նոր սահմանափակումներ:

Եկեք մի փոքր համեմենք բաները հատուկ գաղտնաբառի վավերացմամբ: Հենց հիմա մենք վավերացնում ենք գաղտնաբառը երկարության համար (8 նիշ կամ ավելի): Որպեսզի գաղտնաբառը վավեր համարվի, մենք ցանկանում ենք, որ այն նաև պարունակի առնվազն մեկ մեծատառ, մեկ փոքրատառ, թիվ և հատուկ նիշ: (Արդյոք դա իրականում գաղտնաբառն ավելի անվտանգ է դարձնում, բոլորովին այլ թեմա է:)

Մենք կօգտագործենք սահմանափակման վավերացման API-ը, որը թույլ է տալիս միանալ նույն վավերացման համակարգին, որն օգտագործվում է բնօրինակ ձևի մուտքագրման միջոցով:

let
  VALID_CHARS_CONDITIONS = [
    /[a-z]/, // lower-case
    /[A-Z]/, // upper-case
    /[0-9]/, // number
    /[`~!@#$%^&*()\-_=+\[\];:'"\\|,<.>/?]/, // special character
  ],
  App = class extends Component {
    handlePasswordChange = ev => {
      let
        $passwordInput = ev.target
        password = $passwordInput.value,
        isValidPasswordCharacterMix = VALID_CHARS_CONDITIONS.every(r => r.test(password))

      if (!isValidPasswordCharacterMix) $passwordInput.setCustomValidity('Please use at least one lower-case and upper-case letter, a number, and a special character')
      else $passwordInput.setCustomValidity('')
    }

    render() {
      return (
        {/* .... */}
          <label>
            Password:
            <input type="pasword" name="password" minLength="8" required 
                   onChange={this.handlePasswordChange}/>
          </label>
        {/* .... */}
      )
    }
  }

Եկեք սա փոքր-ինչ քանդենք:

Նախ, մենք ունենք նոր փոփոխական՝ VALID_CHARS_CONDITIONS, որը պարունակում է կանոնավոր արտահայտությունների մի շարք, որոնք օգտագործվում են գաղտնաբառը վավերացնելու համար: Գաղափարն այն է, որ այս կանոնավոր արտահայտությունները պետք է կիրառվեն գաղտնաբառի վավերացման համար:

handlePasswordChange իրադարձության ունկնդիրում մենք նախ ստանում ենք հղումը գաղտնաբառի դաշտին, և մենք փորձարկում ենք VALID_CHARS_CONDITIONS արտահայտությունները՝ տեսնելու, թե արդյոք դրանք բոլորն անցնում են: Եթե ​​արտահայտություններից գոնե մեկը բացասական է, ապա մենք սահմանում ենք հատուկ սխալի հաղորդագրություն (և ես գիտեմ, թե որքան հիմար է դա հնչում հետհամաճարակային ժամանակներում): Հակառակ դեպքում մենք վերակայում ենք հատուկ վավերականությունը դատարկ տողի՝ ոչ մի սխալ:

MDN-ի վերաբերյալ փաստաթղթերն ասում են տարրի վրա reportValidity() կանչելու անհրաժեշտության մասին: Այստեղ դա անհրաժեշտ չէ, քանի որ դա մեզ համար ավտոմատ կերպով արվում է, երբ ձևը ներկայացվում է: Դա անհրաժեշտ է միայն այն դեպքում, երբ մենք ցանկանում ենք, որ սխալի հաղորդագրությունն անմիջապես ցուցադրվի:

Ցույց տալ/թաքցնել գաղտնաբառը

Օգտագործողները երբեմն կարող են սխալ մուտքագրել գաղտնաբառը: Դա կարող է մեծ անհանգստություն առաջացնել՝ հասկանալու համար, որ գրանցվելուց անմիջապես հետո ձեր գաղտնաբառը չի աշխատում: Նախկինում մենք ունեինք «կրկնել գաղտնաբառը» դաշտը, որտեղ մենք նորից մուտքագրում էինք նույն գաղտնաբառը՝ համոզվելու համար, որ դրանք համընկնում են: Ժամանակի ընթացքում դիզայներներն ու մշակողները ավելի լավ լուծում են գտել՝ պարզապես ցույց տալ պարզ տեքստային գաղտնաբառը:

Գաղափարը պարզ է՝ փոխել մուտքագրման տեսակը: Բայց կա մի փոքր բռնում. Ձևաթղթի ներկայացման ժամանակ մենք պետք է վերադարձնենք մուտքագրման տեսակը գաղտնաբառին, հակառակ դեպքում մուտքագրման արժեքը կավելացվի բրաուզերի ավտոմատ լրացման ցանկում՝ հնարավոր բացահայտելով այն, որտեղ այն չպետք է լինի:

let
  App = class extends Component {
    constructor(props) {
      super(props)
      this.state = { passwordShown: false }
    }

    toggleShowPassword = () => {
      this.setState(state => ({ passwordShown: !state.passwordShown }))
    }

    handleSubmit = ev => {
      let $form = ev.target

      ev.preventDefault()

      $form.elements.password.type = 'password'
      this.setState({ passwordShown: false })

      // ...
    }

    render() {
      let { passwordShown } = this.state
      return (
        <>
          {/* .... */}
            <div>
              <label>
                Password:
                <input type={passwordShown ? 'text' : 'password'} 
                       name="password" minLength="8" required 
                       onChange={this.handlePasswordChange}/>
              </label>
              <button type="button" onClick={this.toggleShowPassword}>
                {passwordShown ? 'Hide' : 'Show'} password
              </button>
            </div>
            {/* .... */}
          </form>
          <p aria-live="polite">
            {passwordShown 
              ? 'Your password is shown and audible to people around you.' 
              : ''}
          </p>
        </>
      )
    }
  }

Այս անգամ մենք մի փոքր վիճակ ենք մտցնում խառնուրդի մեջ: Մենք օգտագործում ենք Boolean passwordShown դրոշակ, որը true է, երբ գաղտնաբառը պետք է լինի… լավ, ցուցադրված է: Այս դրոշն օգտագործվում է գաղտնաբառի մուտքագրման տեսակը դինամիկ փոխելու համար:

Գաղտնաբառի դաշտի կողքին մենք ներառում ենք այս դրոշը փոխելու կոճակ: Նկատի ունեցեք, որ կոճակը գտնվում է դրսում պիտակի վրա, որպեսզի այն չընթերցվի որպես պիտակի մաս էկրանի ընթերցողներին: Նշեք type="button" հատկանիշը <button> թեգի վրա: Այս հատկանիշի լռելյայն արժեքը "submit" է, որն այն դարձնում է ուղարկել կոճակ, որն օգտագործվում է ձևը ներկայացնելու համար: Եթե ​​ցանկանում ենք ավելացնել կոճակ, որն ապահովում է որոշ լրացուցիչ հնարավորություններ, բայց չի ներկայացնում ձևը, մենք պետք է օգտագործենք type="button":

Ներկայացման մշակիչում մենք վերադարձնում ենք գաղտնաբառի դաշտի տեսակը password: Գաղտնաբառի մուտքագրման եղանակը ձևի elements հատկության միջոցով է, որը HTMLFormControlCollection օբյեկտ է: Այն ունի հատկություններ, որոնք համապատասխանում են name հատկանիշին մուտքերում, այնպես որ մենք կարող ենք մուտք գործել այդ մուտքերը անունով: Մենք նաև թարմացնում ենք passwordShown դրոշը՝ DOM-ի փոփոխությանը համապատասխանելու համար: Մենք չենք կարող պարզապես թարմացնել վիճակը՝ առանց DOM-ի թարմացման, սակայն, քանի որ վիճակի փոփոխությունները կիրառվում են ասինքրոն: Մեզ անհրաժեշտ է type հատկանիշը՝ անմիջապես թարմացնելու համար, այնպես որ մենք պետք է դա անենք ձեռքով և թարմացնենք վիճակը՝ պարզապես այն համաժամեցված պահելու համար:

Վերջապես, էջի հենց ներքևում մենք ունենք <p> թեգ, որը պարունակում է «Ձեր գաղտնաբառը ցուցադրվում և լսելի է ձեր շրջապատի մարդկանց» տեքստը, երբ գաղտնաբառը ցուցադրվում է: Այս տարրն ունի aria-live="polite" հատկանիշ, որը հուշում է էկրանի ընթերցողին կարդալ տեքստը, երբ տարրի բովանդակությունը փոխվում է: CSS-ում մենք թաքցնում ենք այս տարրը, որպեսզի այն միայն կարդացվի, բայց ոչ մի տեղ չցուցադրվի:

p[aria-live] {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
  pointer-events: none;
}

Ամեն անգամ, երբ մենք ունենք կոճակ կամ որևէ այլ հսկողություն, որը փոխում է UI-ի վիճակը, մենք պետք է հիշենք, որ ոչ տեսողական օգտատերերը կարող են չգիտեն, թե ինչ է արել կոճակը, մինչև չվերադառնան ազդակիր տարրին: Նման դեպքերում օգտակար է թաքնված կենդանի տարածաշրջանի միջոցով էֆեկտի մասին հայտարարելը:

Սխալի վիճակի ձևավորում

Սովորական է սխալներով մուտքագրել տարբեր ոճավորումներ: Մենք պարզ կպահենք այս օրինակում և պարզապես ձևի դաշտերը ներկենք կարմիրով:

Մինչև ձևը ձևավորելը, այնուամենայնիվ, մենք պետք է որոշենք, թե երբ կիրառել ոճավորումը: Լռելյայնորեն ձևի բոլոր դաշտերը տեխնիկապես անվավեր են: Եթե ​​մենք պարզապես օգտագործենք :invalid կեղծ ընտրիչը, նրանք կսկսվեն անվավեր վիճակում: Դրա պատճառով մենք կմշակենք invalid իրադարձությունը ձևի վրա, որն առաջին անգամն է, որ դաշտերը վավերացվում են սկզբնավորումից հետո, և կնշենք դաշտերը որպես պատրաստ՝ ցույց տալու անվավեր վիճակը:

let
  App = class extends Component {
    handleInvalid = ev => {
      ev.target.dataset.invalid = true
    }

    handleSubmit = ev => {
      let $form = ev.target

      ev.preventDefault()

      for (let $ of $form.elements) delete $.dataset.invalid
      // ...
    }

    render() {
      let { passwordShown } = this.state
      return (
        <>
          <form onSubmit={this.handleSubmit} onInvalid={this.handleInvalid}>
            {/* ... */}
          </form>
        </>
      )
    }
  }

Մենք ավելացնում ենք handleInvalid իրադարձությունների ցանկը: Այս ունկնդիրը կկանչվի յուրաքանչյուր մուտքագրման համար, որը չի վավերացվում, չնայած այն կցված է ձևին: Հետևաբար, ev.target-ը մատնանշում է մուտքը, ոչ թե ձևը: Մենք սահմանել ենք dataset.invalid հատկությունը մուտքի վրա, երբ կարգավորիչը կանչվում է:

Ներկայացման մշակիչում մենք հեռացնում ենք dataset.invalid-ը բոլոր մուտքերից՝ կրկնելով $form.elements օբյեկտը: Ինչպես նախկինում նշեցի, սա HTMLFormControlsCollection օբյեկտ է և կրկնվող է:

Հաջորդը, եկեք նայենք CSS-ին.

input[data-invalid]:invalid {
  background: #ffd6d6;
  border-color: #ff0000;
}

Ընտրիչը կիրառվում է ցանկացած մուտքագրման համար, որն ունի data-invalid հատկանիշ (մեր կոդում սահմանված է dataset.invalid հատկության միջոցով) և նաև անվավեր վիճակ է (:invalid կեղծ ընտրիչ): Քանի որ data-invalid հատկանիշն ուղարկվում է միայն առաջին ներկայացումից հետո և ակտիվ է մնում մինչև բոլոր դաշտերը չանցնեն վավերացումը և ձևը չներկայացվի, սխալների վիճակը չի ցուցադրվում առաջին ներկայացումից առաջ: Հետագայում, եթե օգտատերը շտկում է սխալները, ոճավորումը դադարում է կիրառել, քանի որ :invalid կեղծ ընտրիչն այլևս չի կիրառվում, թեև data-invalid հատկանիշը դեռ կա:

Եզրակացություն

React-ը մեզ իսկապես որևէ առավելություն չի տալիս, երբ խոսքը վերաբերում է ձևերի հետ գործ ունենալուն: Նույնիսկ պաշտոնական փաստաթղթերը խորհուրդ են տալիս պարզապես օգտագործել ձևերը այնպես, ինչպես կան: (Հեռացվել է երկար զրպարտությունն այն մասին, թե ինչպես դա չի սահմանափակվում ձևերով:) Ինչպես տեսնում եք օրինակի կոդում, սակայն, այդ ամենի հետ աշխատելը բավականին հեշտ է:

Այնուամենայնիվ, մի մոռացեք ստուգել կապակցված փաստաթղթերը այս հոդվածում օգտագործված API-ների մասին լրացուցիչ տեղեկությունների համար: Շատ ավելին կարող եք անել տարբեր ինտերֆեյսներով, որոնք զննարկիչը բացահայտում է ձևերի և սահմանափակումների վավերացման շուրջ:

Դուք կգտնեք այս օրինակի լիարժեք գործող օրինակ GitHub-ում: Ազատորեն շրջվեք, և եթե գտնում եք այն բարելավելու միջոց, տեղեկացրեք ինձ մեկնաբանություններով կամ PR-ով: 😉