One of the new features of Angular 2 is the communication between components and how they can share informations and events.
It’s very frequent that you have to implement a master/detail view, where you have a list of elements and we need to show the detail when the user click on a row.
Let’s go to implement a parent/child component, such as a customer list and a customer detail.
Customer module
In this module we have a list of the customers, and we need to have CRUD operations.
So, first of all we create the customer.component.ts file and we import the necessary modules:
import { Component, OnInit } from "@angular/core"; import { Customer } from "./customer.model"; import { City } from "./city.model"; import { Constants } from "../shared/commons"; import { CustomerService } from "./customer.service"; .......
We could have other modules used in the component, but it’s not relevant for this topic.
Now we define the component attributes:
@Component({ moduleId: module.id, selector: "customer", templateUrl: "customer.component.html" })
And we start to implement the component class:
export class CustomerComponent implements OnInit { public customers: Customer[]; public cities: City[]; public customer: Customer; public edit = false; public newCustomer = false; constructor(private customerService: CustomerService, private cityService: CityService, ……) {} ngOnInit() { this.Load(); } public Load() { this.customerService.GetAll().subscribe( (data) => { this.customers = data; this.customer = null; this.translateService.get("CUSTOMERSLOADED").subscribe((res: string) => { this.alertService.Success(res); }); }, (error) => this.alertService.Error(error)); } ...... }
We inject the dependencies in the constructor and we load the customer list in the OnInit event of the component.
We need two methods for creating/editing of a customer as well:
public New() { let newCustomer: Customer = { Id: Constants.guidEmpty, IdCity: "", Name: "", Address: "", City: null }; this.customer = newCustomer; this.edit = true; } public Edit(customer: Customer) { this.customer = customer; this.newCustomer = false; this.edit = true; }
The customer property contains the object and the edit property is used to detect if the customer is in edit mode or not.
We can now implement the view:
<div [hidden]="edit"> <input type="button" class="btn btn-primary" value="New" (click)="New()" /> <table class="table table-striped"> <thead> <tr> <th> {{ "NAME" | translate }}</th> <th> {{ "ADDRESS" | translate }}</th> <th> {{ "CITY" | translate }}</th> </tr> </thead> <tbody> <tr *ngFor="let customer of customers" (click)="Edit(customer)"> <td> {{customer.Name}}</td> <td> {{customer.Address}}</td> <td> {{cities | cityName: customer.IdCity}}</td> </tr> </tbody> </table> </div>
Customer detail component
Now we are ready to implement the detail component.
We import some libraries:
import { Component, Input, Output, EventEmitter } from "@angular/core"; import { Customer } from "./customer.model"; import { CustomerService } from "./customer.service"; import { Constants } from "../shared/commons"; .......
And we define the attributes:
@Component({ moduleId: module.id, selector: "customer-detail", templateUrl: "customer.detail.component.html" })
The component class has some basic methods for the CRUD operations:
export class CustomerDetailComponent { @Input() isNew: boolean; private currentCustomer: Customer; @Input() set customer(customer: Customer) { this.currentCustomer = customer; } get customer() { return this.currentCustomer; } constructor(private customerService: CustomerService, private alertService: AlertService, private translateService: TranslateService) {} public Save() { if (this.isNew) { this.customerService.Post(this.customer).subscribe( (data) => { this.customer = data; this.translateService.get("CUSTOMERSAVED").subscribe((res: string) =&gt; { this.alertService.Success(res); }); }, (error) => this.alertService.Error(error)); } else { this.customerService.Put(this.customer.Id, this.customer).subscribe( (data) => { this.translateService.get("CUSTOMERSAVED").subscribe((res: string) =&gt; { this.alertService.Success(res); }); }, (error) => this.alertService.Error(error)); } } public Close() { } public Delete() { this.customerService.Delete(this.customer.Id).subscribe( () => { this.translateService.get("CUSTOMERDELETED").subscribe((res: string) =&gt; { this.alertService.Success(res); }); }, (error) => this.alertService.Error(error)); } } }
The view look like this:
<form #customerForm="ngForm"> <div class="form"> <button type="submit" class="btn btn-primary" (click)="Save(customerForm)" [disabled]="!customerForm.form.valid">{{ "SAVE" | translate }}</button> <button type="submit" class="btn btn-danger" (click)="Delete()" *ngIf="!isNew">{{ "DELETE" | translate }}</button> <button type="submit" class="btn btn-default" (click)="Close()">{{ "CLOSE" | translate }}</button> <div class="form-group"> <label for="name">{{ "NAME" | translate }}</label> <input type="text" name="name" class="form-control" placeholder="{{ 'NAME' | translate }}" [(ngModel)]="customer.Name" /></div> <div class="form-group"> <label for="address">{{ "ADDRESS" | translate }}</label> <input type="text" name="address" class="form-control" placeholder="{{ 'ADDRESS' | translate }}" [(ngModel)]="customer.Address" /></div> </div> </form>
Now we need to implement the communication between the two components.
Communication
In the customer component we add two methods that they will be executed when a customer detail will be closed or deleted:
export class CustomerComponent implements OnInit { ...... onClosed(customer: Customer) { this.customer = customer; this.edit = false; } onDeleted(customer: Customer) { this.customers.splice(this.customers.indexOf(this.customer), 1); this.edit = false; } }
These methods will be binded to the events that will happen in the detail component.
In the component view we add the customer detail element:
<customer-detail *ngIf="customer" [hidden]="!edit" [customer]="customer" [isNew]="newCustomer" (onClosed)="onClosed($event)" (onDeleted)="onDeleted($event)"></customer-detail>
Two of the attributes defined in the component selector are the onClosed and onDeleted; so we need to define this properties in the detail component:
export class CustomerDetailComponent { ....... @Output() onClosed = new EventEmitter<Customer>(); @Output() onDeleted = new EventEmitter<Customer>(); ....... }
The Output keyword means that this is an output property of the component and this property is an EventEmitter with a specific type.
We can now emit this events in the related methods:
export class CustomerDetailComponent { ....... public Close() { this.onClosed.emit(this.customer); } public Delete() { this.customerService.Delete(this.customer.Id).subscribe( () => { this.translateService.get("CUSTOMERDELETED").subscribe((res: string) => { this.alertService.Success(res); }); this.onDeleted.emit(this.customer); }, (error) => this.alertService.Error(error)); } ....... }
We defined these methods above and now we added the emission of the output events; the binded methods of the customer component will be executed.
Here the full Angular2 project.
Leave a Reply