Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Building Nested Component using Angular 2

3.67/5 (5 votes)
1 Jan 2017CPOL3 min read 21.5K   245  
Building nested component using Angular 2 and passing data between parent & Child component

Introduction

In this article, we will be looking into how to create a nested component using Angular 2 framework and pass data from parent component to the child component. Before reading this article, I would recommend reading two of my prior articles for better understanding. This article refers to source code available in a previous article available here.

I would like to get feedback on this article. Please feel free to share your comments below. If you like this article, please don't forget to rate it.

Prerequisites

  • Angular 2 development environment already setup (for reference, click here)
  • Basic running Angular 2 App (for reference, click here)

Let's Get Started

Let's get started, copy the previous article source code (available here) to new folder nestedComponent. Inside nestedComponent folder, we need to re-install npm packages using npm install. This is how your folder should look like after npm install.

In this article, we will move label which was getting rendered at the bottom of student-form component to nested component. This is how our student-form.html will look like after labels are removed.

HTML
<div>
    Student First Name :- <input [(ngModel)] = "Student.FirstName" type="text"><br/>
    Student Last Name :- <input [(ngModel)] = "Student.LastName" type="text"><br/>
    Student Age :- <input [(ngModel)] = "Student.Age"  type="text"><br/>
</div>

Now let's add a new file in app folder and name it student-detail.component.ts. This file will have logic for the student-detail component.

JavaScript
import { Component, Input } from '@angular/core';

@Component({
  selector: 'student-detail',
  templateUrl: '../view/student-detail.html'
})

export class StudentDetailComponent {
   @Input() FirstName:String;
   @Input() LastName:String;
   @Input() Age:number;
}

In this component, we will be passing data from parent control to be rendered. To achieve this, we will also import Input from angular/core. We have defined selector and templateUrl at line 4 & 5. To map parent input with Student-Detail component, we have defined @input() and defined FirstName, LastName & Age at line 9, 10 & 11.

We can also have variable name and attribute name different. To have different attribute name, name needs to be defined inside bracket for example @Input('first-name')

JavaScript
import { Component, Input } from '@angular/core'; 

@Component({ 
    selector: 'student-detail', 
    templateUrl: '../view/student-detail.html' 
}) 

export class StudentDetailComponent {  
    @Input('first-name') FirstName:String; 
    @Input('last-name') LastName:String; 
    @Input() Age:number; 
}

Similarly, we can also evolve methods from parent control using Output. To achieve this, we will have to inject Output & EventEmitter dependency and then add a method to evoke parent method.

JavaScript
import { Component, Input, Output, EventEmitter } from '@angular/core';
 
@Component({
  selector: 'student-detail',
  templateUrl: '../view/student-detail.html'
})

export class StudentDetailComponent { 
   @Input('first-name') FirstName:String;
   @Input('last-name') LastName:String;
   @Input() Age:number;

   @Output('onButtonClick') buttonClick = new EventEmitter();

   onCLick(){
     this.buttonClick.emit();
   }
}

Now we need to add HTML file we referred at line 5. Add this file inside view folder and name it student-detail.html.

HTML
<section>
    <br/>
    <div>Student Details are ...</div>
    <div>
      First Name : {{FirstName}} <br/>
      Last Name : {{LastName}} <br/>
      Age : {{Age}}
    </div>
    <button (click)="onCLick()">Click Me</button>
    <br/>
    <ng-content></ng-content>
</section>

In the HTML template we just added, we are referring to variable defined in student-detail.component.ts at line 5,6 & 7. I have highlighted this variable name in the above HTML template. At line 7, we have mapped onClick method which will internally evoke parent control method mapped to it.

In the HTML template, we have also added ng-content tag. This tag will be rendering content we place between student-detail tag. We will discuss this more when we add student-detail tag in our main component student-form. Let's start injecting our new created component in student-form. Here is the updated template with student-detail tag.

HTML
<div>
     Student First Name :- <input [(ngModel)]="Student.FirstName" type="text"><br/>
     Student Last Name :- <input [(ngModel)]="Student.LastName"  type="text"><br/>
     Student Age :- <input [(ngModel)]="Student.Age" type="text"><br/>
    <student-detail first-name="{{Student.FirstName}}" last-name="{{Student.LastName}}" 
     Age="{{Student.Age}}" (onButtonClick)="onButtonClick()">
        <div>This is inside nested control</div>
    </student-detail>
</div>

At line 5 & 6, we have added tag student-detail attributes, FirstName LastName & Age with respective parent variable mapping. onButtonClick() method is mapped to child control. We have used the curly bracket for mapping variable but the same can also be achieved using square bracket as mentioned below:

HTML
<student-detail [first-name]="Student.FirstName" [last-name]="Student.LastName" 
[Age]="Student.Age" (onButtonClick)="onButtonClick()">
        <div>This is inside nested control</div>
 </student-detail>

Now we need to add onButtonClick method to student.component.ts.

JavaScript
import { Component } from '@angular/core';
import { StudentModel } from './student.model'
 
@Component({
  selector: 'student-form',
  templateUrl: '../view/student-form.html'
})
export class StudentComponent { 
    Student : StudentModel = new StudentModel(); 

    onButtonClick = function(){
      alert('Hey, button was clicked in child control');
    }
}

If you have noticed div tag between student-detail tag, the same will be passed to student-detail component and will be rendered inside ng-content tag which we have added inside student-detail component. If you have worked with Angular 1.x, this is similar to ng-transclude.

The final step is to map dependency in student.module.ts. Below is updated student.module.ts and I have highlighted changes made to this file.

JavaScript
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StudentComponent }  from './student.component';
import {StudentDetailComponent} from './student-detail.component';
import {FormsModule} from '@angular/forms';

@NgModule({
  imports:      [ BrowserModule, FormsModule],
  declarations: [ StudentComponent, StudentDetailComponent ],
  bootstrap:    [ StudentComponent ]
})

export class AppModule { }

At line 4, studentDetailComponent has been imported and at line 9, we mapped the same object in declarations. This is how your final folder should look like:

Now open a command prompt and navigate to base folder & execute npm start. You should now see final out in your default browser. If you type inside textbox, the same should get displayed inside nested component.

Summary

In this article, we created a nested component which takes data from parent component and then renders it. We did also look into a couple of ways we can map data with child control.

Reference

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)