Tartalomjegyzék
Angular - Űrlapok érvényessége
- Szerző: Sallai András
- Copyright © 2022, Sallai András
- Szerkesztve: 2022, 2024, 2025
- Web: https://szit.hu
Kezdő projekt
Készítsünk egy új projektet valid néven.
ng new valid
Készítsünk egy login komponenst.
cd valid ng generate component login
Importálnunk kell a LoginComponent a TypeScript oldalon. A app.component.ts fájlban:
import { LoginComponent } from './login/login.component'; //... imports: [LoginComponent], //...
Építsük az app.component.html sablonba a login komponenst:
<app-login />
A login űrlap
Készítsük el a beléptető felületet.
Kezdjük a TypeScript oldalon. A login.component.ts fájlban importáljuk a FormBuilder és a ReactiveFormsModule modulokat. A ReactiveFormsModule-t a dekorátorban is importálni kell.
import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; //... imports: [ReactiveFormsModule],
Injektáljuk a FormBuilder modult a konstruktorban, hogy használható legyen:
constructor(private builder: FormBuilder) { }
Készítsük el a loginForm objektumot.
loginForm = this.builder.group({ username: [''], password: [''] })
Kezdjük a HTML sablonban egy form elemmel:
<form [formGroup]="loginForm"></form>
Egészítsük ki beviteli mezőkkel:
<div> <label for="username">Felhasználónév</label> <input type="text" id="username" formControlName="username"> </div> <div> <label for="password">Jelszó</label> <input type="password" id="password" formControlName="password"> </div>
A teljes kód:
- src/app/login/login.component.ts
import { Component } from '@angular/core'; import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; @Component({ selector: 'app-login', standalone: true, imports: [ReactiveFormsModule], templateUrl: './login.component.html', styleUrl: './login.component.css' }) export class LoginComponent { constructor(private builder : FormBuilder) {} loginForm = this.builder.group({ username: [''], password: [''] }); }
Az űrlap:
- src/app/login/login.component.html
<h1>Bejelentkezés</h1> <form [formGroup]="loginForm"> <div> <label for="username">Felhasználónév</label> <input type="text" id="username" formControlName="username"> </div> <div> <label for="password">Jelszó</label> <input type="text" id="password" formControlName="password"> </div> </form>
Állapotosztályok
A reaktív űrlap különböző állapotosztályokat ad form és input elemekhez.
<form class="ng-untouched ng-pristine ng-valid">
Minden input elem külön állapotosztályt kap:
<input class="ng-untouched ng-pristine ng-valid">
A következő táblázat alapján kideríthetjük milyen állapotban van az űrlapunk:
ng-pristine | Az űrlap még érintetlen eredeti. |
ng-dirty | Az űrlap már érintett, nem az eredeti. |
ng-touched | Egyszer érintve volt. |
ng-untouched | Már nem érintetlen |
ng-valid | Érvényes |
ng-invalid | Nem érvényes |
Indítsuk el a projektünket én nézzük meg az űrlapot a böngészőben. A böngészőben váltsunk a fejlesztői felületre, ott válasszuk az Elements fület.
<input class="form-control" ng-invalid> <--^^^^^^^^^^ -->
Érvényességhez szükséges modul
Az érvényesség ellenőrzéséhez szükségünk van a Validators modulra.
- src/app/app.module.ts
import { FormBuilder ReactiveFormsModule, Validators //<-- } from '@angular/forms';
Felhasználónév megkövetelése
Most állítsuk be a felhasználónévhez érvényességvizsgálatot.
loginForm = this.builder.group({ username: ['', Validators.required], password: [''] });
Nézzük meg most a böngésző fejlesztői felületén az input elem állapotát. Az állapotok a következők:
form:
- ng-untouched
- ng-pristine
- ng-invalid ⇦
input username:
- ng-untouched
- ng-pristine
- ng-invalid ⇦
input password:
- ng-untouched
- ng-pristine
- ng-valid
Validátorok
Használható statikus függvények a Validators osztályon.
- min()
- max()
- required()
- requiredTrue()
- email()
- minLength()
- maxLength()
- patter(sztring)
- nullValidator()
- compose()
- composeAsync()
Több információ:
CSS kiemelések
Az attribútum alapján lehet egy kis piros széle, CSS-sel. A login.component.css fájlban állítsuk be:
input.ng-invalid { border-left: 5px solid red; } input.ng-valid { border-left: 5px solid green; }
Egyszerre ng-invalid és ng-touched attribútum elvárás:
input.ng-invalid.ng-touched { border: 3px solid red; } input.ng-valid { border: 3px solid lightgreen; }
Több érvényesítő
Ha több érvényesítőt is megadunk, tegyük azokat egy újabb tömbbe (szögletes zárójelbe).
this.loginForm = this.builder.group({ username: ['', [Validators.required, Validators.minLength(3)]], password: [''] })
Kötelezőnek jelölés és email:
email: new FormControl('', [Validators.required,Validators.email])
Minta használata
name: new FormControl('', Validators.pattern('[a-z]'))
Segítő szöveg
Angular 12.x
<small *ngIf="loginForm.controls.email.invalid">
<small *ngIf="loginForm.controls.email.invalid && loginForm.controls.email.touched">
Angular 13.x
<small *ngIf="loginForm.controls['email'].invalid">
<small *ngIf="loginForm.controls['email'].invalid && loginForm.controls['email'].touched">
Angular 19.x
@if(loginForm.controls.username.invalid){ <small>Hiba</small> }
@if( loginForm.controls.username.invalid && loginForm.controls.username.touched ){ <small>Hiba</small> }
A követelmények szétválasztása
Szeretnénk külön reagálni arra az esetre, ha semmit nem írt a felhasználó, és külön arra az esetre, ha beírt ugyan valamit, de nem kevesebbet írt 3 karakternél.
@if( loginForm.controls['username'].errors?.['required'] && loginForm.controls['username'].touched ){ <small>Kötelező kitölteni</small> } @if( loginForm.controls['username'].errors?.['minlength'] && loginForm.controls['username'].touched ){ <small>Minimum 3 karakter</small> }
A get() függvénnyel:
@if( loginForm.get('username')?.errors?.['required'] && loginForm.get('username')?.touched ){ <small>Kötelező kitölteni</small> } @if( loginForm.get('username')?.errors?.['minlength'] && loginForm.get('username')?.touched ){ <small>Minimum 3 karakter</small> }
Saját érvényesítő
Egy direktívát fogunk létrehozni forbiddenName elnevezéssel, ahol az admin nevet nem engedjük meg.
ng generate directive shared/forbiddenName
A következő fájlok jönnek létre:
- src/shared/forbidden-name.directive.spec.ts
- src/shared/forbidden-name.directive.ts
A .ts fájl tartalmát írjuk át így:
- src/shared/forbidden-name.directive.ts
import { Directive } from '@angular/core'; import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; @Directive({ selector: '[appForbiddenName]', standalone: true }) export class ForbiddenNameDirective{ static validate(forbiddenName: RegExp): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { const forbidden = /admin/.test(control.value); return forbidden ? { 'forbiddenName': { value: control.value } } : null; } } }
Használat
loginForm = this.builder.group({ username: ['', [ Validators.required, Validators.minLength(3), ForbiddenNameDirective.validate(/admin/i) ]], password: [''] });
Segítő üzenet
@if( loginForm.controls['username'].errors?.['forbiddenName'] && loginForm.controls['username'].touched ){ <small>Admin nem megengedett</small> }
Lásd még
Saját érvényesítő függvénnyel
ng generate directive shared/forbiddenName
A generált állományban találunk egy előre létrehozott osztályt. Az egész állomány tartalmát törölhetjük. Új tartalom:
- src/app/shared/forbidden-name-validator.directive.ts
import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms"; export function forbiddenNameValidator(forbiddenName: RegExp): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { const forbidden = forbiddenName.test(control.value); return forbidden ? { 'forbiddenName': { value: control.value } } : null; } }
Az érvényesítő használata
import { forbiddenNameValidator } from './shared/forbidden-name-validator.directive'; //... ngOnInit() { this.employeeForm = this.formBuilder.group({ name: ['', [ Validators.required, Validators.minLength(3), forbiddenNameValidator(/admin/i) ]], city: [''], salary: [''] }) }
Megtiltjuk az admin név beírását.
HTML sablonban reagálunk az ilyen hibákra:
@if(employeeForm.controls['name'].errors?.['forbiddenName']) { <span>A név nem megengedett.</span> }
Saját érvényesítő 3
Validátor generálása:
ng generate directive shared/forbiddenNameValidator
- src/app/shared/forbidden-name-validator.directive.ts
import { Directive } from '@angular/core'; import { AbstractControl, ValidationErrors, Validator } from '@angular/forms'; @Directive({ selector: '[forbiddenNameValidator]', standalone: true }) export class ForbiddenNameValidatorDirective implements Validator { constructor() { } validate(control: AbstractControl): ValidationErrors | null { const forbidden = /admin/.test(control.value); return forbidden ? { 'forbiddenName': { value: control.value } } : null; } }
Használat:
loginForm = this.builder.group({ username: ['', [ Validators.required, Validators.minLength(3), new ForbiddenNameValidatorDirective ]],
Segítő szöveg:
@if( loginForm.controls['username'].errors?.['forbiddenName'] && loginForm.controls['username'].touched ){ <small>Admin nem megengedett</small> }
Jelszó érvényessége
Minta jelszó érvényesítéshez:
const pattern = '^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z\\d]{8,}$';
Megkövetelejük:
- legyen benne nagybetű (az első zárójeles rész)
- legyen benne kisbetű (a második zárójeles rész)
- legyen benne szám (a harmadik zárójeles rész)
- csak nagy vagy kisbetű és szám lehet (a szögletes zárójel)
- minimum 8 (a kapcsos zárójel)
- src/app/login/login.component.ts
import { Component } from '@angular/core'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; @Component({ selector: 'app-login', standalone: true, imports: [ReactiveFormsModule], templateUrl: './login.component.html', styleUrl: './login.component.css' }) export class LoginComponent { loginForm !: FormGroup; constructor( private builder: FormBuilder ) { } ngOnInit(): void { const pattern = '^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z\\d]{8,}$'; this.loginForm = this.builder.group({ name: [''], password: ['', Validators.pattern(pattern)] }); } login() { console.log(this.loginForm.value); } }
Példa ha csak kisbetűket fogadunk el, de annak kötelezően lennie kell:
Validators.pattern(/^(?=.*[a-z])[a-z]+$/)
Csak kisbetűk és számok:
Validators.pattern(/^(?=.*[a-z])(?=.*[0-9])[a-z0-9]+$/)
Vagy:
Validators.pattern(/^(?=.*[a-z0-9])[a-z0-9]+$/)
Jelszavakhoz több követelmény Validators osztállyal:
password: ['', [ Validators.required, Validators.minLength(6), Validators.maxLength(12), Validators.pattern(/^(?=.*[a-z])(?=.*[0-9])[a-z0-9]+$/) ]],
Jelszó érvényessége 2
A jelszó-érvényesítőt itt egy szimpla függvényként adjuk meg:
- src/app/signup/signup.component.html
import { Component } from '@angular/core'; import { AbstractControl, FormBuilder, FormGroup, ReactiveFormsModule, ValidatorFn, Validators } from '@angular/forms'; @Component({ selector: 'app-signup', standalone: true, imports: [ReactiveFormsModule], templateUrl: './signup.component.html', styleUrl: './signup.component.css' }) export class SignupComponent { signupForm!: FormGroup; showPassword: boolean = false; constructor(private builder: FormBuilder) {} ngOnInit() { this.signupForm = this.builder.group({ username: ['', [Validators.required, Validators.minLength(3)]], password: ['', [ Validators.required, Validators.minLength(6), this.hasNumber, this.hasUppercase, this.hasLowercase, this.hasSpecialChar ]], confirmPassword: ['', Validators.required] }, { validators: this.passwordMatchValidator }); } hasNumber(control: AbstractControl) { const value = control.value; if (value && !/\d/.test(value)) { return { number: true }; } return null; } hasUppercase(control: AbstractControl) { const value = control.value; if (value && !/[A-Z]/.test(value)) { return { uppercase: true }; } return null; } hasLowercase(control: AbstractControl) { const value = control.value; if (value && !/[a-z]/.test(value)) { return { lowercase: true }; } return null; } hasSpecialChar(control: AbstractControl) { const value = control.value; if (value && !/[!@#$%^&*(),.?":{}|<>]/.test(value)) { return { specialChar: true }; } return null; } passwordMatchValidator (control: AbstractControl) { const password = control.get('password')?.value; const confirmPassword = control.get('confirmPassword')?.value; if (password !== confirmPassword) { return { passwordMismatch: true }; } return null; } onSubmit() { if (this.signupForm.valid) { console.log(this.signupForm.value); } } togglePasswordVisibility(field: string) { this.showPassword = !this.showPassword; } }
- src/app/signup/signup.component.html
<h2>Felhasználói regisztráció</h2> <form [formGroup]="signupForm" (ngSubmit)="onSubmit()"> <div> <label for="username" class="form-label">Felhasználónév:</label> @if(signupForm.get('username')?.invalid && signupForm.get('username')?.touched) { <span class="text-danger ps-1">A felhasználónév kötelező.</span> } @if(signupForm.get('username')?.errors?.['minlength'] && signupForm.get('username')?.touched) { <span class="text-danger ps-1">A felhasználónév minimum 3 karakter hosszú.</span> } <input id="username" formControlName="username" type="text" class="form-control" /> </div> <div> <label for="password" class="form-label">Jelszó:</label> @if(signupForm.get('password')?.invalid && signupForm.get('password')?.touched) { <span class="text-danger ps-1">A jelszó nem megfelelő</span> } <div class="input-group"> <input id="password" formControlName="password" [type]="showPassword ? 'text' : 'password'" class="form-control" /> <span class="input-group-text" id="inputGroupPrepend" (click)="togglePasswordVisibility('password')"> <i class="bi bi-eye"></i> </span> </div> </div> <div> <label for="confirmPassword" class="form-label">Jelszó újra:</label> @if(signupForm.get('confirmPassword')?.invalid && signupForm.get('confirmPassword')?.touched) { <span class="text-danger ps-1">A jelszó ismétlése kötelező.</span> } @if(signupForm.errors?.['passwordMismatch'] && signupForm.touched) { <span class="text-danger ps-1">A jelszavak nem egyeznek.</span> } <div class="input-group"> <input id="confirmPassword" formControlName="confirmPassword" [type]="showPassword ? 'text' : 'password'" class="form-control" /> </div> </div> <button type="submit" class="btn btn-primary mt-3" [disabled]="signupForm.invalid" >Regisztráció</button> </form>
Kattintás
export class LoginComponent { //... onSubmit() { console.log(this.loginForm.value); } }
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()"> <div> <label for="username">Felhasználónév</label> <input type="text" id="username" formControlName="username"> </div> <div> <label for="password">Jelszó</label> <input type="password" id="password" formControlName="password"> </div> <button type="submit">Bejelentkezés</button> </form>
Submit gomb tiltása
Ha nem érvénye az űrlap nem engedélyezzük a nyomógombot.
<button type="submit" [disabled]="employeeForm.invalid">Submit</button>
Teljes példa
- src/app/employee/employee.component.css
.validation-message { min-height: 1.2rem; margin-top: 0.25rem; font-size: 0.875em; color: #dc3545; } .validation-message.hidden { visibility: hidden; }
- src/app/employee/employee.component.ts
import { Component } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; @Component({ selector: 'app-employee', standalone: true, imports: [ReactiveFormsModule], templateUrl: './employee.component.html', styleUrl: './employee.component.css' }) export class EmployeeComponent { employeeForm!: FormGroup; constructor(private builder: FormBuilder) {} ngOnInit() { this.employeeForm = this.builder.group({ name: ['', [Validators.required, Validators.minLength(3)]], city: ['', [Validators.required, Validators.minLength(3)]], salary: [0, [Validators.required, Validators.pattern('^[0-9]+$')]] }); } onSubmit() { if (this.employeeForm.valid) { console.log('Form Submitted!', { name: this.employeeForm.value.name, city: this.employeeForm.value.city, salary: this.employeeForm.value.salary }); } else { console.log('Form is invalid'); } } }
- src/app/employee/employee.component.html
<h2>Dolgozó felvétele</h2> <form [formGroup]="employeeForm" (submit)="onSubmit()"> <div> <label for="name" class="form-label">Név</label> <input id="name" formControlName="name" class="form-control"/> <div class="validation-message" [class.hidden]="employeeForm.get('name')?.valid || employeeForm.get('name')?.untouched"> @if(employeeForm.get('name')?.errors?.['required'] && employeeForm.get('name')?.touched) { <div>A név megadása kötelező.</div> } @if(employeeForm.get('name')?.errors?.['minlength'] && employeeForm.get('name')?.touched) { <div>A név minimum 3 karakter hosszú.</div> } </div> </div> <div> <label for="city" class="form-label">Település</label> <input id="city" formControlName="city" class="form-control"/> <div class="validation-message" [class.hidden]="employeeForm.get('city')?.valid || employeeForm.get('city')?.untouched"> @if(employeeForm.get('city')?.errors?.['required'] && employeeForm.get('city')?.touched) { <div>A település megadása kötelező.</div> } @if(employeeForm.get('city')?.errors?.['minlength'] && employeeForm.get('city')?.touched) { <div>A település minimum 3 karakter hosszú.</div> } </div> </div> <div> <label for="salary" class="form-label">Fizetés</label> <input id="salary" formControlName="salary" class="form-control"/> <div class="validation-message" [class.hidden]="employeeForm.get('salary')?.valid || employeeForm.get('salary')?.untouched"> @if(employeeForm.get('salary')?.errors?.['required'] && employeeForm.get('salary')?.touched) { <div>A fizetés megadása kötelező.</div> } @if(employeeForm.get('salary')?.errors?.['pattern'] && employeeForm.get('salary')?.touched) { <div>A fizetés szám kell legyen.</div> } </div> </div> <button type="submit" [disabled]="employeeForm.invalid" [class]="employeeForm.invalid ? 'btn-info' : 'btn-primary'" class="btn mt-3"> Submit </button> </form>