Learning Angular 8 - Lab Three - Component Communication And Directives

Introduction

 
This is the third article in the beginner’s series of learning Angular 8 for developers who have no prior experience and little knowledge of JavaScript /typescripts. Please go through my previous labs on Angular 8 to learn the  basics and setup.
  1. Lab 1
  2. Lab 2
In the previous articles, we have learned how communication within a component works from typescript code to HTML and vice versa using string interpolation, property and event binding, and two-way binding.
 

Agenda

 
Below is the agenda for this article.
  • Components life cycle
  • Life cycle hooks
  • Component communication
    • @Input and @Output
    • Services
    • Local references
  • Angular Directives:
    • Component Directives
    • Structural Directives
    • Attribute Directives
  • Custom Directives

Component life cycle

 
A component has a lifecycle that is managed by an Angular framework. Its components are created and rendered and then checked for property changes. The framework destroys them before removing them from the DOM. Angular framework provides different life cycle hooks that run with certain functionality. It offers a way to write code on these hooks when they actually occur to achieve real-life cases.
 

Life cycle hooks

  • ngOnchnages- It is called when a bound input property change.
  • ngOnInit- It is called once the component is initialized.
  • ngDocheck – it is called on every change detection runs
  • ngAfterContentInit- it is called when external content is projected into the component’s view.
  • ngAfterContentChecked- It is called whenever projected content has been checked
  • ngAfterviewInit- It is called after component view/child view is initialized
  • ngAfterViewChecked- it is called whenever view /child view is checked
  • ngOnDestroy- Called just before Angular destroys the directive/component and is a very good place to clean up resources.

Ways to communicate between components

 
Now we have to learn using use cases in the real-life app, where these concepts are used to understand inter-component communication and also accessing variables and data outside of the component. Basically, data binding in the component from the outside.
 

Component communication

 
Learning Angular 8 - Component's Communication And Directives
 
This is a very common use case when data communication needs to happen from parent component to child and vice versa.
  1. @Input and @Output
  2. Services
  3. Local references

@Input and @Output parameters

 
@Input is used to pass data from parent to child component meaning the child will receive data using property defined in the child component.
 
Let see how to implement in an example:
 
Parent: - Course component Child: - Course list component
 
Think in a way you have a property that actually holds value from parent component.In course list component (child component) lets declare a variable
  1. @Input() courseNameSelected : string =null;  
In the course component HTML let’s get a paragraph element to display the value using string interpolation
  1. <p>{{ courseNameSelected }}</p>  
Coming to CourseCompoenent (parent component)
  1. parentCourseName:string =null;  
You can write any code to change value to this
  1. ngOnInit() {  
  2.     this.parentCourseName="Angular 8 from parent using input!!!!" ;  
  3.   }  
Course Component HTML
  1. <div class="col-md-5">  
  2.     <app-course-list [courseNameSelected]="parentCourseName" ></app-course-list>  
  3.   </div>  

@Output

 
@Output is used to pass data from the child to the parent component meaning parent will link a property child component and emit it through the event emitter. Parent component can call property and get data from the child.
 
Child: - Course list component Parent: - Course component
 

Course List Component

 
With the help of the Output parameter and EventEmitter class.
 
In the child component, let's define a property decorated with @Output which has a new instance of EventEmitter class of type string, there are which are a part of Angular core.
 
Also, a property that holds the data to be emitted from child.
  1. @Output () courseNameChild=new EventEmitter<string>();  
  2. courseNameSelecedInChild:string=null;  
There is a function which will be called at a button click from the child
  1. ondatasentParent()  
  2.     {  
  3.        this.courseNameChild.emit(this.courseNameSelecedInChild);  
  4.     }  
  5. <button class="btn btn-success" (click)="ondatasentParent()">Send to Parent Compoent</button>  
This is the HTML mark-up for this.
 
Parent component set up to receive the data emitted from the child.
 
A string property to receive the data emitted from child.
  1. childCourseName:string=null;  
a method to receive data from $event.
  1. recievefromChild($event:string){  
  2.     this.childCourseName=$event;  
  3.   }  

