CZY OBIEKT ISTNIEJE?

check-small

omówię:

- Type Script vs Java: różnice związanie ze sprawdzeniem czy obiekt lub zmienna w obiekcie istnieje
- sprawdzenie czy obiekt istnieje: klasa vs szablon HTML

w katalogu app stwórz plik address.ts

export class Address {
constructor(public streetName: string,
public streetNumber: number,
public buildingNumber?: number) {
}
}

w tym samym katalogu dodaj plik car.ts

import {Address} from './address';

export class Car {
constructor(public brand: string,
public model: string,
public engineType: string,
public type: string,
public address?: Address) {}
}

dodaj także plik car.service.ts

import { Injectable } from '@angular/core';
import {Car} from './car';
import {Address} from './address';

@Injectable(
{
providedIn: 'root'
}
)
export class CarService {
private _cars: Car[] = this.prepareCars();

private prepareCars(): Car[] {
const cars: Car[] = [];
const address = new Address('Audicowa', 5, 2);
const car = new Car('Audi', 'A6', 'benzyna', 'sedan', address);
cars.push(car);

const address1 = new Address('Mercedesowa', 7);
const car1 = new Car('Mercedes', 'Klasa E', 'benzyna', 'sedan', address1);
cars.push(car1);

const car2 = new Car('BMW', 'Seria 5', 'benzyna', 'coupe');
cars.push(car2);

return cars;
}

// metoda która udaje getter
public getCars(): Car[] {
return this._cars;
}

// prawdziwy getter w TS
public get cars(): Car[] {
return this._cars;
}
}

Zanim przejdziemy dalej zwróć uwagę, że stworzyliśmy dwie metody. Jedna z nich to getCars która getterem nie jest i druga get cars która getterem jest. Jest to drobna różnica w stosunku do Javy. 

wstrzyknij car.service.ts w pliku app.componet.ts

import {Component, OnInit} from '@angular/core';
import {CarService} from './car/car.service';
import {Car} from './car/car';

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

constructor(private carService: CarService) {
}

ngOnInit(): void {
this.cars = this.carService.getCars();
}
}

następnie w pliku app.component.html wyświetl wszystkie samochody przy użyciu pętli ngFor

<div *ngFor="let car of cars">
{{car.brand}}
{{car.model}}
{{car.type}}
{{car.engineType}}
{{car.address.streetName}}
{{car.address.streetNumber}}
{{car.address.buildingNumber}}
</div>

Gdy przejdziesz do aplikacji localhost:4200 zobaczysz, że wyświetliły się tylko dwa samochody. Kliknij F12 i przejdź do zakładki console. Zobaczysz, że pojawiły się błędy w konsoli. Dzieje się tak ponieważ chcemy wyświetlić obiekt którego nie ma. Nie zabezpieczyliśmy się w żaden sposób przed taką sytuacją.

Są dwie możliwości aby uniknąć takiej sytuacji. Pierwsza z nich to sprawdzenie poprzez ngIf czy obiekt address istnieje. 

Druga opcja to sprawdzenie przy każdym odwołaniu czy obiekt address istnieje poprzez znak zapytania.

<p>Rozwiązanie pierwsze z ngIf</p>
<div *ngFor="let car of cars">
{{car.brand}}
{{car.model}}
{{car.type}}
{{car.engineType}}
<ng-container *ngIf="car.address">
{{car.address.streetName}}
{{car.address.streetNumber}}
{{car.address.buildingNumber}}
</ng-container>
</div>

<p>Rozwiązanie drugie ze znakiem zapytania</p>
<div *ngFor="let car of cars">
{{car.brand}}
{{car.model}}
{{car.type}}
{{car.engineType}}

{{car.address?.streetName}}
{{car.address?.streetNumber}}
{{car.address?.buildingNumber}}
</div>

Takie sprawdzenie zabezpiecza Cię przed błędami związanymi z odwołaniem do biektu który nie istnieje.

W rozwiązaniu pierwszym z ngIf jak możesz zauważyć, nie musisz dodawać sprawdzenia w postaci car.address !== null lub car.address !== undefined.  Zapis *ngIf=”car.address” w domyśle zapewnia Ci sprawdzenie czy obiekt istnieje. 

Podobne sprawdzenie występuje w klasie. Nie musisz sprawdzać czy obiekt nie równa się null lub nie równa się undefined. Wystarczy, że wpiszesz if (car.address)

app.component.ts

import {Component, OnInit} from '@angular/core';
import {CarService} from './car/car.service';
import {Car} from './car/car';
import {newLineWithIndentation} from 'tslint/lib/utils';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
public cars: Car[];
public inlineCarsInfo: string[];

constructor(private carService: CarService) {
}

ngOnInit(): void {
this.cars = this.carService.getCars();
this.inlineCarsInfo = this.prepareInlineCarsInfo();

}

private prepareInlineCarsInfo(): string[] {
// pobranie samochodów przy użyciu gettera
const carsFromGetter = this.carService.cars;
const inlineCarsInfo: string[] = [];

carsFromGetter.forEach(car => {
let inlineCarInfo = `${car.brand} ${car.model} ${car.engineType} ${car.type}`;
if (car.address) {
inlineCarInfo = `${inlineCarInfo} ${car.address.streetName} ${car.address.streetNumber} ${car.address.buildingNumber}`;
}
inlineCarsInfo.push(inlineCarInfo);
});

return inlineCarsInfo;
}
}

app.component.html

<p>Rozwiązanie pierwsze z ngIf</p>
<div *ngFor="let car of cars">
{{car.brand}}
{{car.model}}
{{car.type}}
{{car.engineType}}
<ng-container *ngIf="car.address">
{{car.address.streetName}}
{{car.address.streetNumber}}
{{car.address.buildingNumber}}
</ng-container>
</div>

<p>Rozwiązanie drugie ze znakiem zapytania</p>
<div *ngFor="let car of cars">
{{car.brand}}
{{car.model}}
{{car.type}}
{{car.engineType}}

{{car.address?.streetName}}
{{car.address?.streetNumber}}
{{car.address?.buildingNumber}}
</div>

----------------------------------------------------------
<p>Rozwiązanie trzecie ze sprawdzeniem w komponencie</p>
<div *ngFor="let carInfo of inlineCarsInfo">
{{carInfo}}
</div>