formularz podstawy

omówię:

- wprowadzenie do reaktywnych formularzy
- jak zbudować formularz i dodać prostą walidację
- jak połączyć formularz i jego poszczególne elementy z HTML'em

W app.componet.html dodaj następujący kod, który dodaje prawą sekcję na formularz.

<div class="main-container">
<ng-container *ngIf="!selectedCar; else carInfo">
<app-card *ngFor="let car of cars" class="card"
[identifier]="car.vin"
[title]="car.brand"
[subtitle]="car.model"
[photoSource]="car.photoSource"
[description]="'CENA: ' + car.price + ' ZŁ. VIN: ' + car.vin"
(carSelected)="carSelected($event)">
</app-card>
</ng-container>
<ng-template #carInfo>
<app-card class="info-card"
[identifier]="selectedCar.vin"
[title]="selectedCar.brand"
[subtitle]="selectedCar.model"
[photoSource]="selectedCar.photoSource"
[description]="'CENA: ' + selectedCar.price + ' ZŁ. VIN: ' + selectedCar.vin"
[isCarSelected]="!!selectedCar"
(returnToMainWindow)="resetCarSection()">
</app-card>
<mat-card class="form-card">
<mat-card-header>
<mat-card-title>Wprowadź dane klienta</mat-card-title>
</mat-card-header>
<mat-card-content>

</mat-card-content>
</mat-card>
</ng-template>
</div>

rozszerz app.componet.scss o nastepujące style

.main-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: 60px;
}

.card {
margin: 10px;
}

.info-card {
display: flex;
width: 33%;
}

.form-card {
display: flex;
width: 66%;
}

Po wyborze danego samochodu powinieneś ujrzeć następujący widok

Po prawej stronie dodaliśmy kartę. Jest to miejsce w którym będziemy budować formularz. Komponenty które wykorzystamy z Angular Material to input oraz checkbox. Dodaj do app.module.ts moduły: MatInputModule, MatCheckboxModule, ReactiveFormsModule, FormsModule.

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';

import {AppComponent} from './app.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {MatButtonModule, MatCardModule, MatCheckboxModule, MatInputModule} from '@angular/material';
import {CardComponent} from './card/card.component';
import {HttpClientModule} from '@angular/common/http';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';

@NgModule({
declarations: [
AppComponent,
CardComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
MatCardModule,
MatButtonModule,
HttpClientModule,
MatInputModule, // <----
MatCheckboxModule, // <----
ReactiveFormsModule, // <----
FormsModule, // <----
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}

Do budowania formularza skorzystamy z tzw. reaktywnych formularzy. Podejście to cechuje się tym, że konstrukcja formularza, dodanie walidacji oraz zarządzanie formularzem odbywa się w komponencie/serwisie a nie w HTML’u. 

Dodaj folder form w katalogu app oraz stwórz serwis form.service.ts.

import {Injectable} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';

@Injectable({
providedIn: 'root'
})
export class FormService {

constructor(private formBuilder: FormBuilder) {
}

public createClientFormGroup(): FormGroup {
return this.formBuilder.group({
name: ['', Validators.required],
businessInMonths: ['', Validators.required],
isUnder25: [''],
licenseInMonths: ['', Validators.required],
});
}
}

Zbudowany formularz jest typu FormGroup i posiada wewnątrz siebie kilka elementów. Każdy z elementów jest typu FormControl oraz może posiadać walidację. W tym przypadku wykorzystamy tylko validator required.

Przejdź do app.component.ts i dodaj następujący kod.

import {Component, OnInit} from '@angular/core';
import {CarService} from './car/car.service';
import {FormService} from './form/form.service';
import {FormGroup} from '@angular/forms';
import {Car} from './car/car.interface';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
selectedCar: Car;
cars: Car[] = [];
clientForm: FormGroup; // 1

public ngOnInit(): void {
this.carService.fetchAvailableCars().subscribe(cars => {
this.cars = cars;
});

this.clientForm = this.formService.createClientFormGroup(); // 3
}

constructor(private carService: CarService, private formService: FormService) { // 2
}

public carSelected(vin: string): void {
this.selectedCar = this.cars
.find(car => car.vin === vin);
}

public resetCarSection() {
this.selectedCar = null;
}

public countDiscount() { // 4
console.log(this.clientForm.controls);
}
}

Dodaliśmy tutaj 

1) zmienną  clientForm, która będzie przetrzymywała referencję do formularza 

