Centric connect.engage.succeed

Angular components: How to talk to your children and listen to what they have to say

Geschreven door Martijn Kloosterman - 25 januari 2018

Martijn Kloosterman
Angular is an open-source, front-end framework for building applications. The web apps produced with it are very modular in design and lean heavily on reusability. It has been around for a while now (in software terms that is), and is steadily gaining new versions and functionality. All examples in this blog are based on Angular 5.

You can see the release schedule here (1).

For a new player on the field it might all seem a little bewildering – where to start, what to do or learn first? Angular comes with a great many functionalities, concepts and ideas.

Luckily the amount of resources and examples online is steadily growing. The Angular site itself contains a comprehensive example application (2) which is used to explain and show many of the concepts behind Angular. But as with all examples and demonstrations, it generally doesn’t show that one thing you’re looking for.

That’s why I put together these examples, in the shape of a little family tale, incorporating several things that I ran into when I started playing with Angular myself. It’s all about communicating – talking and listening.

We’ll be using the Node Package Manager (NPM) to install Angular-CLI, which we can then use to quickly setup a basic Angular site to get you started.

The following is a small introduction to basic component-to-component communication in Angular.

Getting started

You’ll need to have both NodeJS and NPM installed. Luckily, the NodeJS installer includes both of these, so it’s a one-stop download for us. Download the NodeJS installer from their site (3). If you’re not sure what version to get, use the LTS version and ‘next-next-next’ your way through the dialogue screens. You can work with NPM directly on the command line (DOS, Bash or PowerShell), but I prefer using a tool like Visual Studio Code (4) since it comes with a built-in text editor and other handy functions.

We will start by installing the Angular CLI environment that we will need. It will be installed globally, so you can use it from anywhere on your PC. To start this, enter the following command:

npm install -g @angular/cli

Next, we’ll start by creating a barebones application so we have something to work with. Enter the following command:

ng new family-app

The CLI will take a while as it generates your starter app and downloads all the necessary node modules.

A simple webserver will also be included in your project, so that you can run and develop locally. CLI installs and configures a couple of other things, such as webpack and a unit test environment. You can ignore those; we’re focusing only on the components.

After it’s done setting up the new app, we’ll need to start the building process and then move on to the webserver so that we can get started with the examples. Technically it’s called ‘transpiling’ because it’s converting the Typescript to JavaScript.

Your family-app will be created in a subfolder. Navigate there first.

Enter the following commands:

cd family-app
ng build
ng serve

The first line will generate all the required JavaScript from the Typescript files and the second will start the NodeJS webserver. It also keeps a watch on the files in the project, and every time you save a file it will rebuild everything for you. If your browser doesn’t start on its own, open it and go to http://localhost:4200/

You can stop the webserver with Ctrl-C in the console where you started it.

You should see the ‘Welcome to app!’ screen in your browser now.

Components

Angular uses components to compose the webpages served to your user. These are the most basic building blocks that your Angular application will consist of.

This first component (that was automatically created for you) can be found under src\app and consists of these files:

Angular components

So, let’s take a look at these components. A component is a template and a bit of JavaScript to implement it; a component can implement others, including itself. You can nest them as deep as you like in a parent-child configuration.

These are the stylesheet file, html template file and component Typescript file. If there is a .spec.ts file you can ignore or delete it; it is a test file to run unit tests on your component that we won’t go into.

The innards of the app.component.ts will look something like this right after generation:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';
}
app.component.ts

Open the app.component.html file and remove the content. We’ll be replacing it with other stuff as we progress.

Parent and child components

Let’s make a new component for our app.component to talk to. We’ll use the CLI again:

cd src\app
ng generate component child
You can shorten this last line to ‘ng g c child’ if you prefer. This creates a second component in a new subdirectory and also wires it up for you in the app.module.ts so that it is available anywhere in the module.

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

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

child.component.ts

It looks very similar to the app.component, with one minor difference. This component has an implementation of OnInit, which is actually entirely optional. Angular/CLI just adds it for you by default. NgOnInit is, as the name would suggest, a function that is fun when the component is initialized. It is not used in these examples, so I will omit it to cut back on the clutter.

If you wish to remove it you can delete ‘, OnInit’ from the first line so it won’t be imported, delete ‘impements OnInit’ from the export class line, and remove the ngOnInit function. You’ll be left with just the constructor() in the class.

