I’m learning Angular right now – as a React.js fangirl.
Pluralsight offered a free month of learning in April. I’ve taken advantage of it.
Here are some notes on the course Angular Component Communication by Deborah Kurata.
Angular Component Communication
Introduction
- components need to communicate with each other, with its template, with the router, can use a service as an intermediary
- check GitHub repository
Communication with a Template
- template binding, e.g., interpolation, property binding, event binding
Interpolation:
// src/app/app.component.ts
currentCustomer = 'Maria'
// src/app/app.component.html
<h3>Current customer: {{ currentCustomer }}</h3>
Property Binding:
// src/app/app.component.ts
imageWidth: number = 50
// src/app/app.component.html
<img [style.width.px]='imageWidth'>
Event Binding:
// src/app/app.component.ts
toggleImage(): void {
this.showImage = !this.showImage;
}
// src/app/app.component.html
<button (click)='toggleImage()'>Show Image</button>
// src/app/app.component.ts
<p *ngIf="condition">Show this sentence unless the condition is true.</p>
- “two-way binding” between template and component
// src/app/app.component.ts
listFilter: string = 'Cart';
onFilterChange(filter:string): void {
this.listFilter = filter;
this.performFilter(this.listFilter);
}
// src/app/app.component.html
<input type='text' [ngModel]='listFilter' (ngModelChange)='onFilterChange=($event)' />
- getters and setters
// src/app/app.component.ts
private _listFilter: string;
get listFilter(): string {
return this._listFilter;
}
set listFilter(value: string) {
this._listFilter = value;
}
// src/app/app.component.html
<input type='text' [(ngModel)]='listFilter' />
ViewChild and ViewChildren
valueChanges
observable- ViewChild
- useful for template-driven forms
- remember that a component is first constructed (
constructor()
) and initialized(ngOnInit()
), after that the view initializes and renders
// src/app/app.component.ts
@ViewChild('filterElement') filterElementRef: ElementRef;
ngAfterViewInit(): void {
this.filterElementRef.nativeElement.focus();
}
// src/app/app.component.html
<input type='text' #filterElement [(ngModel)]='listFilter' />
- use
NgAfterViewInit()
to access the native HTML element (DOM) - subscribe to changes
// src/app/app.component.ts
listFilter: string;
@ViewChild(NgModel) filterInput: NgModel;
ngAfterViewInit(): void {
this.filterInput.valueChanges.subscribe(() => this.performFilter(this.listFilter));
}
// src/app/app.component.html
<input type='text' #filterElement [(ngModel)]='listFilter' />
- ViewChildren: returns a QueryList of element or directive references
- method does not work well with
*ngIf
: in this case the@ViewChild
might be undefined
Communicating with a Child Component
- parent component’s template must contain the child component
- components connected via routing don’t have a parent-child-relationship
// src/app/parent.component.html
<pm-criteria></pm-criteria>
// src/app/child.component.ts
@Component({
selector: 'pm-criteria',
templateUrl: './criteria.component.html',
styleUrls: ['./criteria.component.css']})
- push from parent to child:
@Input
, Getter/Setter,OnChanges
- pull from child to parent: Template Reference Variable,
@ViewChild
@Input
example:
// src/app/parent.component.html
<pm-criteria [displayDetail]='includeDetail'></pm-criteria>
// src/app/parent.component.ts
includeDetail: boolean = true;
// src/app/child.component.ts
@Input() displayDetail: boolean;
- Getter/Setter: favor if child only reacts to changes to specific properties
OnChanges
: favor if child reacts to any input property changes or if you need to access current and prior values- Template Reference Variable: use from the parent’s template
@ViewChild
use from the parent’s class
Communicating with a Parent Component
- use cases: event notification (
@Output
), provide information to parent (Template Reference Variable,@Viewchild
)
// src/app/parent.component.html
<pm-criteria
[displayDetail]='includeDetail'
(valueChange)='onValueChange($event)'>
</pm-criteria>
// src/app/parent.component.ts
includeDetail: boolean = true;
onValueChange(value: string): void {
this.performFilter(value);
}
// src/app/child.component.ts
@Output() valueChange: EventEmitter<string> = new EventEmitter<string>();
private _listFilter: string;
get listFilter(): string {
return this._listFilter;
}
set listFilter(value: string) {
this._listFilter = value;
this.valueChange.emit(value);
}
// src/app/app.component.html
<input type='text' [(ngModel)]='listFilter' />
Communication Through a Service
- state: view state, user information, entity data, user selection and input, etc.
- managing state: property bag, basic state management (via service), state management with notifications (services with
BehaviorSubject
), ngrx/Redux
Example Property Bag:
// src/app/param.service.ts
@Injectable()
export class ParamService {
showImage: boolean
filterBy: string
}
// src/app/product.list.component.ts
export class ProductListComponent {
get ShowImage(): boolean {
return this.paramService.showImage
}
get listFilter(): string {
return this.paramService.filterBy
}
set listFilter(value: string) {
this.paramservice.filterBy = value
}
}
- property bags are great for retaining view state & user selections
- property bags can also communicate with other components
- great for sharing data or other state & communicating state changes
- caveat: any component can read and change values
- components are only notified of state changes if they use template binding
- consider service scope and lifetime
Communicating Through a State Management Service
- good for retrieving entity state and share with others
- purpose: provide state values, maintain and update state, observe state changes
- add a property to retain the list - on get: return the list - on get by id: return an item from the list - on create: add the item the list - on delete: remove the item from the list
// src/app/product.service.ts
private products: IProduct[];
getProducts(): Observable<IProduct[]> {
if (this.products) {
return of(this.products);
}
return this.http.get<IProduct[]>(this.productsUrl)
.pipe(
tap(data => console.log(JSON.stringify(data))),
tap(data => this.products = data),
catchError(this.handleError)
);
}
- benefits: encapsulates retrieve and store operations, retains and shares state values, caches values, provides change notification for bound values using a getter
- cons: stale data, no explicit change notification, state is not immutable
Communicating Through Service Notification
- you can use
Subject
orBehaviorSubject
(multi-cast) to broadcast changes
// src/app/product-list.component.html
<button type='button'
*ngFor='let product of products'
(click)='onSelected(product)'>
// src/app/product-list.component.ts
onSelected(product: IProduct) {
this.productService.changeSelectedProduct(product);
}
// src/app/product.service.ts
private selectedProductSource = new Subject<IProduct>();
selectedProductChanges$ = this.selectedProductSource.asObservable();
changeSelectedProduct(selectedProduct: IProduct) {
// broadcast the notification
this.selectedProductSource.next(selectedProduct);
}
// Component or Service
onSelected(product: IProduct) {
// listen for and respond to the notification
this.productService.changeSelectedProduct(product);
}
Communicating Using the Router
- route parameters: required, optional, query parameters
- benefits: simple, resulting URLs are bookmarkable & sharable
- cons: parameters appear in the URL, not good for large amounts of data
Thoughts
Deborah Kurata is an excellent teacher who can break concepts down to a digestible level.
The course slides and examples helped me immensely.
All in all, the class proved to be fantastic for people new to Angular.
I am sure that I will reference the course’s GitHub repository in the future.
Resources
- Angular Component Communication Pluralsight course by Deborah Kurata
- GitHub repository