2) wstrzyknięcie serwisu form.service

3) przypisanie formularza do zmiennej clientForm

4) countDiscount() która będzie zwracała wartość zniżki po wypełnieniu formularza. W chwili obecnej po naciśnięciu przycisku wyświetlimy obiekt controls z formularza.

Warto by było zaprezentować formularz w szablonie HTML. Przejdź do app.component.html  i dodaj poniższy kod.

<div class="container">
<div class="card-container" *ngIf="!selectedCar; else carInfo">
<app-card *ngFor="let car of cars" class="card"
[identifier]="car.vin"
[title]="car.brand"
[subtitle]="car.model"
[photoSource]="car.photoSource"
[description]="'CENA: ' + car.price + ' ZŁ. VIN: ' + car.vin"
(carSelected)="carSelected($event)">
</app-card>
</div>
<ng-template #carInfo>
<div class="card-container">
<app-card class="info-card-form"
[identifier]="selectedCar.vin"
[title]="selectedCar.brand"
[subtitle]="selectedCar.model"
[photoSource]="selectedCar.photoSource"
[description]="'CENA: ' + selectedCar.price + ' ZŁ. VIN: ' + selectedCar.vin"
[isCarSelected]="!!selectedCar"
(returnToMainWindow)="resetCarSection()">
</app-card>

<mat-card class="form-card">
<mat-card-header>
<mat-card-title>Wprowadź dane klienta</mat-card-title>
</mat-card-header>
<mat-card-content>
<form [formGroup]="clientForm" (submit)="countDiscount()"> <!--1-->

<mat-form-field appearance="outline">
<mat-label>Nazwa klienta</mat-label>
<label>
<input matInput formControlName="name"/> <!--3-->
</label>
</mat-form-field>

<mat-form-field appearance="outline">
<mat-label>Długość prowadzenia firmy ?</mat-label>
<label>
<input matInput formControlName="businessInMonths"/>
</label>
</mat-form-field>

<mat-form-field appearance="outline">
<mat-label>Długość posiadanego prawa jazdy ?</mat-label>
<label>
<input matInput formControlName="licenseInMonths"/>
</label>
</mat-form-field>

<mat-checkbox formControlName="isUnder25">Kierowca poniżej 25 roku ?</mat-checkbox>

<button type="submit" mat-raised-button> <!--2-->
Oblicz zniżkę
</button>
</form>

</mat-card-content>
</mat-card>
</div>
</ng-template>
</div>

1) w tym punkcie dodaliśmy dwie rzeczy. Po pierwsze bindowanie formularza poprzez przypisanie do [formGroup] obiektu clientForm z komponentu. Po drugie przypisanie do zdarzenia submit metody countDiscount()

2) aby powyższe przypisanie na zdarzeniu submit zadziałało poprawnie, niezbędne jest zdefiniowanie w obrębie forma przycisku, który będzie typu submit

3) do połączenia poszczególnego formControl, który został zdefiniowany w form.service.ts (np. name, businnesInMonths itd.) z szablonem HTML wykorzystujemy elemnet formControlName

Po wybraniu poszczególnego auta powinieneś zobaczyć po prawej stronie formularz. Stylowaniem oraz rozbudową funkcjonalności formularza zajmiemy się w kolejnej lekcji.

Po kliknięciu przycisku Oblicz zniżkę w konsoli deweloperskiej powinieneś zobaczyć zawartość poszczególnych formControls z formularza. Rozwiń objekt a następnie rozwiń elemnt name. Wartość poszczególnych formControls jest przechowywana w zmiennej value.