Angular Component Tutorial: Inputs, Outputs, and EventEmitters
In this post I will discuss the basics of Angular components. I’ll cover what makes up an Angular component, and give a brief demonstration using component I/O. This post is part of a series of posts covering various basics of Angular. If you’d like to start from the beginning I’ve listed the previous posts below. This series is written under the assumption that the Angular CLI is being used while developing an Angular project. The components we will be discussing will be generated using the CLI.
Before we jump in, here are some links to other Angular Tutorials I’ve put together:
- Angular Tutorial: Getting started with the Angular CLI
- Angular Component Lifecycle
- Angular Router Tutorial: Setting Up Routing in Your Application
- Angular Module Tutorial: Application Structure Using Modules
- Using Bootstrap 4 with Angular
- Electron Tutorial: Getting started with Electron and Angular CLI
What is an Angular Component?
Angular components are the building blocks of any Angular application. A component is where the content of an application is placed. That means components provide the views or templates for your application, as well as, the logic behind managing the data bound to those views. When using the Angular CLI to generate components (always use the CLI), you will be provided with four files for every component you generate. I briefly covered these files in my introduction to the CLI, but will go over them again here. Below I have pasted the piece of an Angular component’s .ts file that will tell your application that this file is no ordinary typescript file, but rather an Angular component.
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] })
If you are following along in the series, this will look similar to an Angular module, but instead of the @NgModule
decorator, we are using the @Component
decorator. The behavior of the decorators is somewhat similar. By that I mean that the decorator is telling our application that this file is special. Furthermore, inside of the decorator, we are defining the pieces of this given component. Similarly, to the content inside of an @NgModule
in which we declare the content inside of a given section of our application. This component comes from our application AppComponent
. Let’s take a look at what we are defining here.
selector: 'app-root'
- This is a tag that our application will use to select this component. Meaning that if we want to place this component somewhere with our application, all we have to do is add
<app-root></app-root>
inside the view of another component. - Two quick notes on that
- You wouldn’t place the
AppComponent
somewhere inside a different component, this is just for explanation purposes. - If you do want to use the selector to place your components inside other components, they will have to be known by a common module. I cover this in my post.
- You wouldn’t place the
- This is a tag that our application will use to select this component. Meaning that if we want to place this component somewhere with our application, all we have to do is add
templateUrl: './app.component.html'
- This is telling your application what template or view to render in association with this Angular component.
- The CLI generates an html file for you when you create a new component but if you want you can write your html in this section using back-ticks. I don’t recommend it but it doesn’t hurt to know.
styleUrls: ['./app.component.css']
- This defines a component specific style sheet. The CLI also generates this file for every component you create. If you want to add a style definition that is specific to a given component, add it to that component’s style sheet.
- You may notice that it is an array. This means that you can declare multiple files here. That can be useful if you want to reuse styles between just a couple of components.
Above I mentioned that the CLI generates four files for every component. The last file we haven’t talked about is the .spec
file. This file is used to unit test your Angular component. I will be covering unit testing in a future post.
That’s the basic makeup of an Angular component. It’s really just a supped up class file with specific definitions that your application will understand. With that information out of the way, let’s generate a couple components in our example and explore some of the functionality that Angular components have.
Parent-Child Example
In my example repo I am going to create a ComponentInteractionModule
. I will add two components to the module, a ParentComponent
and a ChildComponent
. These names are not special to Angular, I am just using them for demonstration. I am going to create two mock children objects with data in my parent component. I will pass one child to one instance of the ChildComponent
and the other child to a second instance. Both children will hold a collection (array) of futbols, that we will be passing back and forth between the children using the parent as a facilitator to demonstrate component IO. I’m going to build everything first and then I will discuss what I have done. Let’s get started.
Setup the Demo: Create Module, Components, and Route
If you are not following along with the series, and are just interested in more information on components, you can skip this setup portion.
ng g module core/component-interaction
ng g component core/component-interaction/parent
ng g component core/component-interaction/child
- Open
core-routing.ts
and add a path to theParentComponent
called ‘component-interaction’
{ path: 'component-interaction', component: ParentComponent }
- Add the routerLink to the
SidebarComponent
<div class="p-2"> <a routerLink="/component-interaction">Component Interaction Example</a> </div>
With that you should be able to run ng serve
in the example and be able to route to the ParentComponent
using the sidebar.
Parent Child Demo
As I stated above, in my ParentComponent
I will add two child objects to hold some data. I will also add a passBall(id:number)
function that will be used to “pass” a ball back and forth between the child objects. When I’ve done so, my ParentComponent
looks like:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-parent', templateUrl: './parent.component.html', styleUrls: ['./parent.component.css'] }) export class ParentComponent implements OnInit { child1: {futbols: Array<boolean>, name: string, id: number} = { futbols: [true, true, true, true, true], name: 'Child 1', id: 1 }; child2: {futbols: Array<boolean>, name: string, id: number} = { futbols: [true, true, true, true], name: 'Child 2', id: 2 }; constructor() { } ngOnInit() { } passBall(id: number) { if (id == 1) { this.child1.futbols.pop(); this.child2.futbols.push(true); } else if (id == 2) { this.child2.futbols.pop(); this.child1.futbols.push(true); } } }
Now I need to create my template for my ParentComponent
. I am going to need to place my two instances of ChildComponent
inside of this template. I’ll add some classes to set the background colors of the content area that components are in.
<div class="parent"> <h6 class="text-center">I Am The Parent Component!</h6> <div class="d-flex"> <div class="col-6 child-1"> <app-child [child]="child1" (emitPass)="passBall($event)"></app-child> </div> <div class="col-6 child-2"> <app-child [child]="child2" (emitPass)="passBall($event)"></app-child> </div> </div> </div>
Before we setup our ChildComponent
, let’s talk about what we have done in the ParentComponent
. In the template we just set up, there are two pieces of non-standard HTML where we have placed our ChildComponent
inside of our ParentComponent
, like so:
<app-child [child]="child1" (emitPass)="passBall($event)"></app-child>
Here we can see that we are using the selector:
"app-child"
of ChildComponent
to place it inside the ParentComponent
.
When we add[child]="child1"
to the selector, we are passing "child1"
from the definition we declared in the ParentComponent
as data for an input variable named "child"
.
When we add (emitPass)="passBall($event)"
, we are telling our ParentComponent
to detect an event from the ChildComponent
with the name "emitPass"
. When the event occurs, it will fire the passBall(id:number)
function we wrote, with the $event
being the passed variable.
Now we just need to set up our ChildComponent
with that input variable, and output event. Once we have done so, the child.component.ts
will look like this:
import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core'; @Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrls: ['./child.component.css'] }) export class ChildComponent implements OnInit { @Input()child: {futbols: Array<boolean>, name: string, id: number}; @Output()emitPass: EventEmitter<number> = new EventEmitter<number>(); constructor() { } ngOnInit() { } passBall(){ this.emitPass.emit(this.child.id); } }
@Input()
followed by the name of the input variable and it’s type definition is how we have declared the child
input.
@Output()emitPass: EventEmitter<number> = new EventEmitter<number>()
declares our output variable emitPass
as an EventEmitter
, which is a built in Angular class used to emit custom Events. Here we declared the type of payload of the event as a number. Declaring types throughout your application will help you build strong, testable code and improve your aot
compilation.
Finally, our passBall()
function will be used to fire our emitPass
event, sending the id of our child input to the parent. Remember the parent uses the id to know who to simulate the pass from/to.
Now we just add our template for our ChildComponent
, we will need to display the futbols the child current has and add a way to fire the passBall()
function. Our template is going to look like this:
<div class="card"> <div class="card-body"> <div class="card-title"> {{child.name}} </div> <p class="card-text"> <span (click)="passBall()"> <i *ngFor="let ball of child.futbols" class="fa fa-futbol-o"></i> </span> </p> <p> Click a ball to pass it! </p> </div> </div>
The interesting Angular bits:
{{child.name}}
binds the variable with the template to display the name.
(click)="passBall()"
is a built in event that will fire our passBall()
function when that span
element is clicked.
*ngFor="let ball of child.futbols"
is an Angular directive that instantiates an instance of the template within the context of the loop for every item in an iterable. In our case we are just creating a font awesome futbol icon for every futbol in our futbols array. It’s basically a for loop for html elements.
Now, this example will look something like this (with styles added).
Conclusion
Hopefully that provides you with enough information to start adding different Angular components to your application. We have gone over what the different pieces of an Angular component are, looking at the different files and how they are used together to make up an Angular component. We explored basics of inputs and outputs of components, including using an event emitter to send signals to a parent component to do something. If you have any further questions, leave a comment. Thanks for reading.
Trackbacks/Pingbacks