Category: 04. Components

https://cdn3d.iconscout.com/3d/premium/thumb/components-3d-icon-download-in-png-blend-fbx-gltf-file-formats–component-interface-contents-panel-empty-state-pack-user-icons-10523284.png

  • Elements

    What is an Angular Element?

    Angular elements are reusable components that have been transformed into custom elements (also called Web Components). Angular provides a simple and effective method to create Web components.

    Web components are custom HTML elements available in native HTML specifications to extend the features/tags of the HTML document. It can be created through JavaScript and can be made available in the HTML document. JavaScript has special methods to create custom HTML elements.

    Creating custom HTML elements using JavaScript is a lengthy process and developers need to understand the internal workings of custom HTML elements and the Shadow DOM concept. Angular simplifies the process by enabling the conversion of Angular components to web components with minimal change in the component class.

    Creating Angular Elements

    Creating Angular elements means transforming a component into a custom element. For this purpose, the @angular/elements package is used. This package exports a createCustomElement() function that converts a component into a class that can be registered with the browser as a custom element.

    Example

    Let us learn how to create a custom HTML element in Angular.

    In this example, we will create a component to display employee information (say EmpCard) and convert it into custom HTML element.

    Step 1: Create an angular application, emp-card-web-component using Angular CLI.

    ng newemp-card-web-component
    

    Step 2: Create the emp-card component using Angular CLI.

    ng generate component emp-card
    CREATE src/app/emp-card/emp-card.component.html(24 bytes)CREATE src/app/emp-card/emp-card.component.spec.ts(623 bytes)CREATE src/app/emp-card/emp-card.component.ts(253 bytes)CREATE src/app/emp-card/emp-card.component.css(0 bytes)

    Step 3: Add encapsulation option in the @Component decorator with ViewEncapsulation.ShadowDom option. ShadowDom option enables the HTML native ShadowDom concept to preserve the styles of the component without leaking into the other part of the HTML document.

    @Component({// ...
       encapsulation: ViewEncapsulation.ShadowDom
    })exportclassEmpCardComponent{// ...}

    Step 4: Next, add two input properties, name and role of the employee in the HTML element.

    exportclassEmpCardComponent{
       @Input() name: string ='';
       @Input() role: string ='';}

    Step 5: The complete listing of the component is as follows −

    import{ Component, Input, ViewEncapsulation }from'@angular/core';
    
    @Component({
       selector:'app-emp-card',
       templateUrl:'./emp-card.component.html',
       styleUrls:['./emp-card.component.css'],
       standalone:true,
       encapsulation: ViewEncapsulation.ShadowDom
    })exportclassEmpCardComponent{
       @Input() name: string ='';
       @Input() role: string ='';}

    Step 6: Next, open the components template and add markup to display employee name and role as shown below −

    <div class="card"><div class="container"><h4><b>{{ name }}</b></h4><p>{{ role }}</p></div></div>

    Step 7: Next, open the components style and add css to show shadow in the employee card.

    .card{box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);}.card:hover{box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);}.container{padding: 2px 16px;max-width: 200px;}

    Step 8: Next, install @angular/elements module provided by angular team. @angular/elements module has options to create custom HTML element from angular component.

    ng add @angular/elements
    ✔ Determining Package Manager
       Using package manager: npm
    ✔ Searching for compatible package version
       Found compatible package version: @angular/[email protected].
    ✔ Loading package information from registry
    ✔ Confirming installation
    ✔ Installing package
    Package "@angular/elements" was found but does not support schematics.

    Step 9: Next, open the main.ts file and remove all boilerplate code.

    Step 10: Next, import createApplication from @angular/platform-browser. createApplication will bootstrap the angular application.

    import{ createApplication }from'@angular/platform-browser';

    Step 11: Next, import createCustomElement from @angular/element module. createCustomElement will be used to create custom HTML element from the angular component.

    import{ createCustomElement }from'@angular/elements';

    Step 12: Next, import EmpCardComponnet component as shown below −

    import{ EmpCardComponent }from'./app/emp-card/emp-card.component'

    Step 13: Next, create application using createAppliation() method by inputting providers and a callback method. The callback method will be used to create the custom HTML element from angular component.

    createApplication({ providers:[]}).then((appRef)=>{// ...});

    Step 14: Implement the callback method and create custom element using createCustomElement() method. createCustomElement accepts the component to be converted and the apps injector.

    We can get injector from application reference returned from createApplication() method.

    createApplication({ providers:[]}).then((appRef)=>{const empCard =createCustomElement(
    
      EmpCardComponent,{ injector: appRef.injector });});</code></pre>

    Step 15: Next, register the created custom component using JavaScript native method, customElements.define() method.

    createApplication({ providers:[]}).then((appRef)=>{const empCard =createCustomElement(
    
      EmpCardComponent,{ injector: appRef.injector });
    customElements.define('emp-card', empCard);});

    Step 16: The complete listing of the main file, main.ts is as follows,

    import{ AppModule }from'./app/app.module';import{ createApplication }from'@angular/platform-browser';import{ createCustomElement }from'@angular/elements';import{ EmpCardComponent }from'./app/emp-card/emp-card.component'createApplication({ providers:[]}).then((appRef)=>{const empCard =createCustomElement(
    
      EmpCardComponent,{ injector: appRef.injector });
    customElements.define('emp-card', empCard);});

    Step 17: Next, buid the application using angular CLIs build command

    ng build --configuration=production
    Initial chunk files   | Names         |  Raw size | Estimated transfer size
    main-5LWF45YA.js      | main          |106.50 kB |31.17 kB
    polyfills-FFHMD2TL.js | polyfills     |34.52 kB |11.28 kB
    styles-5INURTSO.css   | styles        |0 bytes |0 bytes
    
    
                      | Initial total |141.02 kB |42.45 kB
    Application bundle generation complete.[7.641 seconds] Output location:D:\Angular\emp-card-web-component\dist\emp-card-web-component

    Step 18: Once, the build is done, the output files are available in dist/emp-card-web-component folder. It has below files (similar files) along with a index.html file.

    • main-5LWF45YA.js
    • polyfills-FFHMD2TL.js
    • styles-5INURTSO.css

    Step 19: Update the index.html file of the src folder with the newly created component and check the output for correctness.

    <!doctype html><html lang="en"><head><meta charset="utf-8"><title>EmpCardWebComponent</title><base href="/"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" type="image/x-icon" href="favicon.ico"></head><body><emp-card name="John" role="Angular developer"></emp-card><emp-card name="Maria" role="Frontend developer"></emp-card></body></html>

    Here, we have added to emp-card tag with two different employee details.

    Step 20: Finally, run the application and check the output in the browser.

    Employee Information
  • Dynamic components

    Angular allows the component to be dynamically created and loaded at run time at a specific location in the host (another) component. Loading a component at run time opens a lot of opportunities to do advanced functionality. For example, a banner rotation component can accept heavily customized banner item instead of accepting banners confirming to a specific template, which is always pre-defined and static in nature.

    Let us learn how to create an Angular component at run time and attach it to the host component in this chapter.

    How to Render Angular Components Dynamically?

    There are two ways to create dynamic components in Angular:

    • Using NgComponentOutlet
    • Using ViewContainerRef

    Using NgComponentOutlet

    The NgComponentOutlet is a directive that helps in dynamic component creation. It instantiates a Component and embeds its view (host view) inside the current view. Please note it only accepts Components and to use it, you need to import CommonModule.

    Example

    In this example, we see the practical implementation of NgComponentOutlet directive. Here, we create an Angular application that will have two main child components and they will be associated with buttons named Admin and User. On clicking the Admin button, AdminBioComponent will be displayed and when you click the User button, StandardBioComponent will be displayed.

    Step 1: Create AdminBioComponent using the given command −

    ng g c admin-bio
    

    Step 2: Create a second component, StandardBioComponent using the command given below −

    ng g c standard-bio
    

    Step 3: We need another component that will contain the logic of dynamic component creation.

    ng g c check-bio
    

    Step 4: Now, open admin-bio.component.html file and copy the below code −

    <h3>Admin Bio</h3><p>Content of the Admin Bio Component.</p>

    Step 5: Then, open standard-bio.component.html file and copy the below code −

    <h3>Standard Bio</h3><p>Content of the Standard Bio Component.</p>

    Step 6: Next, open check-bio.component.html file and copy the below code −

    <ng-container *ngComponentOutlet="getBioComponent()"></ng-container>

    Step 7: In the check-bio.component.ts file, create a method named getBioComponent(). This method will check which button is clicked among the two and display the view accordingly. The complete code is given below −

    import{ Component, Input }from'@angular/core';import{ AdminBioComponent }from'../admin-bio/admin-bio.component';import{ StandardBioComponent }from'../standard-bio/standard-bio.component';import{ NgComponentOutlet }from'@angular/common';
    
    @Component({
      selector:'app-check-bio',
      standalone:true,
      imports:[NgComponentOutlet],
      templateUrl:'./check-bio.component.html',
      styleUrl:'./check-bio.component.css'})exportclassCheckBioComponent{
      @Input() user!:{name: string, isAdmin: boolean};getBioComponent(){ 
    
    console.log(this.user.isAdmin)returnthis.user.isAdmin ? AdminBioComponent : StandardBioComponent;}}</code></pre>

    Step 8: Next, open app.component.html file and copy the below code −

    <!-- buttons to trigger admin() and userLog() methods --><button(click)="admin()">Admin</button><button(click)="userLog()">User</button><!-- conditional rendering --><div *ngIf="isValid"><!--input binding --><app-check-bio [user]="user"></app-check-bio><router-outlet /></div>

    Here, the given buttons will trigger the admin() and userLog() methods. The *ngIf directive conditionally includes the <div> element in the DOM if the isValid property is true. If it is false, the <div> and its contents are not rendered.

    Step 10: Finally, inside the app.component.ts file, copy the below code −

    import{ Component }from'@angular/core';import{ RouterOutlet }from'@angular/router';import{ CheckBioComponent }from'./check-bio/check-bio.component';import{ CommonModule }from'@angular/common';
    
    @Component({
      selector:'app-root',
      standalone:true,
      imports:[RouterOutlet, CheckBioComponent, CommonModule],
      templateUrl:'./app.component.html',
      styleUrls:['./app.component.css']})exportclassAppComponent{
      title ='dynamicComponent';
    
      user ={"name":"Admin","isAdmin":false}
      isValid =false;admin(){this.user.isAdmin =true;this.isValid =true;}userLog(){this.user.isAdmin =false;this.isValid =true;}}

    Output

    dynamic component using NgComponentOutlet

    Using ViewContainerRef

    The second way of creating and rendering components dynamically is by using ViewContainerRef. Other components or directives use it to get a reference to a view container corresponding to their location in the DOM. In this way, ViewContainerRef helps to dynamically create and insert components at that specific location.

    Example

    Let us create a real time application to display a collection of employees in two different formats, table and gallery. We will create two components, one to show employees in the table and another in gallery format. Then we will create a host component, which will dynamically load either table based component or gallery based component.

    Step 1: Create an interface to hold common data across multiple dynamic component

    $ ng generate interfaceDynDataCREATE src/app/dyn-data.ts(29 bytes)

    Step 2: Create a directive, DynSample. The purpose of the directive is to target and select the view container (where dynamic component will be placed) in the host component template.

    $ ng generate directive DynSample
    CREATE src/app/dyn-sample.directive.spec.ts(237 bytes)CREATE src/app/dyn-sample.directive.ts(147 bytes)

    Step 3: Create a dynamic component, DynList. The purpose of the component is to list the employee as gallery.

    $ ng generate component DynList
    CREATE src/app/dyn-list/dyn-list.component.css(0 bytes)CREATE src/app/dyn-list/dyn-list.component.html(23 bytes)CREATE src/app/dyn-list/dyn-list.component.spec.ts(567 bytes)CREATE src/app/dyn-list/dyn-list.component.ts(209 bytes)

    Step 4: Create a dynamic component, DynTable. The purpose of the component is to list the employees in tabular format.

    $ ng generate component DynTable
    CREATE src/app/dyn-table/dyn-table.component.css(0 bytes)CREATE src/app/dyn-table/dyn-table.component.html(24 bytes)CREATE src/app/dyn-table/dyn-table.component.spec.ts(574 bytes)CREATE src/app/dyn-table/dyn-table.component.ts(213 bytes)

    Step 5: Create a host component, DynHost. The purpose of the component is to host any one of the component, DynList and DynTable based on configuration.

    $ ng generate component DynHost
    CREATE src/app/dyn-host/dyn-host.component.css(0 bytes)CREATE src/app/dyn-host/dyn-host.component.html(23 bytes)CREATE src/app/dyn-host/dyn-host.component.spec.ts(567 bytes)CREATE src/app/dyn-host/dyn-host.component.ts(209 bytes)

    Step 6: Next, open DynSampleDirective and start updating the code. The code generated by angular CLI is as follows,

    import{ Directive }from'@angular/core';
    
    @Directive({
       selector:'[appDynSample]'})exportclassDynSampleDirective{constructor(){}}

    Here,

    • Attribute directive purpose is to target the element (ng-template) in the host components template where the dynamic component will be injected.
    • appDynSample is the selector to target and select the element (ng-template). The dynamic component will be created, initialized and injected into the appHello element (ng-template) later.

    Step 7: Next, initialize the view component reference object (ViewComponentRef) in the directive as shown below −

    import{ Directive, ViewContainerRef }from'@angular/core';
    
    @Directive({
       selector:'[appDynSample]'})exportclassDynSampleDirective{constructor(public viewContainerRef: ViewContainerRef){}}

    Here,

    • Imported the ViewContainerRef from @angular/core module
    • Initialized the viewContainerRef object through contructor dependency injection. viewContainerRef is the view container of the targeted element (ng-template) in the host components template. ViewContainerRef has method to dynamically create a component and append that component to it. We will use it later in the host component to dynamically create the DynListComponent and DynTableComponent.

    Step 8: Next, open the interface, DynData and add an array property, data

    exportinterfaceDynData{
       data : any[]}

    Step 9: Next, open the DynListComponent component and implement DynData interface.

    import{ Component }from'@angular/core';import{ DynData }from'../dyn-data'
    
    @Component({
       selector:'app-dyn-list',
       templateUrl:'./dyn-list.component.html',
       styleUrls:['./dyn-list.component.css']})exportclassDynListComponentimplementsDynData{
       data: any[]=[]}

    Here,

    • Imported DynData interface and implements it in class definition
    • Included the data property as per DynData interface specification

    Step 10: Next, open the components template and render the data as list of items

    <div class="gallery"><div *ngFor="let item of data" class="card"><div class="container"><h4><b>{{ item.name }}</b></h4><p>{{ item.role }}</p></div></div></div>

    Here,

    • data hold the list of employees with two properties, name and role
    • Used ngFor to show the employees as list of cards

    Step 11: Next, open the components styles and add necessary CSS as shown below −

    .gallery{display: flex;flex-wrap: wrap;justify-content: left;gap: 10px;}.card{flex-basis: 200px;box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);}.card:hover{box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);}.container{padding: 2px 16px;max-width: 200px;}

    Here, we used flex feature of CSS to show the employees in gallery format

    Step 12: Next, open the DynTableComponent component and implement DynData interface.

    import{ Component }from'@angular/core';import{ DynData }from'../dyn-data'
    
    @Component({
       selector:'app-dyn-table',
       templateUrl:'./dyn-table.component.html',
       styleUrls:['./dyn-table.component.css']})exportclassDynTableComponentimplementsDynData{
       data: any[]=[]}

    Here,

    • Imported DynData interface and implements it in class definition
    • Included the data property as per DynData interface specification

    Step 13: Next, open the components template and render the data as list of items

    <table class="employees"><thead><tr><th>Name</th><th>Role</th></tr></thead><tbody><tr *ngFor="let item of data"><td>{{ item.name }}</td><td>{{ item.role }}</td></tr></tbody></table>

    Here,

    • data hold the list of employees with two properties, name and role
    • Used ngFor to render the employees as rows in the html table

    Step 14: Next, open the components styles and add necessary CSS as shown below −

    .employees{border-collapse: collapse;width: 400px;}.employees td, .employees th{padding: 8px;}.employees tbody tr:nth-child(even){background-color: #f2f2f2;}.employees tbody tr:hover{background-color: #ddd;}.employees thead th{padding-top: 12px;padding-bottom: 12px;text-align: left;background-color: brown;color: white;}

    Step 15: Next, open DynHostComponent components template and include the DynSampleDirective as shown below −

    <ng-templte appDynSample></ng-template>

    Here, we have shown the ng-template using our DynSample directive.

    Step 16: Next, open DynHostComponent and import necessary classes

    import{ Component, ViewContainerRef, OnInit, ViewChild, Input }from'@angular/core';

    Step 17: Next, import interface, list component, table component and directive

    import{ DynData }from'../dyn-data'import{ DynSampleDirective }from'../dyn-sample.directive'import{ DynListComponent }from'../dyn-list/dyn-list.component'import{ DynTableComponent }from'../dyn-table/dyn-table.component'

    Step 18: Implement OnInit life cycle hook in the class declaration

    exportclassDynHostComponentimplementsOnInit{}

    Step 19: Declare an input property to get format information (table / list) from user.

    @Input() format: string ='list'

    Step 20: Declare a property, data and set the sample data

    private data =[{'name':'John','role':"Manager"},{'name':'Peter','role':"Marketing Intern"},{'name':'Mary','role':"Technical Intern"},{'name':'Jack','role':"Sales Manager"},{'name':'Jessica','role':"Delivery Head"},]

    Step 21: Declare host property and get the ng-template view component from the template by using @ViewChild and directive type, DynSampleDirective

    @ViewChild(DynSampleDirective,{static:true}) host!: DynSampleDirective;

    Step 22: Do actual implementation of dynamically creating the component and loading it into the appropriate place in the ngOnInit life cycle hook

    ngOnInit(){const viewContainerRef =this.host.viewContainerRef;
       viewContainerRef.clear()if(this.format =='table'){const compRef = viewContainerRef.createComponent<DynData>(DynTableComponent);
    
      compRef.instance.data =this.data;}else{const compRef = viewContainerRef.createComponent&lt;DynData&gt;(DynListComponent);
      compRef.instance.data =this.data;}}</code></pre>

    Step 23: The complete listing of the host component is as follows,

    import{ Component, ViewContainerRef, OnInit, ViewChild, Input }from'@angular/core';import{ DynData }from'../dyn-data'import{ DynSampleDirective }from'../dyn-sample.directive'import{ DynListComponent }from'../dyn-list/dyn-list.component'import{ DynTableComponent }from'../dyn-table/dyn-table.component'
    
    @Component({
       selector:'app-dyn-host',
       templateUrl:'./dyn-host.component.html',
       styleUrls:['./dyn-host.component.css']})exportclassDynHostComponentimplementsOnInit{
       @Input() format: string ='table'private data =[{'name':'John','role':"Manager"},{'name':'Peter','role':"Marketing Intern"},{'name':'Mary','role':"Technical Intern"},{'name':'Jack','role':"Sales Manager"},{'name':'Jessica','role':"Delivery Head"},]
       
       @ViewChild(DynSampleDirective,{static:true}) host!: DynSampleDirective;ngOnInit(){const viewContainerRef =this.host.viewContainerRef;
    
      viewContainerRef.clear()if(this.format =='table'){const compRef = viewContainerRef.createComponent&lt;DynData&gt;(DynTableComponent);
         compRef.instance.data =this.data;}else{const compRef = viewContainerRef.createComponent&lt;DynData&gt;(DynListComponent);
         compRef.instance.data =this.data;}}}</code></pre>

    Step 24: Next, open the app components template and include the host component.

    <app-dyn-host format="table"/>

    Here, we have directed the host component to render the employees data in the tabular format. The host component will dynamically create the DynTableComponent and inject into the host component template.

    Step 25: Next, run the application and you can see that the employee data is shown in a tabular format as shown below −

    employee data

    Step 26: Next, open the app components template and change the format to gallery.

    <app-dyn-host format="gallery"/>

    Here, we have directed the host component to render the employees data in gallery format. The host component will dynamically create the DynListComponent and inject into the host component template.

    Step 27: Next, run the application and you can see that the employee data is shown in gallery format as shown below −

    gallery format
  • Content projection

    What is Content Projection in Angular?

    Content projection is a technique available in the Angular component to include external content (from consumer of the component) along with layout and styles in the specific area of the component template.

    Different Implementation of Content Projection

    The different ways in which we can implement content projection are:

    • Single-slot content projection
    • Multi-slot content projection
    • Conditional content projection

    Single-Slot Content Projection

    A component involving only one content projection is called single-slot content projection.

    Example

    Let us understand the content projection by creating the above explained component.

    Step 1: Create a projectionSample application using the below command −

    ng newprojectionSample

    Step 2: Navigate to project folder and create a new component with the name content-projection-sample:

    cd projectSample
    
    ng generate component content-projection-sample
    CREATE src/app/content-projection-sample/content-projection-sample.component.html(41 bytes)CREATE src/app/content-projection-sample/content-projection-sample.component.spec.ts(736 bytes)CREATE src/app/content-projection-sample/content-projection-sample.component.ts(320 bytes)CREATE src/app/content-projection-sample/content-projection-sample.component.css(0 bytes)

    Step 3: Add <ng-content> tag in the template, i.e. content-projection-sample.component.html file as shown below −

    <p>This is content from the component template</p><ng-content></ng-content><p>This is another content from component template</p>

    Step 4: Use the content-projection-sample component in the app.component.html file as shown below −

    <app-content-projection-sample><p>This is external content</p></app-content-projection-sample><router-outlet></router-outlet>

    Step 5: Now import the ContentProjectionSampleComponent inside the AppComponent.

    import{ Component }from'@angular/core';import{ RouterOutlet }from'@angular/router';import{ ContentProjectionSampleComponent }from'./content-projection-sample/content-projection-sample.component';
    
    @Component({
      selector:'app-root',
      standalone:true,
      imports:[RouterOutlet, ContentProjectionSampleComponent],
      templateUrl:'./app.component.html',
      styleUrl:'./app.component.css'})exportclassAppComponent{
      title ='projectionSample';}

    The output of the application is as shown below −

    Application Output

    Multi-Slot Content Projection

    Angular allows multiple content to be projected in the component as well and it is called multi-slot content projection.

    Example

    Let us see how to use multiple content projections by modifying the above example.

    Step 1: Add another <ng-content> along with selector attribute in the component template, content-projection-sample.component.html as shown below −

    <p>This is content from the component template</p><ng-content></ng-content><p>This is another content from component template</p><ng-content select="[second]"></ng-content>

    Step 2: Update the app component template, app.component.html as shown below −

    <app-content-projection-sample><p>This is external content</p><p second>This is yet another external content</p></app-content-projection-sample><router-outlet></router-outlet>

    Here, the value of selector attribute (second) set in the component template is used in the content to be projected.

    Step 3: Now, run the application and check the output.

    multi-slot content

    Conditional content projection

    Conditional content projection is projecting a content when certain condition is met. We can use ng-content to conditionally project the content. But it is not recommended since <ng-content> will be initialized even if the content is not going to be rendered. Instead, we can use <ng-template> to project the content safely since it will initialize the content only when it is going to be rendered.

    Example

    Let us create a working demo by updating our projectionSample application and learning the conditional projection in more detail.

    Step 1: Create a directive, greet-content using angular CLI as shown below −

    ng g directive directives/greet-content
    CREATE src/app/directives/greet-content.directive.spec.ts(257 bytes)CREATE src/app/directives/greet-content.directive.ts(184 bytes)

    Step 2: Update the directive and get the template reference as shown below −

    import{ Directive, TemplateRef }from'@angular/core';
    
    @Directive({
      selector:'[appGreetContent]',
      standalone:true})exportclassGreetContentDirective{constructor(public template: TemplateRef){}}

    Here,

    • selector is the key to identify the directive
    • template is the reference object of type TemplateRef injected into the directive through constructor injection (dependency injection concept)

    Step 3: Update the component, ContentProjectionSampleComponent to get the directive object and set the actual condition as shown below −

    import{ Component, ContentChild }from'@angular/core';import{ CommonModule }from'@angular/common';import{ GreetContentDirective }from'../directives/greet-content.directive';
    
    @Component({
      selector:'app-content-projection-sample',
      standalone:true,
      imports:[CommonModule, GreetContentDirective],
      templateUrl:'./content-projection-sample.component.html',
      styleUrls:['./content-projection-sample.component.css']})exportclassContentProjectionSampleComponent{
      show =true;
      @ContentChild(GreetContentDirective) greet!: GreetContentDirective;}

    Here,

    • show is the variable, which holds the deciding condition
    • @ContentChild is the decorator, which will be used to get the directive instance

    Step 4: In the component’s template, use ngIf for checking the condition, ng-container and ngTemplateOutlet to display the template (greet.template) in the components template as shown below −

    <p>This is content from the component template</p><ng-content></ng-content><p>This is another content from component template</p><ng-content select="[second]"></ng-content><div *ngIf="show"><ng-container [ngTemplateOutlet]="greet.template"></ng-container></div>

    Step 5: Finally, use the component and its conditional projection in the app component as shown below −

    <app-content-projection-sample><p>This is external content</p><ng-container ngProjectAs="second"><p>This is yet another external content</p></ng-container><ng-template appGreetContent>
    
      Hi,I am coming from conditional template
    </ng-template></app-content-projection-sample><router-outlet></router-outlet>

    Step 6: Now import the directive inside the AppComponent.

    import{ Component }from'@angular/core';import{ RouterOutlet }from'@angular/router';import{ ContentProjectionSampleComponent }from'./content-projection-sample/content-projection-sample.component';import{ GreetContentDirective }from'./directives/greet-content.directive';
    
    @Component({
      selector:'app-root',
      standalone:true,
      imports:[RouterOutlet, ContentProjectionSampleComponent, GreetContentDirective],
      templateUrl:'./app.component.html',
      styleUrl:'./app.component.css'})exportclassAppComponent{
      title ='projectionSample';}

    Step 7: Run the application and check the output to find whether content is rendered through conditional projection concept or not:

    conditional projection

    Step 8: Update the condition, show in the component to false and check the output to find that the ng-template content is not rendered.

    exportclassContentProjectionSampleComponent{
       show =false;
       @ContentChild(GreetContentDirective) greet!: GreetContentDirective;}
    Component Template

    Special Case: ngProjectAs Attribute

    ngProjectAs is a special attribute used to project content in complex scenarios. One example is using ng-container to layout the template. As we know, ng-container does not render itself and out-render its child content, we need to use ngProjectAs attribute to project its content.

    Let us change the above example to use ng-container and ngProjectAs attribute. Update the app component template, app.component.html as shown below −

    <app-content-projection-sample><p>This is external content</p><ng-container ngProjectAs="second"><p>This is yet another external content</p></ng-container></app-content-projection-sample><router-outlet></router-outlet>

    Here, the value of selector attribute (second) set in the component template is used in the ng-container.

    The output of the application is as shown below −

    ngProjectAs
  • Nested Components

    What is Nested Component?

    When you work with a large Angular application, placing all features in a single Component is not a good idea. As the application grows, maintaining it can become quite challenging. Therefore, we need to split it into multiple components. It may require creating components inside others to form a hierarchical structure.

    In Angular, it is possible to nest components inside each other. The outside container is known as the parent container, and the inner one is known as the child container.

    How to Create Nested Components in Angular?

    Nested components are normal Angular Components with parent-child relations. The parent can access and share data with the child, either partially or fully. Creating nested components follows the same process as creating regular components.

    Example

    Let’s look at an example of how to create nested components in an Angular application. Following are the steps −

    Step 1: Create an application named nestedApplication.

    ng newnestedApplication

    Step 2: Use the cd command to navigate inside the project folder. Then, create a component called parent using the following command −

    ng g c parent
    

    Step 3: To create child component, use the command given below −

    ng g c parent/child
    

    Here, child will be created inside the parent component. And, your project structure will look like this −

    nested component project structure

    Step 4: Next, make changes in the child.component.html file:

    <p>This is child component. It is inside parent component.</p>

    Step 5: Add the below code snippet to the parent.component.html file:

    <p>This is child component. It is inside parent component.</p>

    Step 6: Now, call the ParentComponent inside the app.component.html file:

    <h2>Nested Component Example</h2><app-parent></app-parent><router-outlet />

    Step 7: After working on the HTML files, it’s time to update all the TypeScript files. Start with the child.component.ts file:

    import{ Component }from'@angular/core';
    
    @Component({
      selector:'app-child',
      standalone:true,
      imports:[],
      templateUrl:'./child.component.html',
      styles:' p { border: 1px solid blue; border-radius: 25px; padding: 16px 32px; } '})exportclassChildComponent{}

    Step 8: Next, import the ChildComponent in parent.component.ts file:

    import{ Component }from'@angular/core';import{ ChildComponent }from'./child/child.component';
    
    @Component({
      selector:'app-parent',
      standalone:true,
      imports:[ChildComponent],
      templateUrl:'./parent.component.html',
      styleUrl:'./parent.component.css'})exportclassParentComponent{}

    Step 9: Finally, call the ParentComponent by importing it inside app.component.ts file:

    import{ Component }from'@angular/core';import{ RouterOutlet }from'@angular/router';import{ ParentComponent }from'./parent/parent.component';
    
    @Component({
      selector:'app-root',
      standalone:true,
      imports:[RouterOutlet, ParentComponent],
      templateUrl:'./app.component.html',
      styleUrl:'./app.component.css'})exportclassAppComponent{
      title ='nestedApplication';}

    Once you save all the code changes and run the application using ng s command, you will get the following output −

    Nested Containers
  • Component Styling

    Styling is the process of designing and formatting the visual presentation of web pages. Since Angular applications are composed of multiple components, styles are applied to them individually. Angular components can be styled using CSS as well as popular pre-processors such as Sass and Less.

    Let’s see different techniques of applying styles to the Angular components and different ways to allow safe and efficient customization of styles by the component users in this chapter.

    How to Add Styles to Angular Components?

    You can use the following ways for styling Angular components −

    • Using “styles”
    • Using “styleUrls”
    • Styling through template
    • Using global styles
    • Using CSS preprocessor
    • Customized styles
    • Using custom selectors

    Using “styles”

    The simplest and fastest way to style a component is by usingstyles. It is a property of the@Componentdecorator. It accepts an array of CSS styles and applies it to the component view. The pseudo-code of using styles in a component is as follows:

    @Component({// selector and template code
       
       styles:['p { font-style: italic; }', 'em { font-style: italic;
       font-weight: bold }']// other properties / meta data})exportclassMyComponent{// ...}

    The style applied using styles will affect the component view generated through component template only and is not inherited by the view of the nested components in the component template as well as any content projected in the component view.

    Using “styleUrls”

    The styleUrls is also a property of the @Component decorator and is similar to styles property. It accepts an array of CSS style file paths instead of the style itself and applies it to the component view. The pseudo-code of using styleUrls in a component is as follows:

    @Component({// selector and template code
       
       styleUrls:['my-component.component.css','my-component-another.component.css']// other properties / meta data})exportclassMyComponent{// ...}

    The style applied using styleUrls will affect the component view generated through the component template only and is not inherited by the view of the nested components in the component template as well as any content projected in the component view.

    Styling Through Template

    Styling through template is old school method of using CSS styles in the HTML itself through template property of @Component decorator. We can style a component using template in two ways, which are:

    • Applying styles using HTML style tag
    • Linking CSS files through link tag

    Styling through template does not restrict the styles to the component view only and applies to the entire HTML generated from the component including nested components and projections.

    Applying Style Using “style” Tag

    In HTML, style tag is used to define internal CSS styles for a web page. This tag allows you to embed CSS rules directly within the HTML document. The use of this tag in the Angular template is similar to how we use it in HTML. The pseudo-code to apply styles using style tag is as follows:

    @Component({// selector
       
       template:`
    
      &lt;style&gt;
         h1 {
               color: red;
         }
      &lt;/style&gt;
      &lt;h1&gt;Hello&lt;/h1&gt;
    `,// other properties / meta data})exportclassMyComponent{// ...}

    Applying Style Using “link” Tag

    The most common use of link tags in HTML documents is to attach external stylesheets. The pseudo code to apply styles to an Angular component using link tag is as follows:

    @Component({// selector
       
       template:`
    
      &lt;link rel="stylesheet" href="../assets/my-component.component.css"&gt;
      &lt;h1&gt;Hello&lt;/h1&gt;
    `,// other properties / meta data})exportclassMyComponent{// ...}

    The CSS file path should be relative to the component file. Otherwise, the CSS path in the generated HTML may be pointing to an incorrect or non-existing file.

    @import can be used in CSS files to load more CSS files and the import file should be relative to the host CSS file.

    @import'./another-css.css'

    Using Global Styles

    Global styles will be configured during the application creation itself. Angular CLI will set src/styles.css file as a global stylesheet. We can use it to set styles targeting anything in the application. We can use the global setting to set one or more style sheets as required. The below snippet shows the initial angular configuration created during application creation through angular CLI.

    {"$schema":"./node_modules/@angular/cli/lib/config/schema.json","version":1,"newProjectRoot":"projects","projects":{"tutorial":{// ..."architect":{"build":{"builder":"@angular-devkit/build-angular:browser","options":{"outputPath":"dist/tutorial","index":"src/index.html","main":"src/main.ts","polyfills":["zone.js"],"tsConfig":"tsconfig.app.json","assets":["src/favicon.ico","src/assets"],"styles":["src/styles.css"],"scripts":[]},"configurations":{"production":{// ...},"development":{// ...}},"defaultConfiguration":"production"},}}}}

    Using CSS Preprocessor

    Angular allow not only the plain CSS to style the component. It allows the popular CSS preprocessors such as Sass and Less. Using Sass and Less is as simple as including the Sass and Less files instead of CSS files.

    Component styleUrls can be used to include Sass and Less as specified below.

    @Component({// selector and template code
       
       styleUrls:['my-component.component.scss']// other properties / meta data})exportclassMyComponent{// ...}

    CSS preprocessor can be configured at project level or component level through angular settings. Angular CLI command “ng new” will ask the preprocessor to set during initial project creation.

    $ ng newmyapp? Would you like to add Angular routing? No
    ? Which stylesheet format would you like to use?(Use arrow keys)CSSSCSS[ https://sass-lang.com/documentation/syntax#scss                ]
      Sass   [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]
      Less   [ http://lesscss.org  
    

    Angular CLI will configure the project with proper CSS preprocessor.

    Customize Styles of Angular Component

    Generally, every angular component will have a default style associated with it. As components can be used in various scenarios, there is a necessity to customize the style of the component to match a given scenario. A good component should have a clear approach to changing the style to suit the environment where the component is used. Angular provides four ways to customize the style of a component.

    • CSS custom properties
    • Global CSS along with @mixins
    • Using pseudo selectors of shadow DOM
    • Using component properties / API

    CSS Custom Properties

    CSS provides CSS variables to hold styles. Once the CSS variables are defined, they can be used in any CSS properties and allow certain styles to be changed across the application by changing the value of the CSS variable.

    Let us see how to define and use CSS variables. CSS variables can be created using :root pseudo selector as shown below −

    :root{--myColor: red;}

    Here, myColor variable is defined and its initial value is red. myColor variable can be used in other CSS properties as shown below −

    p{color:var(--myColor, grey);}

    Here, color will be set with the value of myColor (red in this case). If myColor does not have any value or undefined, then grey will be set as color.

    The same concept can be applied in a component by using component property and CSS Variable as shown below.

    • Declare a property for the CSS variable, myColorVar in the component as shown below −
    @Component({// setting})exportclassMyComponent{
       myColorVar: string =null;}
    • Use CSS variable, myColor and Component property, myColorVar in the style (template) as shown below −
    <p [style.--myColor]="myColorVar">Hello</p>
    • Now, the component can be customized by changing the CSS variable in the :root pseudo selector as shown below −
    :root{--myColor: green;}

    Global CSS with @mixins

    CSS preprocessor provides a great option to mix different style as and when needed in the CSS using mixin concept. Mixin is simply grouping of one or more CSS styles with a name and using it afterwards wherever necessary using the defined name. Let us define two mixin, one for color and another for font style:

    @mixin color($c){color: $c;}@mixin fontstyle($fs){font-style: $fs;}

    Next, we can use the mixin to define a global style using the @include option as shown below −

    .mystyle {
       @include color('red');
       @include fontstyle('italic');}

    Finally, the user can customize the color and font style in the global style without interfering with the component.

    Pseudo Selector of Shadow DOM

    Components using shadow DOM can be set with part attributes to allow the users to customize the style using ::part pseudo selector in the CSS styles as shown below −

    <template id="my-web-component"><h1 part="hello">Hello</h1></tempalte>

    Here, my-web-component web component sets the part attribute for the h1 tag.

    my-web-component::part(hello){// custom css styles}

    Here, h1 tag is targeted through ::part selector and can be customized.

    Component Properties

    The final and least preferred way to customize the style of the component is through custom component property as shown below −

    @Component({// ...})exportclassMyComponent(){
       color: string;}

    Here, component will use the properties to get the color information from the user and set it in the template.

    Using Custom Selectors

    Angular provides two custom selectors similar to the shadow DOM concept.

    • :host
    • :host-context

    :host selector is used to style the host element of the component. It comes in two forms. First one is a simple form as shown below −

    Here, :host selector applies to the host element and all its descendant elements.

    The second one is the function form, which will target the host element only if the host element has the included selector (as arguments) as shown below −

    :host(.active){font-weight: bold;}

    Here, :host(.active) targets the host element only if it has an active class.

    Finally, :host-context selector is similar to host function form :host() except it checks whether any of the ancestor of the host element has the specified selector (in the argument).

    :host(.active){font-weight: bold;}

    Here, the style applies to the host only when any of the ancestors of the host element has .active class assigned to it.

  • Component Interaction

    Sharing Data between Angular Components

    Angular provides options to share data from parent component to child component as well as, child to parent component. Also, it is possible to share data between any other component within the Angular application. Component is a TypeScript class decorated with the @Component decorator. It is used to create the user interface.

    Data sharing is the practice of making important information accessible to different parts of an application. It is done by transferring data from one component to another, allowing access to data during operations, or synchronizing data between different parts of an application.

    Interaction between components is one of the important and necessary features in the context of component based architecture. Angular provides multiple options to pass and receive data between components. Let us see how to share data between components in this tutorial.

    How Components Interact in Angular?

    In Angular, parent and child components shares data or interacts to each other through the following ways −

    Using @Input Decorator

    The parent component can pass the data to the child component through the @Input()decorator. The child component receives data during its entire life cycle from initialization to destruction. Angular is designed in such a way that the child component automatically intercepts the data changes from the parent component and does the necessary update. Angular also provides hooks to intercept the data changes and write custom processing.

    Overall, we have three ways for the child component to intercept data from parent component −

    • Auto interception
    • Setter interception
    • ngOnChanges hook

    Auto Interception

    Auto interception simplifies receiving the data from parent component. Angular provides a decorator @Input to receive data from parent component. It receives the data whenever the parent component updates the data Configuring Input decorator is very simple. Just append the input decorator to a property in the child component and then pass the data from parent component through child component attributes in the template.

    Example

    Let us create a child component and then try to pass the data from parent to child and render it in the child component.

    Step 1: Create a new child component, InOutChildSample using angular CLI as shown below −

    $ ng generate component InOutChildSample
    CREATE src/app/in-out-child-sample/in-out-child-sample.component.html(35 bytes)CREATE src/app/in-out-child-sample/in-out-child-sample.component.spec.ts(688 bytes)CREATE src/app/in-out-child-sample/in-out-child-sample.component.ts(295 bytes)CREATE src/app/in-out-child-sample/in-out-child-sample.component.css(0 bytes)

    Step 2: Add a counter property in the child component and decorate it with @Input() decorator as shown below −

    import{ Component, Input }from'@angular/core';
    
    @Component({
       selector:'app-in-out-child-sample',
       templateUrl:'./in-out-child-sample.component.html',
       styleUrls:['./in-out-child-sample.component.css']})exportclassInOutChildSampleComponent{
       @Input() counter : number =0;}

    Step 3: Open the child component template, in-out-child-sample.component.html and use the counter property as shown below −

    <div><p>Counter: {{counter}}</p></div>

    Step 4: Open the parent component template, app.component.html and render the child component along with counter attribute as shown below −

    <app-in-out-child-sample [counter]="counter"></app-in-out-child-sample><router-outlet />

    Step 5: Open the app.component.ts file and add the code given below −

    import{ Component }from'@angular/core';import{ RouterOutlet }from'@angular/router';import{ InOutChildSampleComponent }from'./in-out-child-sample/in-out-child-sample.component';
    @Component({
      selector:'app-root',
      standalone:true,
      imports:[RouterOutlet, InOutChildSampleComponent],
      templateUrl:'./app.component.html',
      styleUrl:'./app.component.css'})exportclassAppComponent{
      title ='sample-app';
      counter =10;}

    Step 6: Finally, run the application and check that the counter shows whether the value passed from the parent component or not −

    counter

    Setter Interception

    The setter based interception is just an extension of the previous technique. It basically uses a getter and setter for the property used in @Input decorator.

    Example

    Let us change our counter example, in-out-child-sample.component.ts to intercept the counter using setter and reset the counter to 1, if the counter value exceeds 25.

    import{ Component, Input }from'@angular/core';
    
    @Component({
      selector:'app-in-out-child-sample',
      standalone:true,
      imports:[InOutChildSampleComponent],
      templateUrl:'./in-out-child-sample.component.html',
      styleUrl:'./in-out-child-sample.component.css'})exportclassInOutChildSampleComponent{
      @Input()getcounter(): number {returnthis._counter;}setcounter(val: number){this._counter = val ||0;if(val >25)this._counter = val %25;}private _counter: number =1;}

    Add a function to increment the counter value in the parent component as shown below −

    inc(){this.counter++}

    Add a button in the parent components template and bind the function as shown below −

    <button(click)="inc()">Increment counter</button><app-in-out-child-sample [counter]="counter"/>

    Run the application, and you can see that the counter will reset to 1 once it reaches 25.

    Counter Reset

    The ngOnChanges Hook Interception

    As we learned in the life cycle of a component and its hook methods, ngOnChanges is a hook method, which will run whenever angular detects changes in its input.

    ngOnChanges hook accepts an object of type SimpleChanges. SimpleChanges is a dictionary having all properties with changes. We can go through all the properties and find the latest value of the property. The pseudo-code to go through all changed properties is as follows −

    ngOnChanges(changes: SimpleChanges){for(const key in changes){const prop = changes[key];const prevVal = prop.previousValue
    
      const currentVal = prop.currentValue
      cont isFirstChange = pop.isFirstChange()if(prop.isFirstChange()){
         console.log("The current value is ${prop.currentValue}")}else{
         console.log(`${key} changed from ${prop.previousValue} to
         ${prop.currentValue}`);}}}</pre>

    Using @Output Decorator

    Child component can send the data to parent component through the @Output decorator. Use of this decorator is quite simple and similar to @Input decorator except that the output is actually an event emitter passing the data (output) along with event. The parent component can be subscribed for the event in the child component and get the emitted value from the child component whenever the data is changed in the child component.

    Example

    Let us write an @output decorator in our child component, InOutChildSample component and try to get the output from the parent component.

    Step 1: Create an output event emitter in the child component, in-out-child-sample.component.ts along with a method to pass the value of the counter by emitting the event along with counter data in the child component, in-out-child-sample.component.ts.

    import{ Component, Input, Output, EventEmitter }from'@angular/core';
    
    @Component({
      selector:'app-in-out-child-sample',
      standalone:true,
      imports:[InOutChildSampleComponent],
      templateUrl:'./in-out-child-sample.component.html',
      styleUrl:'./in-out-child-sample.component.css'})exportclassInOutChildSampleComponent{
      @Input() counter : number =0;
      @Output() counterEvent =newEventEmitter<number>();passCounterToParent(){this.counterEvent.emit(this.counter)}}

    Step 2: Open the child component template, in-out-child-sample.component.html and add a button to invoke the counter event when the user clicks the button.

    <div><p>Counter:{{counter}}</p><button(click)="passCounterToParent()">Pass Counter to Parent</button></div>

    Here,

    • click is the button click event and it is configured to run passCounterToParent() function when it is clicked.

    Step 3: Add a variable in the parent component to hold the output data passed through event from child component. Also, add a function in the parent component to get the output data passed through event from child component.

    import{ Component }from'@angular/core';import{ RouterOutlet }from'@angular/router';import{ InOutChildSampleComponent }from'./in-out-child-sample/in-out-child-sample.component';
    @Component({
      selector:'app-root',
      standalone:true,
      imports:[RouterOutlet, InOutChildSampleComponent],
      templateUrl:'./app.component.html',
      styleUrl:'./app.component.css'})exportclassAppComponent{
       title ='sample-app';
       counter =10;
       childCounter: number =0;inc(){this.counter++}get(val: number){this.childCounter = val;}}

    Step 4: Open the parent component template, app.component.html and add the code as shown below −

    <button(click)="inc()">Increment counter</button><p>Data from child:{{childCounter}}</p><app-in-out-child-sample [counter]="counter"(counterEvent)="get($event)"/>

    Here,

    • counterEvent is the event from the child component
    • get($event) is the callback function. $event will hold the current counter value.
    • childContent is the data from the child component.

    Step 5: Finally, run the application and you can see that the child component will send the updated counter value to the parent component when the button in the child component is clicked.

    child component

    Using Local Variable

    Parent components can get complete access to the child component throughlocal variable. However, parent component gets access to the child component in its template only.

    Example

    Let us create two component, ParentCounterComponent and ChildCounterComponent to understand the concept. The purpose of the ParentCounterComponent is to provide counter functionality through two button, increment and decrement button. The increment button will increment the counter and the decrement button will decrement the counter. Parent component will get the increment and decrement functionality from child component instead of implementing itself.

    Step 1: Create child component, ChildCounterComponent using angular CLI as shown below −

    ng generate component ChildCounter
    CREATE src/app/child-counter/child-counter.component.html(29 bytes)CREATE src/app/child-counter/child-counter.component.spec.ts(658 bytes)CREATE src/app/child-counter/child-counter.component.ts(273 bytes)CREATE src/app/child-counter/child-counter.component.css(0 bytes)

    Step 2: Declare a variable counter and two methods inc() and dec() to increment and decrement the counter respectively inside the child component −

    import{ Component }from'@angular/core';
    
    @Component({
      selector:'app-child-counter',
      standalone:true,
      imports:[],
      templateUrl:'./child-counter.component.html',
      styleUrl:'./child-counter.component.css'})exportclassChildCounterComponent{
      counter: number =0// increment and decrement counter  inc(){this.counter++}dec(){this.counter--}}

    Step 3: Next, open parent component's template file, app.component.html and add child component along with an id, #child to access the child component.

    <app-child-counter #child></app-child-counter>

    Step 4: Next, add two buttons and bind click events with child component's inc() and dec() methods accessed through child identifier. Also, show the current value of counter using child identifier.

    <p>counter:{{child.counter}}</p><button(click)="child.inc()">Increment</button><button(click)="child.dec()">Decrement</button><app-child-counter #child></app-child-counter><router-outlet />

    Step 5: Next, include the given code inside app.component.ts file.

    import{ Component }from'@angular/core';import{ RouterOutlet }from'@angular/router';import{ ChildCounterComponent }from'./child-counter/child-counter.component';
    
    @Component({
      selector:'app-root',
      standalone:true,
      imports:[RouterOutlet, ChildCounterComponent],
      templateUrl:'./app.component.html',
      styleUrl:'./app.component.css'})exportclassAppComponent{
      title ='exampleApp';}

    Step 6: Finally, run the application and check whether the counter is working fine.

    counter working

    Using @Viewchild Decorator

    Like the local variable, the @ViewChild decorator is also used to get complete access to the child component. However, along with template, the parent component will get access to the child component in its class environment as well. This makes a huge difference as the parent component can use the child component functionality in its methods as well.

    Example

    To understand the @Viewchild decorator, let's change the parent component, i.e. AppComponent. Now, we will use @ViewChild concept instead of local variable. We don't need to make any changes in the child component.

    Step 1: Import necessary classes from @angular/core module:

    import{ Component, ViewChild, AfterViewInit }from'@angular/core';

    Step 2: Implement AfterViewInit life cycle hook as shown below:

    exportclassVcParentCounterComponentimplementsAfterViewInit{ngAfterViewInit(){// ...}}

    Step 3: Access the child component using @ViewChild as shown below −

    @ViewChild(ChildCounterComponent)private child!: ChildCounterComponent;

    Here, @ViewChild decorator accepts the type of the child component, which is in the component's template.

    Step 4: Implement increment and decrement functionality by accessing the child component.

    inc(){this.child.inc()}dec(){this.child.dec()}

    Here, we have used this.child variable to access the functionality from child component.

    Step 5: Implement a counter functionality to retrieve the current counter value as shown below −

    counter(){return0;}ngAfterViewInit(){setTimeout(()=>this.counter=()=>this.child.counter,0)}

    Here, we have created a counter method in ngAterViewInit life cycle hook. The child component will be available only after this life cycle. So, we have created a dummy counter method (which needs to access child component's counter value ) during component initialization and the update the counter method in the hook method.

    Step 6: The complete code of the AppComponent is as follows −

    import{ Component, ViewChild, AfterViewInit }from'@angular/core';import{ RouterOutlet }from'@angular/router';import{ ChildCounterComponent }from'./child-counter/child-counter.component';
    
    @Component({
      selector:'app-root',
      standalone:true,
      imports:[RouterOutlet, ChildCounterComponent],
      templateUrl:'./app.component.html',
      styleUrl:'./app.component.css'})exportclassAppComponentimplementsAfterViewInit{
       title ='exampleApp';  
       @ViewChild(ChildCounterComponent)private child!: ChildCounterComponent;inc(){this.child.inc()}dec(){this.child.dec()}counter(){return0;}ngAfterViewInit(){setTimeout(()=>this.counter=()=>this.child.counter,0)}}

    Step 7: Next, open the component's template file, app.component.html and add the child component along with button and method binding as shown below −

    <p>counter:{{counter()}}</p><button(click)="inc()">Increment</button><button(click)="dec()">Decrement</button><app-child-counter></app-child-counter>

    Here, we have not included the identifier and used only the parent components functionality instead of child component (which we have done in previous example, local variable concept). The parent component will get the required functionality from child variable, which it got through @ViewChild decorator.

    Step 8: Finally, run the application and check that the counter shows that the value passed from parent component as shown below −

    parent component

    Using a Common Service

    Service is an integral part of the angular framework. We can create a service to implement specific functionality and then use it in any component. The best use cases of services are as follows:

    • API calls
    • Utility functions
    • Sharing data between components

    Example

    Let us learn how to use services to share data between components in this section. We will learn the step by step process to share data through a service in this example.

    Step 1: Create a service, MyCounterService using angular CLI as shown below −

    ng g service services/MyCounter
    CREATE src/app/services/my-counter.service.spec.ts(389 bytes)CREATE src/app/services/my-counter.service.ts(147 bytes)

    It is better to put all the services inside a single folder. Therefore, we are creating MyCounter inside service folder using the above command.

    Step 2: Create a component, MyCounterServiceComponent using angular CLI as shown below −

    ng generate component MyCounterService
    CREATE src/app/my-counter-service/my-counter-service.component.html(34 bytes)CREATE src/app/my-counter-service/my-counter-service.component.spec.ts(687 bytes)CREATE src/app/my-counter-service/my-counter-service.component.ts(292 bytes)CREATE src/app/my-counter-service/my-counter-service.component.css(0 bytes)

    Step 3: Create an observable object to track the value of counter variable in the service as shown below −

    private counterSource =newSubject<number>();public counter$ =this.counterSource.asObservable()

    Here,

    • counterSource is a variable of type Subject. Subject is an observable object provided by rxjs library. Subject can emit and receive values.
    • Invoked asObservable method on the counterSource to hide the identity of the source sequence.

    Step 4: Implement increment and decrement methods as shown below −

    inc(val: number){this.counterSource.next(val +1)}dec(val: number){this.counterSource.next(val -1)}

    Here,

    • next() method from counterSource is used to update the value of the counter.

    Step5: The complete code of the service, MyCounterService is as follows:

    import{ Injectable }from'@angular/core';import{ Subject }from'rxjs';
    
    @Injectable({
       providedIn:'root'})exportclassMyCounterService{constructor(){}private counterSource =newSubject<number>();public counter$ =this.counterSource.asObservable()inc(val: number){this.counterSource.next(val +1)}dec(val: number){this.counterSource.next(val -1)}}

    Step 6: Now, open the my-counter-service.component.ts file and inject the service through constructor in the component.

    exportclassMyCounterServiceComponent{constructor(private counterService: MyCounterService){}}

    Step 7: Subscribe to the observables available in the service through component constructor as shown below −

    this.counterService.counter$.subscribe(counter=>{this.counter = counter;})

    Here, the subscription will update the counter value whenever there is a change in the observable.

    Step 8: Implement increment (inc()) and decrement (dec()) methods by calling counter service methods as shown below −

    inc(){this.counterService.inc(this.counter)}dec(){this.counterService.dec(this.counter)}

    Step 9: The complete code of the component, MyCounterServiceComponent is as follows,

    import{ Component }from'@angular/core';import{ MyCounterService }from'../my-counter.service'
    
    @Component({
       selector:'app-my-counter-service',
       templateUrl:'./my-counter-service.component.html',
       styleUrls:['./my-counter-service.component.css'],})exportclassMyCounterServiceComponent{
       counter: number =0;constructor(private counterService: MyCounterService){this.counterService.counter$.subscribe(counter=>{this.counter = counter;})}inc(){this.counterService.inc(this.counter)}dec(){this.counterService.dec(this.counter)}}

    Step 10: Next, open the component's template, my-counter-service.component.html and write template markup to show the current counter value and then add two more buttons to increment and decrement the counter value. Bind inc() and dec() methods to increment and decrement button's click event respectively.

    <p>counter: {{counter}}</p><button (click)="inc()">Increment</button><button (click)="dec()">Decrement</button>

    Step 11: Next, open the app component's template and include our component as shown below −

    <app-my-counter-service /><router-outlet />

    Step 12: Run the application and check the output.

    App Component

    Step 13: Next, add another component in the app component's template as shown below −

    <app-my-counter-service /><app-my-counter-service /><router-outlet></router-outlet>

    Step14: Run the application and see that incrementing one component will reflect in the other component as well. It happens as it is based on the same service.

    Two Component
  • View Encapsulation

    View encapsulation is a technique to encapsulate the style of the given view from other sections of the application. By default, the CSS style applied in an HTML document will affect the entire document. The same applies to the Angular framework as well. This default behaviour is going to be an advantage in some scenarios like global styles but at the same time, it may affect specific parts of the application unintentionally (like a special button/link with specific styles).

    To make sure that the style of the specific part of the application does not get affected, the view encapsulation concept provided by Angular can be used.

    View Encapsulation Modes in Angular

    Angular provides a property named view encapsulation in the Component decoration to direct the scope of component style. There are three modes of encapsulation, which are as follows −

    • None (ViewEncapsulation.None)
    • Emulated (ViewEncapsulation.Emulated)
    • ShadowDom (ViewEncapsulation.ShadowDom)

    ViewEncapsulation.None

    The mode None will not do anything to safeguard the style of an element inside the component. Component view will be exposed to all the global styles and get affected by it.

    Example

    Let us create a simple component to check how ViewEncapsulation.None mode works.

    Step 1: Create a new Angular application with the name sample-app using the below command −

    ng newsample-app
    

    Step 2: Navigate to the project folder using cd command. Create a new component and name it view-encapsulation-sample.

    $ ng generate component view-encapsulation-sample
    CREATE src/app/view-encapsulation-sample/view-encapsulation-sample.component.html(41 bytes)CREATE src/app/view-encapsulation-sample/view-encapsulation-sample.component.spec.ts(736 bytes)CREATE src/app/view-encapsulation-sample/view-encapsulation-sample.component.ts(320 bytes)CREATE src/app/view-encapsulation-sample/view-encapsulation-sample.component.css(0 bytes)

    Step 3: Now, go to view-encapsulation-sample.component.ts file. Add ViewEncapsulation.None mode for the component as shown below −

    import{ Component, ViewEncapsulation }from'@angular/core';
    
    @Component({
      selector:'app-view-encapsulation-sample',
      standalone:true,
      imports:[ViewEncapsulationSampleComponent],
      templateUrl:'./view-encapsulation-sample.component.html',
      styleUrl:'./view-encapsulation-sample.component.css',
      encapsulation: ViewEncapsulation.None
    })exportclassViewEncapsulationSampleComponent{}

    Step 4: Change the template, view-encapsulation-sample.component.html and add two containers as shown below −

    <div>I am inside the none container</div><div class="mystyle">I am inside the none container and has my own style</div>

    Here, the first container does not have any styles or class and it is more prone to get affected by the global styles. The second container has class attributes and yet has a chance to get affected by global styles.

    Step 5: Apply the style in the component css file, view-encapsulation-sample.component.css as shown below −

    div.mystyle { color: brown }

    Step 6: Add the component in the app component, app.component.html as shown below −

    <app-view-encapsulation-sample /><router-outlet></router-outlet>

    Step 7: On running the application, you can clearly see that the generated style and element are plain and no safeguard is applied and the application will look as shown below −

    None ViewEncapsulation Mode

    Step 8: Add a style in the global css assets, styles.css targeting div tag and re-run the application.

    div { color: blue }

    Now, the color of the first container changed to blue as shown below −

    None ViewEncapsulation Mode

    ViewEncapsulation.Emulated

    Emulated mode will change the styles in such a way that it only applies to the element inside the component only. However, global styles may still affect elements inside a component.

    Example

    Let us change our application and apply the Emulated option as shown below −

    import{ Component, ViewEncapsulation }from'@angular/core';
    
    @Component({
      selector:'app-view-encapsulation-sample',
      standalone:true,
      imports:[ViewEncapsulationSampleComponent],
      templateUrl:'./view-encapsulation-sample.component.html',
      styleUrl:'./view-encapsulation-sample.component.css',
      encapsulation: ViewEncapsulation.Emulated
    })exportclassViewEncapsulationSampleComponent{}

    Now, re-run the application and check the result −

    Emulated ViewEncapsulation Mode

    ViewEncapsulation.ShadowDom

    ShadowDom mode will apply the HTML native shadow dom concept to scope the style of the component. The element of the component will not be affected by the global styles in any situation as it completely hide using shadowDOM concept.

    Example

    Change our application and apply the ShadowDOM mode as shown below −

    import{ Component, ViewEncapsulation }from'@angular/core';
    
    @Component({
      selector:'app-view-encapsulation-sample',
      standalone:true,
      imports:[ViewEncapsulationSampleComponent],
      templateUrl:'./view-encapsulation-sample.component.html',
      styleUrl:'./view-encapsulation-sample.component.css',
      encapsulation: ViewEncapsulation.ShadowDom
    })exportclassViewEncapsulationSampleComponent{}

    Now, re-run the application and check the output.

    ShadowDOM ViewEncapsulation Mode

    Now, both containers are safeguarded by native shadowDOM concept and are not affected by the global styles.

    Applying different encapsulation in an application

    View encapsulation of a component can be different from other components used in the application as view encapsulation is applied per component basis. Even the nested component can have different view encapsulation options as per component requirements. Angular will apply the encapsulation as directed even in very complex nested component trees as well.

  • Component Lifecycle

    Angular component goes through a series of stages/events during its existence. Before moving further in the journey of learning Angular, it is necessary to understand how a component interacts with the framework and the DOM throughout its existence.

    When an angular component is constructed, it first goes through the change detection phase, where it checks whether there are any changes in the input and then acts accordingly. Then, the initialization phase kicks on and continues to other phases and finally gets destroyed in the destruction phase.

    Component Lifecycle Phases in Angular

    The different stages of the Angular Components Lifecycle are as follows −

    • Creation: It is the first phase where a component is instantiated.
    • Change Detection: Then, Angular try to detect changes in the View and Content of the application.
    • Rendering: After change detection, the new template is updated.
    • Destruction: The Component is destroyed at the end.

    Component Lifecycle Hook in Angular

    Each phase of the angular component is associated with a lifecycle hook interface which can be implemented to perform arbitrary action in that particular phase. Generally, the lifecycle hooks refer to the methods of lifecycle hook interfaces. Let’s see the phases, their sequence and the corresponding hooks.

    PhasesLifecycle HooksDescription
    CreationConstructorConstructor runs when Angular instantiates the component for the first time.
    Change DetectionngOnChanges()Change detection is the first phase, where the angular component will check the inputs for changes and act accordingly. It has a corresponding lifecycle hook, ngOnChanges(). This hook runs before the ngOnInit() during the first initialization process.
    ngOnInit()The ngOnInit() lifecycle hook runs exactly once after ngOnChanges(). It is raised to do the necessary initialization process based on the initial input.
    ngDoCheck()Next, Angular tries to detect the changes in the component and act accordingly. The lifecycle hook used for checking is ngDoCheck(). This hook is invoked even if there is not change in the input bound properties. Avoid defining this hook as it might affect the page’s performance.
    ngAfterContentInit()This lifecycle hook is called only once after the initialization of all children nested inside the content of component.
    ngAfterContentchecked()It is invoked during every change detection phase after the children nested inside the component’s content have been checked for changes.
    ngAfterViewInit()Next is the view initialization phase, where angular sets the various child views of the component template. The lifecycle hook used for view initialization phase is ngAfterViewInit().
    ngAfterViewchecked()Now, Angular tries to detect the changes in the view of the component/directive. The lifecycle hook for view checking phase is ngAfterViewchecked().
    RenderingafterNextRender()It runs only once when all components have been rendered to the DOM.
    afterRender()Runs every time after all components have been rendered to the DOM.
    DestructionngOnDestroy()In the final phase, ngOnDestroy() hook is called to destroy the component/directive.

    Execution Order of Component Lifecyle Hooks

    Let us see the lifecycle sequence of an arbitrary component/directive through its hooks.

    • ngOnChanges
    • ngOnInit
    • ngDoCheck
    • ngAfterContentInit
    • ngAfterContentChecked
    • ngAfterViewInit
    • ngAfterViewChecked
    • ngOnChanges
    • ngDocheck
    • ngAfterContentChecked
    • ngAfterViewchecked
    • Repeation of Step 8 – 11 until destruction
    • ngOnDestroy

    Example

    Let us create a new component, MyLifecycleComponent, wire up all hooks and check the sequence of the lifecylce using console output.

    Step 1: Create a new component using angular CLI as shown below −

    ng generate component my-lifecycle-sample
    

    This will create a new component and its related template and styles as shown below.

    $ ng generate component my-lifecycle-sample
    CREATE src/app/my-lifecycle-sample/my-lifecycle-sample.component.css(0 bytes)CREATE src/app/my-lifecycle-sample/my-lifecycle-sample.component.html(34 bytes)CREATE src/app/my-lifecycle-sample/my-lifecycle-sample.component.spec.ts(638 bytes)CREATE src/app/my-lifecycle-sample/my-lifecycle-sample.component.ts(252 bytes)

    Step 2: Add all lifecycle hooks into the component and log messages:

    import{ Component, OnChanges, OnInit, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy }from'@angular/core';
    
    @Component({
       selector:'app-my-lifecycle-sample',
       templateUrl:'./my-lifecycle-sample.component.html',
       styleUrls:['./my-lifecycle-sample.component.css']})exportclassMyLifecycleSampleComponentimplementsOnChanges, OnInit {ngOnChanges(){
    
      console.log("Change detection")}ngOnInit(){
      console.log("Initialization of component / directive")}ngDoCheck(){
      console.log("Custom change detection")}ngAfterContentInit(){
      console.log("Content initialization")}ngAfterContentChecked(){
      console.log("Checking changes in content")}ngAfterViewInit(){
      console.log("View initialization")}ngAfterViewChecked(){
      console.log("Checking changes in views")}ngOnDestroy(){
      console.log("Destruction of component / directive")}}</code></pre>

    Step 3: Add the component in the app components template app.component.html.

    <app-my-lifecycle-sample></app-my-lifecycle-sample><router-outlet></router-outlet>

    Run the application using ng serve and test the console through developer tool in the browser. It will show all the lifecycle events executing in the above discussed order.

    lifecycle events
    component example angular
  • Components

    Components are the building blocks of an Angular application. The primary use of Angular Component is to generate a section of web page called View. By combining different views, a single application is created. Every component will have an associated template and it will be called in order to generate views.

    Let us learn the basic concept of components in this tutorial.

    Structure of Angular Component

    Each component of an Angular application has a few important parts which are as follows −

    • @component Decorator: All the application related configurations are written inside this decorator.
    • HTML Template: View of the Angular application.
    • Styles: It controls the styles of a view.
    • TypeScript Class: Code related to behavior of the Angular application goes into this class.

    By default, all these parts are created by Angular CLI, you can update, add or even delete them if any of them is not required.

    Component Structure

    How to Create Components in Angular?

    In Angular, a new Component is created using the ng generate component command as specified below −

    ng generate component name-of-component
    

    Example

    Let’s see an example where we create a new component in our ExpenseManager application. This component will contain our first expense entry. First, open the command prompt and navigate to ExpenseManager application.

    cd expense-manager
    

    Now, use the command given below to create a expense-entry component −

    ng generate component expense-manager
    

    Following files and folders will be created by Angular CLI on the above command −

    CREATE src/app/expense-entry/expense-entry.component.html(28 bytes)CREATE src/app/expense-entry/expense-entry.component.spec.ts(671 bytes)CREATE src/app/expense-entry/expense-entry.component.ts(296 bytes)CREATE src/app/expense-entry/expense-entry.component.css(0 bytes)UPDATE src/app/app.module.ts(431 bytes)

    Here,

    • ExpenseEntryComponent is created under src/app/expense-entry folder.
    • Component class, Template and stylesheet are created.

    Next, we add a title property to ExpenseEntryComponent, i.e., (src/app/expense-entry/expense-entry.component.ts) component.

    import{ Component, OnInit }from'@angular/core';
    
    @Component({
       selector:'app-expense-entry',
       standalone:true,
       imports:[ExpenseEntryComponent],
       templateUrl:'./expense-entry.component.html',
       styleUrl:'./expense-entry.component.css'})exportclassExpenseEntryComponentimplementsOnInit{
       title: any;constructor(){}ngOnInit():void{this.title ="Expense Entry"}}

    Update template, src/app/expense-entry/expense-entry.component.html with below content.

    <h3>{{title}}</h3><ul><li><b>Item:</b> Pizza</li><li><b>Amount:</b>20</li><li><b>Category:</b> Food</li><li><b>Location:</b> Zomato</li><li><b>Spend On:</b> Dec 2024</li></ul>

    Open src/app/app.component.html and include the newly created component.

    <h1>Expense Management Application</h1><app-expense-entry></app-expense-entry><router-outlet />

    Here,

    app-expense-entry is the selector value and it can be used as a regular HTML Tag.

    At the end, import ExpenseEntryComponent to the app.component.ts file as shown below −

    import{ Component }from'@angular/core';import{ RouterOutlet }from'@angular/router';import{ ExpenseEntryComponent }from'./expense-entry/expense-entry.component';
    
    @Component({
     selector:'app-root',
     standalone:true,
     imports:[RouterOutlet, ExpenseEntryComponent],
     templateUrl:'./app.component.html',
     styleUrl:'./app.component.css'})exportclassAppComponent{
     title ='expense-manager';}

    Finally, the output of the application is −

    component example angular

    Component Lifecycle Hook

    Angular component goes through a series of stages/events during its existence. The different stages of the Angular Components Lifecycle are creation, change detection, rendering and destruction.

    Each phase of the angular component is associated with a lifecycle hook interface which can be implemented to perform arbitrary action in that particular phase. The lifecycle hooks refer to the methods of lifecycle hook interfaces.

    Component Interaction

    Component interaction is one of the important and necessary features in the context of component based architecture. Angular provides multiple options to pass and receive data between components.

    You can share data from parent component to child component as well as, child to parent component. Also, it is possible to share data between any other component within the Angular application.

    In Angular, parent and child components interacts through the following ways −

    • @Input decorator
    • @Output decorator
    • local variable
    • @Viewchild decorator
    • Services

    Component Styling

    Component styling is the process of designing and formatting the visual presentation of views or components. You can use the following ways for styling:

    • Using “styles”
    • Using “styleUrls”
    • Styling through template
    • Using global styles
    • Using CSS preprocessor
    • Customized styles
    • Using custom selectors

    Nested Components

    Nested components are normal Angular Components with parent-child relations. The parent can access and share data with the child, either partially or fully. The component nested inside another component is called child component. The component containing the child component is called the parent component.

    Dynamic Components

    Angular allows the component to be dynamically created and loaded at run time at a specific location in the host (another) component.

    You can create dynamic components in Angular using the following ways −

    • Using NgComponentOutlet
    • Using ViewContainerRef