Objavio sam već nekoliko blog postova u kojima sam koristio obične, template-driven, forme dok ću u ovom blog postu pokazati primjer korištenja reaktivne forme tzv. reactive forms.
Postavljanje aplikacije
Za početak je potrebno kreirati novu Ionic aplikaciju.
|
$ ionic start Ionic3ReaktivnaForma blank $ cd Ionic3ReaktivnaForma |
S obzirom da ćemo ovdje koristiti reaktivne forme u module moramo dodati ReactiveFormsModule. To ćemo učiniti na način da u app.module.ts dodamo
import { ReactiveFormsModule } from '@angular/forms'; .
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
|
import { BrowserModule } from '@angular/platform-browser'; import { ErrorHandler, NgModule } from '@angular/core'; import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular'; import { SplashScreen } from '@ionic-native/splash-screen'; import { StatusBar } from '@ionic-native/status-bar'; import { MyApp } from './app.component'; import { HomePage } from '../pages/home/home'; import { ReactiveFormsModule } from '@angular/forms'; @NgModule({ declarations: [ MyApp, HomePage ], imports: [ BrowserModule, ReactiveFormsModule, IonicModule.forRoot(MyApp) ], bootstrap: [IonicApp], entryComponents: [ MyApp, HomePage ], providers: [ StatusBar, SplashScreen, {provide: ErrorHandler, useClass: IonicErrorHandler} ] }) export class AppModule {} |
Kreiranje reaktivne forme
Sada smo spremni za kreiranje reaktivne forme na stranici home.html tj. home.ts.
Forma će se sastojati od tri polja (korisničko ime, e-mail i lozinka). Sada možemo kreirati izgled reaktivne forme.
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
|
<ion-header> <ion-navbar> <ion-title> Ionic3 - Reaktivna forma </ion-title> </ion-navbar> </ion-header> <ion-content padding> <form [formGroup]="korisnik" (ngSubmit)="posaljiPodatke(korisnik)"> <ion-list> <ion-item> <ion-label stacked>Korisničko ime</ion-label> <ion-input type="text" formControlName="korisnicko_ime"></ion-input> </ion-item> <ion-item> <ion-label stacked>E-mail</ion-label> <ion-input type="text" formControlName="email"></ion-input> </ion-item> <ion-item> <ion-label stacked>Lozinka</ion-label> <ion-input type="text" formControlName="lozinka"></ion-input> </ion-item> </ion-list> <button ion-button full type="submit">Spremi</button> </form> </ion-content> |
Logika će se nalaziti unutar home.ts. Ovdje imamo objekt
korisnik i funkciju
posaljiPodatke(korisnik){};.
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
|
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import { FormControl, FormGroup } from '@angular/forms'; @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { korisnik: FormGroup; constructor(public _navCtrl: NavController) { this.korisnik = new FormGroup({ korisnicko_ime: new FormControl(''), email: new FormControl(''), lozinka: new FormControl('') }); } posaljiPodatke(korisnik){ console.log(korisnik); console.log(korisnik._value); } } |
Ako sada pokrenemo aplikaciju dobit ćemo sljedeće
Iako se ovo još uvijek puno ne razlikuje od primjera formi koje smo koristili u prijašnjim blog postovima ipak se kreće u smjeru onoga što s reaktivnim formama možemo i želimo postići. Više o tome u nastavku.
Napredne mogućnosti (FormBuilder)
Kako bi unutar naše forme kasnije mogli koristiti napredne mogućnosti dodatno ćemo prilagoditi logiku unutar home.ts datoteke na način da ćemo dodati FormBuilder klasu. Ako sada pokrenemo aplikaciju rezultat će biti isti kao na slici iznad.
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
|
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import { FormControl, FormGroup, FormBuilder } from '@angular/forms'; @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { korisnik: FormGroup; constructor(public _navCtrl: NavController, private _fb: FormBuilder) { this.korisnik = this._fb.group({ korisnicko_ime: [''], email: [''], lozinka: [''] }); } posaljiPodatke(korisnik){ console.log(korisnik); console.log(korisnik._value); } } |
Proširivanje mogućnosti forme (FormArray)
Dodatna funkcionalnost naše forme stižu u obliku FormArray klase.
FormArray is one of the three fundamental building blocks used to define forms in Angular, along with FormControl and FormGroup.
Sada ćemo proširiti funkcionalnost naše forme iz primjera iznad na način da ćemo dodati mogućnost unosa adrese i kućnog broja. Posebno zanimljiva će biti mogućnost dodavanja više adresa unutar iste forme tj. kreiranje niza adresa.
Unutar postojećeg objekta
korisnik dodajemo niz
adrese. Također, dodajemo i funkcije za dodavanje nove adrese
dodajAdresu(); tj. brisanje adrese
obrisiAdresu(i);.
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
|
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import { FormControl, FormGroup, FormBuilder, FormArray } from '@angular/forms'; @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { korisnik: FormGroup; constructor(public _navCtrl: NavController, private _fb: FormBuilder) { this.korisnik = this._fb.group({ korisnicko_ime: [''], email: [''], lozinka: [''], adrese: this._fb.array([ this.dohvatiAdresu() ]) }); } dohvatiAdresu(){ return this._fb.group({ ulica: [''], broj: [''] }); } dodajAdresu(){ const control = <FormArray>this.korisnik.controls['adrese']; control.push(this.dohvatiAdresu()); } obrisiAdresu(i:number){ const control = <FormArray>this.korisnik.controls['adrese']; control.removeAt(i); } posaljiPodatke(korisnik){ console.log(korisnik._value); } } |
Prema tome i izgled forme smo morali prilagoditi. Ovdje nikako se smijemo zaboraviti dodati
type="button" za gumbe za dodavanje i brisanje adrese jer ćemo u suprotnom dobiti vrlo zanimljive nuspojave, a to ne želimo.
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
|
<ion-header> <ion-navbar> <ion-title> Ionic3 - Reaktivna forma </ion-title> </ion-navbar> </ion-header> <ion-content padding> <form [formGroup]="korisnik" (ngSubmit)="posaljiPodatke(korisnik)"> <ion-list> <ion-item> <ion-label stacked>Korisničko ime</ion-label> <ion-input type="text" formControlName="korisnicko_ime"></ion-input> </ion-item> <ion-item> <ion-label stacked>E-mail</ion-label> <ion-input type="text" formControlName="email"></ion-input> </ion-item> <ion-item> <ion-label stacked>Lozinka</ion-label> <ion-input type="text" formControlName="lozinka"></ion-input> </ion-item> <ion-list formArrayName="adrese" no-lines> <div *ngFor="let adresa of korisnik.controls.adrese.controls; let i=index"> <div [formGroupName]="i"> <ion-item> <ion-label stacked>Adresa {{i + 1}}</ion-label> <ion-input type="text" formControlName="ulica" placeholder="Ulica..."></ion-input> <ion-input type="text" formControlName="broj" placeholder="Kućni broj..."></ion-input> </ion-item> <div text-center> <button ion-button outline small (click)="obrisiAdresu(i)" type="button" color="danger">Obriši adresu</button> </div> </div> </div> <div text-center> <button ion-button outline small (click)="dodajAdresu()" type="button" color="secondary">Dodaj adresu</button> </div> </ion-list> </ion-list> <button ion-button full type="submit">Spremi</button> </form> </ion-content> |
Ako sada pokrenemo aplikaciju forma će izgledati ovako
Validacija forme
U našoj trenutnoj formi možemo u bilo koje polje unijeti bilo koju vrijednost jer nemamo nikakvu vrstu provjere tih vrijednosti. Sada ćemo to omogućiti koristeći Validators klasu.
Korisničko ime mora biti duže od 3 znaka, email mora imati ispravan oblik tj. mora sadržavati znak ‘@’, a lozinka ne smije biti duža od 9 znakova.
U našoj komponenti to izgleda ovako:
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
|
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import { FormControl, FormGroup, FormBuilder, FormArray, Validators } from '@angular/forms'; @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { korisnik: FormGroup; changeLog: string[] = []; constructor(public _navCtrl: NavController, private _fb: FormBuilder) { this.korisnik = this._fb.group({ korisnicko_ime: ['', [Validators.required, Validators.minLength(3)]], email: ['', [Validators.required, Validators.email]], lozinka: ['', [Validators.required, Validators.maxLength(9)]], adrese: this._fb.array([ this.dohvatiAdresu() ]) }); } dohvatiAdresu(){ return this._fb.group({ ulica: [''], broj: [''] }); } dodajAdresu(){ const control = <FormArray>this.korisnik.controls['adrese']; control.push(this.dohvatiAdresu()); } obrisiAdresu(i:number){ const control = <FormArray>this.korisnik.controls['adrese']; control.removeAt(i); } posaljiPodatke(korisnik){ console.log(korisnik._status); console.log(korisnik._value); } } |
Dok forma nije validna korisnik ne smije imati mogućnost slanja podataka tj. mora mu se onemogućiti klik na gumb za slanje/spremanje podatka
[disabled]="korisnik.invalid".
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
|
<ion-header> <ion-navbar> <ion-title> Ionic3 - Reaktivna forma </ion-title> </ion-navbar> </ion-header> <ion-content padding> <form [formGroup]="korisnik" (ngSubmit)="posaljiPodatke(korisnik)"> <ion-list> <ion-item> <ion-label stacked>Korisničko ime</ion-label> <ion-input type="text" formControlName="korisnicko_ime"></ion-input> </ion-item> <ion-item *ngIf="korisnik.controls.korisnicko_ime.invalid && korisnik.controls.korisnicko_ime.dirty" no-lines> <p>Korisničko ime treba biti duže od 3 znaka!</p> </ion-item> <ion-item> <ion-label stacked>E-mail</ion-label> <ion-input type="text" formControlName="email"></ion-input> </ion-item> <ion-item *ngIf="korisnik.controls.email.invalid && korisnik.controls.email.dirty" no-lines> <p>E-mail nije ispravan!</p> </ion-item> <ion-item> <ion-label stacked>Lozinka</ion-label> <ion-input type="password" formControlName="lozinka"></ion-input> </ion-item> <ion-item *ngIf="korisnik.controls.lozinka.invalid && korisnik.controls.lozinka.dirty" no-lines> <p>Lozinka ne može biti duža od 9 znakova!</p> </ion-item> <ion-list formArrayName="adrese" no-lines> <div *ngFor="let adresa of korisnik.controls.adrese.controls; let i=index"> <div [formGroupName]="i"> <ion-item> <ion-label stacked>Adresa {{i + 1}}</ion-label> <ion-input type="text" formControlName="ulica" placeholder="Ulica..."></ion-input> <ion-input type="text" formControlName="broj" placeholder="Kućni broj..."></ion-input> </ion-item> <div text-center> <button ion-button outline small (click)="obrisiAdresu(i)" type="button" color="danger">Obriši adresu</button> </div> </div> </div> <div text-center> <button ion-button outline small (click)="dodajAdresu()" type="button" color="secondary">Dodaj adresu</button> </div> </ion-list> </ion-list> <button ion-button full type="submit" [disabled]="korisnik.invalid">Spremi</button> </form> </ion-content> |
Forma sada izgleda ovako
Kreiranje prilagođenog validatora
Ovisno o potrebama moguće je napraviti i prilagođene validatore. Npr. ako pogledate validator za e-mail adresu iz gornjeg primjera možete vidjeti da on dopušta unos e-mail adrese sa nastavkom (.com) koji ima više od 3 znaka. Zato ćemo mi sada napraviti prilagođeni validator koji će takvu adresu označiti kao neispravnu.
Unutar mape Ionic3ReaktivnaForma\src\ kreirati ćemo novu mapu \validators\ sa datotekom email.ts
|
import { FormControl } from '@angular/forms'; export class EmailValidator { static isValid(control: FormControl){ const res = /^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/.test(control.value); if (res) { return null; } return {'invalidEmail': true}; } } |
Prema tome možemo prilagoditi i home.ts datoteku
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
|
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import { FormControl, FormGroup, FormBuilder, FormArray, Validators } from '@angular/forms'; import { EmailValidator } from './../../validators/email'; @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { korisnik: FormGroup; changeLog: string[] = []; constructor(public _navCtrl: NavController, private _fb: FormBuilder) { this.korisnik = this._fb.group({ korisnicko_ime: ['', [Validators.required, Validators.minLength(3)]], email: ['', [EmailValidator.isValid]], lozinka: ['', [Validators.required, Validators.maxLength(9)]], adrese: this._fb.array([ this.dohvatiAdresu() ]) }); } ... |
Validacija e-mail polja sada izgleda ovako
Bonus – provjera tijekom unosa
Još jedna od mogućnosti koju možete koristiti je i provjera unešenih podataka u polje forme u trenutku unosa. U ovom ćemo primjeru provjeravati samo polje koje se tiče unosa korisničkog imena.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
... constructor(public _navCtrl: NavController, private _fb: FormBuilder) { this.korisnik = this._fb.group({ korisnicko_ime: ['', [Validators.required, Validators.minLength(3)]], email: ['', [EmailValidator.isValid]], lozinka: ['', [Validators.required, Validators.maxLength(9)]], adrese: this._fb.array([ this.dohvatiAdresu() ]) }); const nameControl = this.korisnik.get('korisnicko_ime'); nameControl.valueChanges.forEach( (value: string) => this.changeLog.push(value) ); } ... |
Na formi ćemo to prikazati na sljedeći način:
|
... </ion-list> <button ion-button full type="submit" [disabled]="korisnik.invalid">Spremi</button> </form> <div *ngFor="let ime of changeLog">{{ ime }}</div> </ion-content> |
Na kraju to izgleda ovako:
Zaključak
Ovisno o potrebama koristiti ćete reactive ili template-driven forme. Ne postoji bolje ili lošije rješenje jer sve ovisi što vam u kojem trenutku treba.
Ako trebate napraviti jednostavniju tj. manju formu možete bez problema koristiti template-driven formu. To su forme koje sadržavaju
[(ngModel)]="objekt".
Kada počnete raditi veće i kompleksnije forme svakako se prebacite na reactive forme. Jednostavnije ih je održavati i testirati plus što se sva logika nalazi na jednom mjestu.