Rafał Brzoska
Bottega IT Minds
Rafał Brzoska
Wprowadzenie
Komponenty, Szablony komponentów,
Dyrektywy wbudowane
Pipes (filtry)
Routing
Serwisy
HttpClient
RxJS
Formularze( Template driven, Reactive )
Dependency Injection
Moduły (NgModule) i architektura
Testy
Własne dyrektywy i dynamiczne komponenty
Zarządzanie stanem
Biblioteki
interface MyCustomType {
name: string;
}
interface TestType {
testNum: number;
testType: MyCustomType;
}
interface ClassInterface {
myFn(): void;
mySecondFunction(num: number): string;
}
class Greeter {
greeting: string;
constructor(message: string, private userId: number) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world", 1);
type C = { a: string, b?: number }
type stringOrNumber = string | number // Union Type
function myGenericFunction<T>(arg: T): T {
return arg;
}
class CustomList<T> {
private list: T[];
push(element: T) { list.push(element) };
getELements(): T[] {
return this.list;
}
}
Root
S
D
D
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
...
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
// template: '<div></div>',
// styles: [`.btn { color: red }`],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
providers: [DataService]
})
...
@NgModule({
declarations: [
AppComponent,
HomeComponent
],
imports: [
BrowserModule,
RouterModule.forRoot(routes),
providers: [MyService],
bootstrap: [AppComponent]
})
export class AppModule { }
const routes: Routes = [
{ path: 'home', component: HomeComponent }
];
...
imports: [ RouterModule.forRoot(routes) ],
<nav>
<a routerLink="/">Home</a>
<a routerLink="/kontakt">Kontakt</a>
<a [routerLink]="['/about']">About</a>
</nav>
<router-outlet></router-outlet>
AppModule
AppComponent/NavigationComponent
<p> {{ title }} </p>
<my-component [myInput]="inputData"></my-component>
<my-component (myOutput)="outputHandler($event)"></my-component>
<h1 #myTemplateReference></h1>
Interpolacja
Input - property binding
Output - event binding
template reference variable
@Component({
selector: 'child-component',
template: `
<h3>{{user.name}} says:</h3>
<p>I, {{user.name}}, am at your service, {{masterName}}.</p>
<button (click)="handleClick()">Say Hello</button>
`
})
export class ChildComponent {
@Input() user: User;
@Input('master') masterName: string;
@Output() sayHello = new EventEmitter<string>();
handleClick() {
this.sayHello.emit(this.user.name)
}
}
ChildComponent
@Component({
selector: 'ParentComponent',
template: `
<child-component
[user]="user"
[master]="master"
(sayHello)="handleSayHello($event)">
</child-component>
`
})
export class ParentComponent {
user = { name: 'Alojzy' };
master = 'Majster';
handleSayHello(name: string) { alert(name) }
}
ParentComponent
<div *ngIf="show; else elseBlock">Pokaz to jesli show = true</div>
<ng-template #elseBlock>Pokaż jesli show = false</ng-template>
<div *ngIf="warunek">...</div>
<ng-template [ngIf]="warunek"><div>...</div></ng-template>
*ngIf
[ngIf]
* przy dyrektywie dodaje <ng-template> i upraszcza notacje
<ul>
<li *ngFor="let user of users">{{user.name}}</li>
</ul>
<ng-template ngFor let-user
[ngForOf]="users"
[ngForTrackBy]="trackById">
<div {{user.name}}</div>
</ng-template>
* przy dyrektywie dodaje <ng-template> i upraszcza notacje
<element [ngClass]="'className1 className2'">...</element>
<element [ngClass]="['className1', 'className2']">...</element>
<element
[ngClass]="{'className1 ': true, 'className2 ': true, 'className3 ': false}">
...
</element>
<div [class.isOnline]="isOnline">Status</div>
<div [style.color]="isOnline ? 'red' : 'green'">Status</div>
Attribute binding
<container [ngSwitch]="myNumber">
<element *ngSwitchCase="1"> jeden </element>
<element *ngSwitchCase="2"> dwa </element>
<element *ngSwitchDefault>jakiś numer</element>
</container>
<pre> {{ myObject | json }} </pre>
<p> {{ jakasData | date | uppercase }} </p>
<p> {{ myNumber | currency:'USD' }} </p>
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class UserService {
}
class MyComponent {
users: User[];
constructor(private userService: UserService) { }
someFunction() { this.users = this.userService.getUsers() }
}
// import in AppModule
import: [... HttpClientModule]
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class MyDataService {
constructor(private http: HttpClient) { }
getMyData() {
this.http.get('http://url...')
}
}
AppModule
Data Service
Funkcja która przekazuje observerowi dane ze strumienia
let myObservable = new Observable( observer => {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
});
i udostępnia metodę subscribe w której przekazujemy observera
myObservable.subscribe(observer)
Obiekt posiadający 3 metody: next, complete, error
myObservable.subscribe({
next: () => ... ,
error: () => ... ,
complete: () => ...
});
lub poprostu zestaw tych metod podawanych osobno
myObservable.subscribe(
() => ..., // next
() => ..., // error
() => ... // complete
);
Jest Observablem
let mySubject = new Subject();
mySubject.subscribe(observer);
i jednocześnie jest observerem
mySubject.next(1);
mySubject.complete();
mySubject.error(error);
Cold - Observable 'produkuje' dane i przy każdym subscribe podaje ten sam strumień
new Observable( observer => {
observer.next(1);
observer.next(2);
});
Hot - Observable reaguje na zewnętrzne źródło danych (np Event) i zamienia go w strumień
let element = document.getElementById('myButton');
const myHotObservable = new Observable(obs =>
element.addEventListener('click', e => obs.next(e))
)
myHotObservable.subscribe(x => console.log(x));
Cold - generuje dane tylko po subscribe
Hot - generuje dane bez względu na to czy ktoś je zasubskrybował czy nie
Cold - może mieć jednocześnie tylko jednego observera i jeden subscribe (unicast)
Hot - może mieć kilku observerow i kilka subscribe (multicast)
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [
BrowserModule,
FormsModule // <-- import
],
....
})
export class AppModule { }
<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">
<input name="myName" ngModel required #myName="ngModel">
<input name="age" ngModel>
<button>Submit</button>
</form>
<p>Name value: {{ myName.value }}</p>
<p>Name valid: {{ myName.valid }}</p>
<p>Form value: {{ myForm.value | json }}</p>
<p>Form valid: {{ myForm.valid }}</p>
Szablon + dyrektywa NgForm
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
BrowserModule,
ReactiveFormsModule // <-- import
],
....
})
export class AppModule { }
export class UserFormComponent {
userForm = new FormGroup({
myName: new FormControl(''),
age: new FormControl(''),
});
}
FormControl & FormGroup
export class UserFormComponent {
constructor(private fb: FormBuilder){}
userForm = fb.group({
myName: '',
age: '',
});
}
FormBuilder
<form [formGroup]="userForm">
<label>
Name:
<input type="text" formControlName="myName">
</label>
<label>
Age:
<input type="number" formControlName="age">
</label>
</form>
Szablon
Feature Module | Declarations | Providers | Exports | Imported by |
---|---|---|---|---|
Domain | Yes | Rare | Top component | Feature, AppModule |
Routed (Lazy Loaded) | Yes | Rare | No | None |
Routing | No | Yes (Guards) | RouterModule | Feature (for routing) |
Service (Core) | No | Yes | No | AppModule |
Shared (Widget) | Yes | Rare | Yes | Feature |
Module Type | Declarations | Providers | Exports | Imported by |
---|---|---|---|---|
Domain (Feature) | Yes | Rare | Top component | Feature, AppModule |
Routed (Lazy Loaded Feature) | Yes | Rare | No | None |
Routing | No | Yes (Guards) | RouterModule | Feature (for routing) |
Service (Core) | No | Yes | No | AppModule |
Shared (Widget) | Yes | Rare | Yes | Feature |