HTML mark-up

 
This is very important to understand that courseNameChild property is defined in child with @Output decorator which is hooked with a function which actually receives data with $event as a parameter. Here you can see all the previous implementation of property binding, event binding and string interpolation .
  1. <div class="col-md-5">  
  2.       <app-course-list [courseNameSelected]="parentCourseName" (courseNameChild)="recievefromChild($event)"></app-course-list>  
  3.     </div>  
  4.   
  5. <p>{{ childCourseName }}</p>  
On a click of the button
 
The parent has received a string from the child and displays it.
 
Learning Angular 8 - Component's Communication And Directives
 

Angular Services

 
Services are nothing but a class with a reusable piece of code that serves a unique purpose and also decouples some business logic away from the component. It modularises the app code by separating out the responsibilities and keep component lean. Services use dependency injection to be utilized across the component by getting injected in the constructor. We will take a deeper look later in the course.
 
Basically, it will show another way to communicate between components.
 
Taking an example of logging functionality which is common to every component. Rather reparenting the logging code in every component, a logging service will be created which later can be injected in component
 
I have created a service named course.service.ts
  1. import { Injectable } from '@angular/core';  
  2. import { Course} from './course.model';  
  3.   
  4. @Injectable({providedIn:'root'})  
  5. export class CourseService  
  6. {  
  7.    courses: Course[]=[  
  8.        new Course('Angular 8 ''Learning Angular 8 !!!'),  
  9.        new Course('React Js''Learning react  !!!'),  
  10.        new Course('webapi Core 8 ''Learning asp.net core !!!'),  
  11.        new Course('Azure''Learning Azure  !!!'),  
  12.        new Course('Mongo DB''Learning MongoDB !!!')  
  13. ];  
  14.     getCourses()  
  15.     {  
  16.         return this.courses.slice();  
  17.     }  
  18. }  
This service is very simple example service which has method and it returns list of course with name and description in it.
 