The thing of interest to us right now is the selector property. It is listed as app-child, and this is the string we’ll need to know to implement the component in the parent template as a html tag.

Go back to the app.component.ts and add at the top of the file:

import { ChildComponent } from './child/child.component';

Then open app.component.html and replace the content with this:

<h1>
  This is the parent component!
</h1>
<app-child></app-child>

Save the file and this should trigger a rebuild automatically. Sometimes the built-in webserver caches files and you need to shut it down with Ctrl-C and restart it with ng serve to get rid of cached responses in your development environment.

You’ve just combined your first parent-child combination in angular. The app.component is implementing the child.component and the result is what you see in your browser. It should look something like this:

Angular child.component

Easy, wasn’t it?

But they aren’t talking to each other at all. Their relationship is strictly one way, and neither is listening to the other, so it’s pretty static and boring. Let’s do something about that.

How to tell your child something (and make sure they listen)

There are two ways for the app.component to talk directly to the child.component. You can do it in a static fashion, or you can use a variable to convey a value that can change at any given time.

In the app.component.html, extend the app-child element like this to use both static and variable implementations:

<app-child [dynamicdata]="dynamicdata" 
staticdata="This is static data!"></app-child>

Pay attention to the square brackets around dynamicdata. These are necessary to tell Angular that this is a variable and that we aren’t interested in the name but in the content. The staticdata property behaves like any other regular property you’d set on an element.

In the app.component.ts file, change the AppComponent class to this:

export class AppComponent {
  dynamicdata: string = 'This is dynamic data!';
}

This sets the string that is passed to the child component’s dynamicdata property. Now we alter the child component to be able to receive these two items.

Alter the child.component.ts file like this at the top:

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

and this in the class itself:

export class ChildComponent {
  @Input() dynamicdata: string;
  @Input() staticdata: string;

  constructor() { }

}

Now, to get some visible feedback, we’ll print the values the child component receives on the page so you can see that it actually arrived.

In the child.component.html file, replace the content with the following:

<div style="border: dashed 1px black;">

  <p>
    This is the child component
  </p>

  {{ dynamicdata }}<br/>
  {{ staticdata }}

</div>

Save all your files to trigger the rebuild.

Angular parent component

We really do like exclamation marks. Besides, this way you see that the dynamic and static data was added to the page by the child component.

To show you that it is actually dynamic, let’s update the string the child component receives. We’re adding a simple interval with a start and stop button. Update the app.component.ts like this:

import { Component } from '@angular/core';
import { ChildComponent } from './child/child.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  interval: any;
  dynamicdata: string = 'This is dynamic data!';

  start() {
    this.interval = setInterval(() => {
      this.dynamicdata = new Date().toLocaleTimeString();
    }, 1000);
  }

  stop() {
    clearInterval(this.interval);
  }
}

And add this to the app.component.html, just above the app-child element:

<button type="button" (click)="start()">Start</button>
<button type="button" (click)="stop()">Stop</button>

Save your files to trigger a rebuild and click the start button. You’ll see the ‘This is dynamic data!’ gets replaced with the current time that updates every second. This is the app.component telling the child.component new things. I’ll leave it to you to figure out what the stop button does.

How to listen to your child telling you something

Sometimes child components need to communicate back to their parents. The easiest way to do so is to implement an event emitter and have the app.component listen to the events coming from its child.

We’ll start by adding a button to the child.component.html, somewhere inside the <div> element:

<button type="button" (click)="talkBack('No!')">Be obstinate</button>

Next change the child.component.ts to this, by extending the import, adding the talk EventEmitter and creating the talkBack function:

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

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent {

  @Input() dynamicdata: string;
  @Input() staticdata: string;

  @Output() talk: EventEmitter<string> = new EventEmitter<string>();

  constructor() { }

  talkBack(say: string) {
    this.talk.emit(say);
  }

Next we update the app.component.html to hook the child’s talk EventEmitter to the talkBack function in the app.component like this:

<app-child [dynamicdata]="dynamicdata" staticdata="This is static data!" (talk)="talkBack($event)"></app-child>
<div *ngFor="let c of chatItems">Child says: {{ c }}</div>

Now we need to create a talkBack function in the app.component to handle the event for us:


import { Component } from '@angular/core';
import { ChildComponent } from './child/child.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  interval: any;
  dynamicdata: string = 'This is dynamic data!';
  chatItems: Array<string> = [];

  start() {
    this.interval = setInterval(() => {
      this.dynamicdata = new Date().toLocaleTimeString();
    }, 1000);
  }

  stop() {
    clearInterval(this.interval);
  }

  talkBack(e: string) {
    this.chatItems.push(e);
  }
}

