Felhasználói eszközök

Eszközök a webhelyen


oktatas:web:angular:angular_urlapok_ervenyessege

< Angular

Angular - Űrlapok érvényessége

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: ['']
    })

E-mail

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>

Források

oktatas/web/angular/angular_urlapok_ervenyessege.txt · Utolsó módosítás: 2025/09/28 17:59 szerkesztette: admin