Now we need to know how it can be used.
  1. import { Component, OnInit , Input} from '@angular/core';  
  2. import {CourseService} from '../course.service';  
  3. import {Course} from '../course.model';  
  4.   
  5. @Component({  
  6.   selector: 'app-course-list',  
  7.   templateUrl: './course-list.component.html',  
  8.   styleUrls: ['./course-list.component.css']  
  9. })  
  10. export class CourseListComponent implements OnInit {  
  11.     
  12.   courses: Course[]=[];  
  13.   statusMessage:string = "No course found!!!!";  
  14.   result : number=10;  
  15.   isDisabled= false;  
  16.   isData=false;  
  17.   courseName="";  
  18.   
  19.   constructor(private courseService:CourseService) {   
  20.     setTimeout(() => {  
  21.       this.isDisabled=true;  
  22.     }, 5000);  
  23.   }  
  24.   
  25.   ngOnInit() {  
  26.       this.courses= this.courseService.getCourses();  
  27.       console.log(this.courses);  
  28.   }  
  29. constructor (private courseService:CourseService) { }  
You can inject the service which is required in the component constructor by informing angular we need the service instance here in this component and once you have the service instance available you can call the require method to communicate using this service.
 
Providers for service: registrations
 
Service needs to be registered to at least one provider of any service you are going to use. Your register providers are in the metadata of the service
  1. @Injectable() decorator- Register a provider with the root injector for your service by including provider metadata in the @Injectable()

    @Injectable({ providedIn: 'root', })

    Registering service at the root level, a single, shared instance of service is created and injects it into any class that asks for it.
  1. @NgModule()– Register a provider with a specific NgModule, the same instance of a service is available to all components in that NgModule.

    @NgModule({providers: [ service1, service2], ... })
  1. @Component()metadata- Register a provider at the component level, a new instance of the service with each new instance of that component.
    1. @Component({   
    2. selector: 'app-test',   
    3. templateUrl: './test.component.html',  
    4.  providers: [ Service1]   
    5. })  

Cross Component Communication using Services

 
It will be very easy with services to communicate by using event emitter and services.
 
Let’s understand with a user case and demo.
 
There are two components course list and course where you want to send course name on click of button in course list component using services.
 
Let’s create a service course service which will help in commutation between these components.
 
Course Service
  1. import { Injectable , EventEmitter} from '@angular/core';  
  2. import { Course} from './course.model';  
  3.   
  4. @Injectable({providedIn:'root'})  
  5. export class CourseService  
  6. {  
  7.    courseSelected=new EventEmitter<Course>();  
  8.   
  9.    courseNameSelected=new EventEmitter<string>();  
  10.   
  11.    courses: Course[]=[  
  12.        new Course('Angular 8 ''Learning Angular 8 !!!'),  
  13.        new Course('React Js''Learning react  !!!'),  
  14.        new Course('webapi Core 8 ''Learning asp.net core !!!'),  
  15.        new Course('Azure''Learning Azure  !!!'),  
  16.        new Course('Mongo DB''Learning MongoDB !!!')  
  17. ];  
  18.     getCourses()  
  19.     {  
  20.         return this.courses.slice();  
  21.     }  
  22. }  
In course service, lets create a string property which holds new event emitter of type string.
  1. courseNameService: string =null;  
  2. ngOnInit() {  
  3.   this.courses= this.courseService.getCourses();  
  4.       
  5.   this.courseNameSelecedInChild="Angular 8 from child compoement !!!!";  
  6.   this.courseNameRef= "Angular 8 from child compoement with reference !!!!"  
  7.   this.courseNameService="Angular 8 from child compoement using service !!!!"  
  8.   }  
ngOnInit() , I have assign a string which I want to send to a course component, which is the parent one.
  1. ondatasentParent()  
  2.   {  
  3.      this.courseService.courseNameSelected.emit(this.courseNameService);  
  4.   }  
On a button click using the course service you need to emit the course name.
 
Parent– Course Component which will receive course name sent from service.
  1. namefromService:string='';  
You need to subscribe to the event to get the value emitted from child component.
  1. ngOnInit() {  
  2.     this.parentCourseName="Angular 8 from parent using input!!!!" ;  
  3.     this.courseService.courseNameSelected.subscribe(  
  4.       (name:string)=>{  
  5.             this.namefromService=name;  
  6.       });  
  7.   }  
In simple words, we have created an event using EventEmitter and emitted the event with data from the button click in course list component which later subscribed in parent component using service instance and read the values emitted from child.
  1. <p> {{ namefromService}}</p>  
Learning Angular 8 - Component's Communication And Directives
 
I have attached the source code with this article which you can download later and play with it.
 

Local Reference

 
Local references a way to communicate from HTML to component typescript code and also using @view child and afterViewinit lifecycle hook.
 
HTML code
  1. <div class="row">     
  2.   <div class="col-xs-5">  
  3.       <input type="text" class="form-control" #courseName>  
  4.       <br>  
  5.       <button class="btn btn-primary" (click)="onAddCourse(courseName)"  
  6.           [disabled]="!isDisabled"  
  7.           >Add New Course</button>  
  8.   </div>  
  9.   <p>{{ name }}</p>  
  10.     
  11. </div>  
Type script code: Basically, courseName is of HTMLInputElement whose reference has been passed and name variable will hold this value and passed and displayed using string interpolation.
  1. name:string="";  
  2. onAddCourse(courseName:HTMLInputElement)  
  3.   {    
  4.        this.name=courseName.value;  
  5.   }  
There is another way also to achieve by Using @ViewChild
  1. @ViewChild('courseName', { static:false}) courseName : ElementRef  
  2.   name:string="";  
  3. onAddCourse(courseName:HTMLInputElement)  
  4.   {    
  5.        this.name= this.courseName.nativeElement.value;  
  6.   }  
But this is not the actually use case we are trying to achieve in here. Our goal was to do inter-component communication.
 
Child Component
 
Here courseNameRef is a string type which holds data which will be sent to parent.
  1. courseNameRef: string = '';  
  2.   
  3.  ngOnInit() {  
  4.      this.courseNameRef= "Angular 8 from child compoement with reference !!!!"  
  5.   }  
Parent Component
  1. import { Component, OnInit, ViewChild, ElementRef , AfterViewInit } from '@angular/core';  
  2. import { CourseListComponent} from './course-list/course-list.component'  
  3.   
  4. @ViewChild(CourseListComponent , { static:false}) childReference;  
  5.   
  6.  ngAfterViewInit()  
  7.   {  
  8.     this.nameRef= this.childReference.courseNameRef;  
  9.   }  
HTML code
  1. <p>{{ nameRef }}</p>  
By doing these changes in the parent component we can achieve the communication.
 
Here you can see nameRef is being assigned with the child component’s courseNameRef. This will be available on ngAfterViewInit hook as the child component will be fully available on this hook only.
 

Angular Directives

 
Directives are the building blocks for any angular application. These are used for HTML DOM manipulation. HTML can be extended to achieve a more efficient HTML response.
It has been divided into three categories.
 
Component Directives
 
Components are directives with a template that instructs angular how to be processed, instantiated and used at runtime meaning selector of the component used instruct angular to inject HTML and typescript code to build output.
 
Structural Directives
 
Structural directives start with a * sign and look like a normal HTML attribute. These directives are used to manipulate and change the structure of the dom elements meaning it adds or removes elements from dom.
 
There are a lot more directive but below are most comply used one.
 
*ngIf
 
ngIf Directives is used to add or remove HTML Elements in dom where in expression should return a Boolean value.
  1. <p *ngIf="condition"> condition is true and ngIf is true. </p>    
  2. <p *ngIf=”! condition">condition is false and ngIf is false.</p>  
If the expression is false then the element is removed, otherwise element is inserted. It does not hide the DOM element rather removes the entire element along with its subtree from the DOM.
 
This directive can be used in different ways as per your use case. I will be demonstrating with a very simple user case of showing and hiding the HTML element with a message.
 
Behind the scene it is adding and removing the element.
  1. <div class="row">     
  2.     <div class="col-xs-12">  
  3.         <div *ngIf="!isData">  
  4.             <p>{{ statusMessage }}</p>         
  5.         </div>    
  6.   
  7.         <div *ngIf="isData">  
  8.                 <p> {{ status Message }}</p>       
  9.             </div>    
  10.     </div>  
  11. </div>  
*ngFor
*ngFor directive is used iterate in HTML element of HTML template once per each item. 
 
Here you can see the *ngFor="let student of students" basically it iterates through the list and provide data in each interaction to work with. This directive is very commonly used in real-world projects.
  1. <div class="row">  
  2.     <div class="col-xs-7">       
  3.         <a *ngFor="let student of students"   
  4.              [ngClass]="{'highlight': student.Status==='Active'}"  
  5.             style="cursor: pointer;"  
  6.             [routerLink]="[index]"  
  7.             routerLinkActive="active"  
  8.             class="list-group-item clearfix"  
  9.           >  
  10.             <div class="pull-left" >  
  11.                 <h4 class="list-group-item-heading">{{student.name}}</h4>  
  12.                 <p class="list-group-item-text">{{student.Course}}</p>    
  13.             </div>  
  14.         </a>  
  15.     </div>  
  16.   </div>  
Structural directive starts with * like *ngIf , *ngFor , behind the scene angular transforms to something which can be used as angular binding so that it can be available to element as there is nothing called * in angular. It is there only to recognise the some structural directive and get this transformed to be used by elements.
 

Attribute Directives

 
Attribute directives are used to change the look and behavior of the DOM elements.
 
ngClass Directive
 
The ngClass directive is used to add or remove CSS classes to an HTML element. Basically, dynamical applying CSS to Dom element.
 
[ngClass]="{'highlight': student.Status==='Active'}"
 
Above you can see [ngClass] which actually sets a class highlight on the basis of student status.
  1. <div class="row">  
  2.     <div class="col-xs-7">       
  3.         <a *ngFor="let student of students"   
  4.              [ngClass]="{'highlight': student.Status==='Active'}"  
  5.             style="cursor: pointer;"  
  6.             [routerLink]="[index]"  
  7.             routerLinkActive="active"  
  8.             class="list-group-item clearfix"  
  9.           >  
  10.             <div class="pull-left" >  
  11.                 <h4 class="list-group-item-heading">{{student.name}}</h4>  
  12.                 <p class="list-group-item-text">{{student.Course}}</p>    
  13.             </div>  
  14.         </a>  
  15.     </div>  
  16.   </div>  
Student component
  1. import { Component, OnInit } from '@angular/core';  
  2.   
  3. @Component({  
  4.   selector: 'app-student',  
  5.   templateUrl: './student.component.html',  
  6.   styleUrls: ['./student.component.css']  
  7. })  
  8. export class StudentComponent implements OnInit {  
  9.   
  10.   constructor() { }  
  11.   
  12.   students: any[] = [  
  13.     {  
  14.       "name""Abhishek Singh ",  
  15.       "Course"'Angular 8',  
  16.       "Status" :"Active"  
  17.     },  
  18.     {  
  19.       "name""David  Banner",  
  20.       "Course"'React Js',  
  21.       "Status" :"Inactive"  
  22.     },  
  23.     {  
  24.       "name""Rohan Kumar",  
  25.       "Course"'React Js',  
  26.       "Status" :"Active"  
  27.     },  
  28.     {  
  29.       "name""Priya Soni",  
  30.       "Course"'Asp.net core ',  
  31.       "Status" :"Active"  
  32.     },  
  33.     {  
  34.       "name""Rajeev Nigam",  
  35.       "Course"'MongoDB',  
  36.       "Status" :"Inactive"  
  37.     }  
  38.   ];  
  39.   
  40.   ngOnInit() {  
  41.   }  
  42.   
  43. }  
Learning Angular 8 - Component's Communication And Directives 
 
ngStyle Directive
 
The ngStyle directive help in modifying the style of an HTML element using the expression basically you can dynamically change the style of your HTML element.
  1. [ngStyle]="{ 'color': 'white'}"  
Working with the same example ngStyle can also be used to set style CSS to a Dom.
 
Learning Angular 8 - Component's Communication And Directives 
 
In this example, I have set up font color from div to white using ngStyle.
 
Dynamic values in the object literal that you assign to ngStyle can be JavaScript. Expressions which are evaluated and the result of that expression is used as the value of the CSS property. It need not be inline, you can use the function to evaluate the value or call an expression to assign the value whatever you feel comfortable doing this.
 
Function getColor() method is called as below.
  1.  getColor(status:string) {   
  2.     switch (status) {  
  3.       case 'Active':  
  4.         return 'Red';  
  5.       case 'Inactive':  
  6.         return 'Black';     
  7.     }  
  8.   }  
  9.   
  10. [ngStyle]="{'color': getColor(student.Status)}"  
Creating your own directive
 
Header Highlighter directive
  1. import { Directive, OnInit, ElementRef } from '@angular/core';  
  2.   
  3. @Directive({  
  4.     selector:'[appheaderhighlighter]'  
  5. })  
  6. export class HeaderHighlighter implements OnInit  
  7. {  
  8.     constructor(private elmentref: ElementRef){}  
  9.   
  10.     ngOnInit()  
  11.     {  
  12.         this.elmentref.nativeElement.style.backgroundColor='yellow';  
  13.     }  
  14. }  
Applying custom attribute directive to the paragraph element, as shown below
  1. <p appheaderhighlighter >Student List</p>     
Learning Angular 8 - Component's Communication And Directives
 
This is a custom directive to highlight the header, but this method doesn't have the best way to achieve the functionality. Here, you can see we have accessed the property directly which is not best practice as some of the user case where angular uses DOM before it actually loads it might cause some issues.
 
Let work together to improve our directive.
 
Renderer2
 
It extends this base class to implement custom rendering. Angular renders a template into DOM. custom rendering can be used to intercept rendering calls or to render to something other than DOM.
 
Learning Angular 8 - Component's Communication And Directives 
  1. import { Directive, Renderer2 , OnInit, ElementRef} from '@angular/core';  
  2.   
  3. @Directive({  
  4.     selector:'[apphighlighetrImproved]'  
  5. })  
  6. export class HeaderHighlighterImproved implements OnInit{  
  7.     constructor(private elmRef:ElementRef,private rederer:Renderer2){}  
  8.   
  9.     ngOnInit()  
  10.     {  
  11.     this.rederer.setStyle(this.elmRef.nativeElement,'backgroundColor','gray');  
  12.     }  
  13. }  
apphighlighetrImproved is attribute directive.
  1. <p apphighlighetrImproved >Student List</p>  
I have used the same as attribute to the paragraph element.
 
Here is the result.
 
Learning Angular 8 - Component's Communication And Directives
 
I am attaching a source code with this article.
 
Steps to run:
  1. Download the class-app zip.
  2. Open in vs code editor.
  3. Open the terminal in editor and run “npm install”
  4. Run “ng serve”

Conclusion

 
The next article in the series will be deep dive in Angular routing.
 
Keep learning and keep smiling.
 
Resources and Useful Links
 
Thanks to the authors of the below links


Similar Articles