Save your files and rebuild. The application should look something like this:


Screenshot Angular application

Almost done. Click the ‘Be obstinate’ button and see what the child component tells its parent through the EventEmitter’s emit event.

How to get your child component to do something

That’s all well and good, but sometimes children don’t listen to begin with. Getting them to do something by telling them directly is then the only solution. How do you go about doing that?

The easiest way to accomplish this is by using the ViewChild object.

First in app.component.ts, add ‘ViewChild’ to the import statements:

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

Next we’ll add a variable in the AppComponent right above the interval definition:

@ViewChild('littleTimmy') littleTimmy: ChildComponent;

Finally add this function to do the actual calling for us:

admonish() {
    this.littleTimmy.listen();
  }

<app-child #littleTimmy [dynamicdata]="dynamicdata" staticdata="This is static data!" (talk)="talkBack($event)"></app-child>

Open app.component.html and add ‘#littleTimmy’ to the <app-child> element:

<app-child #littleTimmy [dynamicdata]="dynamicdata" staticdata="This is static data!" (talk)="talkBack($event)"></app-child>
 

In the same file, add a button below the start and stop buttons that were already there:

<button type="button" (click)="admonish()">Make Timmy stop</button>

With this you make the child component available in your code. To do this, you need two things; the component needs a #somename definition (#littleTimmy in this case) and you need to use the exact name (minus the hashtag) in the @ViewChild(‘somename’) definition. The second littleTimmy in that line is the reference you use in your code. It can be something else, but it is good practice to keep them the same.

Next we are going to alter the child component.

Open child.component.ts and add the variable ‘whatIsTimmyDoing’ in the top of the class just above the constructor:

whatIsTimmyDoing: string;

Add the listen function to the child class:

  listen() {
    this.whatIsTimmyDoing = 'Timmy stopped what he was doing!';
  }

Go to child.component.html and add the ‘whatIsTimmyDoing’ variable after the staticdata:

  <br/>
  {{ whatIsTimmyDoing }}

Save your files, rebuild. Clicking the ‘Make Timmy stop’-button now calls the ‘listen’ function on the child component.

Here’s a schematic view of what you just built:

Schematic view

TLDR;

This concludes the first part of this introduction to communication between components. These are all very basic examples, used to get the concept across. I hope they help you as you start learning Angular.

Angular isn’t going anywhere: it will be with us for many years to come. It’s good to spend some time familiarizing yourself with it – but I’m not deluding myself into thinking that this is the silver bullet. Other frameworks have come and gone, and this one will too. But I find myself enjoying the ‘new’ Angular version tremendously.

Next time we’ll use a service to communicate between components.

Resources

  1. https://angular.io/guide/releases#release-schedule
  2. https://angular.io/tutorial 
  3. https://nodejs.org/en/download/
  4. https://code.visualstudio.com/

About Martijn

Craft Expert Martijn Kloosterman is part of the .NET team within Craft, the development programme for IT professionals (powered by Centric). If you would like to follow his blog, sign up for the monthly Craft update.

Want to know more about Craft, the development programme for IT professionals? Check out the website.

Tags:.NET

     
Reacties
  • ve
    anver sadat
    12 augustus 2018
    I have gone through many articles to understand the concept but you rocked it to make me understand it easily and smoothly.. thanks..
  • me
    William
    31 augustus 2018
    Loved it! I found this extremely helpful. You clear plain detailed thoughtful approach was perfect. Thanks!
  • None
    Haider
    21 februari 2019
    How can I display only the child HTML when I click on child bound button?
  • SAP
    Jens Steckhan
    08 mei 2019
    Simply love this tutorial, very concise and clear.
    And pressing the "Be obstinate" button made me chuckle. Thank you!
  • none
    Y
    02 juli 2019
    Well written and helpful.
    Thank you :)!
  • CREO
    Thomas
    05 juli 2019
    This is superbly written, thank you.
Schrijf een reactie
  • Captcha image
  • Verzenden