Categories
Angular JavaScript

Creating Web Components with Angular

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at how to create Angular Elements, which are packaged as Web Components.

Angular Elements

Web Components are supported by most browsers like Chrome, Firefox, Opera or Safari.

We can transform Angular components to Web Components to make all the Angular infrastructure available to the browser.

Features like data-binding and other Angular functionalities are mapped to their HTML equivalents.

Creating and Using Custom Elements

We can create a Web Component by creating an Angular component, then building it into a Web Component.

To create a Web Component with Angular, we have to do a few things.

First, we create a component to build into Web Components. Then we have to set the component we created as the entry point.

Then we can add it to the DOM.

We’ll make a custom component to get a joke. To do this, we first run:

ng g component customJoke  
ng g service joke

to create our component and service to get our joke and display it.

Then we run:

ng add @angular/element

to add the Angular Element files to create our Web Component.

Then injoke.service.ts , we add:

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

@Injectable({  
  providedIn: 'root'  
})  
export class JokeService { 
  constructor() { } 

  async getJokeById(id: number) {  
    const response = await fetch(`http://api.icndb.com/jokes/${id}`)  
    const joke = await response.json();  
    return joke;  
  }  
}

The code above gets a joke from the Chuck Norris API by ID.

Next, we write our component code as follows:

app.module.ts :

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule, Injector } from '@angular/core';  
import { AppComponent } from './app.component';  
import { CustomJokeComponent } from './custom-joke/custom-joke.component';  
import { JokeService } from './joke.service';

@NgModule({  
  declarations: [  
    CustomJokeComponent,  
    AppComponent  
  ],  
  imports: [  
    BrowserModule,  
  ],  
  providers: [JokeService],  
  bootstrap: [AppComponent],  
  entryComponents: [CustomJokeComponent]  
})  
export class AppModule {}

In AppModule , we add CustomJokeComponent to entryComponents so that it’ll be the entry point component instead of AppComponent .

It’ll load when custom-joke element is created.

app.component.ts :

import { Component, Injector } from '@angular/core';  
import { createCustomElement, WithProperties, NgElement } from '@angular/elements';  
import { CustomJokeComponent } from './custom-joke/custom-joke.component';

@Component({  
  selector: 'app-root',  
  template: ''  
})  
export class AppComponent {  
  constructor(injector: Injector) {  
    const JokeElement = createCustomElement(CustomJokeComponent, { injector });  
    customElements.define('custom-joke', JokeElement);  
    this.showAsElement(20);  
  } 

  showAsElement(id: number) {  
    const jokeEl: WithProperties<CustomJokeComponent> = document.createElement('custom-joke') as any;  
    jokeEl.id = id;  
    document.body.appendChild(jokeEl as any);  
  }  
}

The code in the constructor creates the custom component and attaches it to the DOM with our showAsElement method.

createCustomElement is from our @angular/element code.

The showAsElement method loads our custom-joke Web Component that we defined earlier.

custom-joke.component.ts :

import { Component, OnInit, ViewEncapsulation, Input } from '@angular/core';  
import { JokeService } from '../joke.service';

@Component({  
  selector: 'custom-joke',  
  template: `<p>{{joke?.value?.joke}}</p>`,  
  styles: [`p { font-size: 20px }`],  
  encapsulation: ViewEncapsulation.Native  
})  
export class CustomJokeComponent implements OnInit {  
  @Input() id: number = 1;  
  joke: any = {}; 

  constructor(private jokeService: JokeService) { } 

  async ngOnInit() {  
    this.joke = await this.jokeService.getJokeById(this.id)  
  }}

We put everything in one file so they can all be included in our custom-joke Web Component.

The @Input will be converted to an attribute that we can pass a number into and get the joke by its ID.

We leave custom-joke.component.html and app.component.html blank.

Conclusion

We use the @angular/element package to create a Web Component that we can use.

The difference is that we include the template and styles inline.

Also, we have to register the component and attach it to the DOM.

Categories
Angular JavaScript

Loading Angular Components Dynamically

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at how to load and display Angular components dynamically.

Dynamic Component Loader

We can load components by using the ComponentFactoryResolver to load multiple dynamic components.

Also, we have to make a directive to host the components.

First, we have to create the following components and directives by running the following commands:

ng g directive BannerHost  
ng g component Foo  
ng g component Bar

Then we write the following code:

app.module.ts :

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { FooComponent } from './foo/foo.component';  
import { BarComponent } from './bar/bar.component';  
import { BannerHostDirective } from './banner-host.directive';

@NgModule({  
  declarations: [  
    AppComponent,  
    FooComponent,  
    BarComponent,  
    BannerHostDirective  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent],  
  entryComponents: [  
    FooComponent,  
    BarComponent,  
  ]  
})  
export class AppModule { }

banner-host.directive.ts :

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({  
  selector: '[banner-host]'  
})  
export class BannerHostDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

bar.component.html :

bar

foo.component.ts :

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

@Component({  
  selector: 'app-foo',  
  templateUrl: './foo.component.html',  
  styleUrls: ['./foo.component.css']  
})  
export class FooComponent implements OnInit {  
  @Input() data: string; constructor() { } 

  ngOnInit() {  
  }
}

foo.component.html :

foo {{data}}

app.component.ts :

import { Component, ComponentFactoryResolver, ViewChild } from '@angular/core';  
import { FooComponent } from './foo/foo.component';  
import { BannerHostDirective } from './banner-host.directive';  
import { BarComponent } from './bar/bar.component';

@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  interval: any;  
  currentBannerIndex: number = -1;  
  banners = [FooComponent, BarComponent]  
  @ViewChild(BannerHostDirective, { static: true }) bannerHost: BannerHostDirective;  

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { } 

  ngOnInit() {  
    this.loadComponent();  
    this.getBanners();  
  } ngOnDestroy() {  
    clearInterval(this.interval);  
  }

  loadComponent() {  
    this.currentBannerIndex = (this.currentBannerIndex + 1) % this.banners.length;  
    const bannerItem = this.banners[this.currentBannerIndex];
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(bannerItem);  
    const viewContainerRef = this.bannerHost.viewContainerRef;  
    viewContainerRef.clear();  
    const componentRef = viewContainerRef.createComponent(componentFactory);  
    if (componentRef.instance.constructor === FooComponent) {  
      (<FooComponent>componentRef.instance).data = 'hello';  
    }  
  } 

  getBanners() {  
    this.interval = setInterval(() => {  
      this.loadComponent();  
    }, 3000);  
  }  
}

app.component.html :

<ng-template banner-host></ng-template>

In the code above, we created the banner-host directive to inject the component that we’re going to load.

We applied that to ng-template in app.component.html .

FooComponent takes a data input that we’re going to set.

Then the components are loaded in AppComponent .

The loadComponent method in AppComponent by going through the banners that we defined in the banners array.

We changed the currentBannerIndex every 3 seconds as indicated by the getBanners method.

The actual component loading is done by the following code:

const componentFactory = this.componentFactoryResolver.resolveComponentFactory(bannerItem);  
const viewContainerRef = this.bannerHost.viewContainerRef;  
viewContainerRef.clear();  
const componentRef = viewContainerRef.createComponent(componentFactory);  
if (componentRef.instance.constructor === FooComponent) {  
  (<FooComponent>componentRef.instance).data = 'hello';  
}

In the code above, we run the this.componentFactoryResolver.resolveComponentFactory method with the bannerItem as set by:

const bannerItem = this.banners[this.currentBannerIndex];

Then we clear the current view by writing:

const viewContainerRef = this.bannerHost.viewContainerRef;  
viewContainerRef.clear();

Next, we load the component by writing:

const componentRef = viewContainerRef.createComponent(componentFactory);

Finally, we check that the component is an instance of the FooComponent . If it is, then we load the string data property as indicated in the data field of FooComponent as follows:

if (componentRef.instance.constructor === FooComponent) {  
  (<FooComponent>componentRef.instance).data = 'hello';  
}

We used componentRef.instance.constructor to get which component instance is loaded.

Then if the component is an instance of FooComponent , we cast it to the FooComponent and set the data property of it to 'hello' as follows:

(<FooComponent>componentRef.instance).data = 'hello';

In the end, we should see foo hello and bar displayed one after the other in a 3-second cycle.

Conclusion

Displaying multiple Angular components dynamically involves several steps.

First, we have to create a directive to inject the components that we want to load.

Then we have to add an ng-template with the directive that we just created applied to it.

Next, we clear the existing view and use the ComponentFactoryResolver to clear the screen and then load the component that we want to load.

We can check the instance of the component that’s loaded and then do different things according to what component instance is loaded.

Categories
Angular JavaScript

An Overview of Angular Component Interaction

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at some examples of Angular component interaction.

Pass Data From Parent to Child with Input Binding

We can pass data from a parent component to a child component by using the @Input operator in the child component.

For example, we can write the following to do that:

app.module.ts :

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { PersonComponent } from './person/person.component';

@NgModule({  
  declarations: [  
    AppComponent,  
    PersonComponent  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

app.component.ts :

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

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

app.component.html :

<app-person [person]='person'></app-person>

person.component.ts :

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

@Component({  
  selector: 'app-person',  
  templateUrl: './person.component.html',  
  styleUrls: ['./person.component.css']  
})  
export class PersonComponent {  
  @Input() person;  
}

person.component.html :

<p>{{person.name}}</p>

In the code above, we have the AppComponent , which is the parent of PersonComponent .

In AppComponent , we have the person field, which is passed into the person property of app-person .

Then in the PersonComponent , we have:

@Input() person;

to get the person input value that’s passed in. Then we display the name property with:

<p>{{person.name}}</p>

in the template.

Then we should see ‘Jane’ on the screen.

Intercept Input Property Changes With a Setter

We can add a setter to the @Input decorator to intercept the value that’s set and change it to something else.

For example, we can write the following code:

app.component.ts :

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

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

app.component.html :

<app-person [person]='person'></app-person>  
<app-person [person]='undefined'></app-person>

person.component.ts :

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

@Component({  
  selector: 'app-person',  
  templateUrl: './person.component.html',  
  styleUrls: ['./person.component.css']  
})  
export class PersonComponent {  
  private _person = {};  
  @Input()  
  set person(person) {  
    this._person = person || { name: 'Foo' };  
  } 

  get person() { return this._person; }  
}

person.component.html :

<p>{{person.name}}</p>

In the code above, we added getters and setters for the person property in the PersonComponent .

In the setter, we set person depending if person is defined or not. If it’s not then we set this._person to a default object. Otherwise, we set this._person to whatever the value is passed in from the parent.

In the getter, we get the person value that was set by return this._person .

The first line should display ‘Jane’ since we passed in person into app-person .

The second line should display ‘Foo’ since we pass undefined as the value of person .

Intercept Input Property Changes with ngOnChanges()

We can use ngOnChanges to intercept the change and then set the value passed into the @Input field by changing it to what we want.

For example, we can write the following code:

app.component.ts :

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

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

app.component.html :

<app-person [person]='person'></app-person>  
<app-person [person]='undefined'></app-person>

person.component.ts :

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

@Component({  
  selector: 'app-person',  
  templateUrl: './person.component.html',  
  styleUrls: ['./person.component.css']  
})  
export class PersonComponent {  
  @Input() person; 

  ngOnChanges(change: { [propKey: string]: SimpleChange }) {  
    this.person = change.person.currentValue || { name: 'Foo' };  
  }  
}

person.component.html :

<p>{{person.name}}</p>

In the code above, we have the ngOnChanges hook in the PersonComponent .

It takes a change parameter that has the value of the change. In the hook, we check if change.person.currentValue is defined.

If it’s not, then we set this.person to { name: ‘Foo’ } . Otherwise, we set this.person to change.person.currentValue .

Then we should see the same thing as in the first example.

Parent Listens for Child Event

We can call emit on an EventEmitter in the child component and then listen to the emitted event in the parent.

For instance, we can do that as follows:

app.component.ts :

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

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

app.component.html :

<app-person (emittedPerson)='person = $event'></app-person>  
<p>{{person.name}}</p>

person.component.ts :

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

@Component({  
  selector: 'app-person',  
  templateUrl: './person.component.html',  
  styleUrls: ['./person.component.css']  
})  
export class PersonComponent {  
  @Output() emittedPerson = new EventEmitter(); emitPerson() {  
    this.emittedPerson.emit({ name: 'Jane' });  
  }  
}

person.component.html :

<button (click)='emitPerson()'>Get Person</button>

In the code above, we have the emitPerson method in the PersonComponent , which calls emit on the emittedPerson emitter, where we pass in the payload to send to the parent.

Then in app.component.html , we have:

<app-person (emittedPerson)='person = $event'></app-person>

to listen to the emittedPerson event from app-person and then set the person field to the $event variable passed in from the child by calling emit .

Then we have:

<p>{{person.name}}</p>

to display the name property value from the emitted object.

Therefore, when we click the Get Person button, we should see ‘Jane’ displayed.

Conclusion

We can pass data from a parent to a child by using the @Input decorator in the child.

To pass data from a child to a parent, we can emit an event from the child. Then the parent can listen to it and then run code to do something with it.

$event has the object that’s passed in with the emit call.

Categories
Angular JavaScript

A guide to styling Angular Components

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at ways to style Angular components.

Component Styles

Angular components are styled with CSS. Everything about CSS applies to Angular components.

We can also compound component styles with components, which allows for more modular designs than regular stylesheets.

Using Component Styles

We can define CSS styles in addition to template content in our components.

One way to do it is to use the styles property in our component.

For example, we can write that as follows:

app.component.ts :

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

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styles: [  
    `  
      h1 {  
        font-size: 50px;  
      }  
    `  
  ]  
})  
export class AppComponent {}

app.component.html :

<h1>Foo</h1>

Then we should see ‘Foo’ that’s 50 pixels big on the screen.

Styles in styles isn’t inherited by any components nested within the template or any content projected into the component.

We can use CSS class names and selectors that make the most sense in each component.

Class names and selectors don’t collide with class names and selectors in other parts of the app.

Changes elsewhere in the app don’t affect changes in the current component.

We can co-locate the CSS code of each component with TypeScript and HTML code of the component, which makes the project structure cleaner.

Also, we can change or remove CSS code without searching through the whole app to find where else the code is used.

Special Selectors

Angular apps can use special selectors to style components.

They come from selectors for styling the shadow DOM.

:host

The :host pseudoclass selector targets styles in the element that hosts the component rather than the elements inside the component’s template.

For example, we can write:

:host {  
  display: block;  
  border: 3px solid black;  
}

We can style host styles with the given selector by using the function form as follows:

:host(.active) {  
  border-width: 1px;  
}

:host-context

We can use :host-context to apply styles on some condition outside of a component’s view.

It works the same way as the :host selector, which can use the function form.

It’ll look in ancestor components all the way up to the document root for the given selector.

For example, we can write:

:host-context(.theme-light) h2 {  
  background-color: #fff;  
}

/deep/, >>>, and ::ng-deep (Deprecated)

We can use /deep/, >>>, and ::ng-deep to apply styles to outside of the component by disabling view encapsulation for the rule.

For example, we can write:

:host /deep/ h3 {  
  font-weight: bold;  
}

Style Files in Component Metadata

We can set the styleUrls to point to a stylesheet file to style a component.

For example, we can write the following:

app.component.ts :

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

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

app.component.css :

h1 {  
  font-size: 50px;  
}

app.component.html ;

<h1>Foo</h1>

In the code above, we put our styles in app.component.css and point our styleUrls to that file in app.component.ts .

We can specify more than one style file in both styles and styleUrls .

Template Inline Styles

We can put style tags in our templates to add template inline styles.

For example, we can write:

app.component.html :

<style>  
  h1 {  
    font-size: 50px;  
  }  
</style>  
<h1>Foo</h1>

The code above will set the h1’s font size to 50 pixels.

Template Link Tags

We can add link tags to templates to reference other styles.

For example, we can write the following code:

app.component.ts :

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

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html"  
})  
export class AppComponent {}

app.component.html :

<link rel="stylesheet" href="./app.component.css" />  
<h1>Foo</h1>

We see that we removed the styleUrls from AppComponent and put a link tag to reference the CSS file.

The styles should be applied from the file we referenced in the link tag.

CSS @imports

We can import CSS files with the standard CSS @import rule.

For example, we can write the following code:

foo.css :

h1 {  
  font-size: 50px;  
}

app.component.css :

@import url("./foo.css");

app.component.html :

<h1>Foo</h1>

External and Global Style Files

We can add external global styles to angular.json to include them in the build.

To register a CSS file, we can put in the styles section, which is set to styles.css by default.

Non-CSS Style Files

Angular CLI supports building style files in SASS, LESS or Stylus.

We can specify those files in styleUrls with the appropriate extensions (.scss, .less, or .styl) as in the following example:

app.component.scss :

h1  {  
  font-size: 70px;  
}

app.component.html :

<h1>Foo</h1>

View Encapsulation

We can control how view encapsulation works by setting the encapsulation setting in out component code.

The following are the possibilities for view encapsulation:

  • ShadowDom view encapsulation uses the browser’s native shadow DOM implementation. The component’s styles are included in the Shadow DOM
  • Native view encapsulation uses the now deprecated version of the browser’s native shadow DOM implementation
  • Emulated is the default option. It emulates the behavior of the shadow DOM by preprocessing and renaming CSS code to effectively scope the CSS to the component’s view
  • None means that Angular does no view encapsulation. The styles are added to global styles.

We can change the encapsulation setting as follows:

app.component.ts :

import { Component, ViewEncapsulation } from "@angular/core";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.scss"],  
  encapsulation: ViewEncapsulation.Native  
})  
export class AppComponent {}

Conclusion

We can use CSS to style Angular components. In addition, it supports SASS, LESS, and Stylus for styling.

By default, the scope of the styles is local to the component. However, we can change that to be different.

We can also include inline styles in our templates via the style and link tags.

Categories
Angular JavaScript

Complex Angular Component Interaction Examples

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at some complex examples of Angular component interaction.

Parent Interacts with Child Via Local Variable

We can create template reference variable for the child element and then reference that variable within the template to access a child component’s variable in the parent.

For example, we can write the following code:

app.module.ts :

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { PersonComponent } from './person/person.component';

@NgModule({  
  declarations: [  
    AppComponent,  
    PersonComponent  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

app.component.ts :

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

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

app.component.html :

<app-person #appPerson></app-person>  
<button (click)='appPerson.setPerson()'>Set Person</button>  
<p>{{appPerson.person.name}}</p>

person.component.ts :

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

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

  setPerson() {  
    this.person = { name: 'Jane' };  
  }  
}

We left person.component.html blank.

In the code above, we have the PersonComponent , which is the child of AppComponent .

In PeronComponent , we have the person variable and the setPerson method.

Then in app.component.html , we added a #appPerson template variable so that we can access its variable.

Then we used it to call PersonComponent ‘s setPerson method on the Set Person button.

We also displayed appPerson.person.name from PersonComponent in the p element.

When we click the Set Person button, the p tag automatically updates to show the value of appPerson.person.name set by setPerson .

Parent calls an @ViewChild()

In the previous example, we access a child component’s local variables. It’s simple and easy, but it’s limited because the parent-child wiring is all done in the parent.

The parent component has no access to the child.

To access the child component in the parent component’s code, we can use a ViewChild .

To do use ViewChild , we can write the following code:

app.module.ts :

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { PersonComponent } from './person/person.component';

@NgModule({  
  declarations: [  
    AppComponent,  
    PersonComponent  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

app.component.ts :

import { Component, ViewChild } from '@angular/core';  
import { PersonComponent } from './person/person.component';

@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  @ViewChild(PersonComponent, {static: false})  
  private personComponent: PersonComponent;

  setPerson(){  
    this.personComponent.setPerson();  
  }  
}

app.component.html :

<button (click)='setPerson()'>Set Person</button>  
<p>{{personComponent && personComponent.person.name}}</p>  
<app-person></app-person>

person.component.ts :

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

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

  setPerson() {  
    this.person = { name: 'Jane' };  
  }  
}

We left person.component.html blank again.

In the code above, we have the same person and setPerson members in PersonComponent .

Then in in AppComponent , we added the ViewChild as follows:

@ViewChild(PersonComponent, {static: false})  
private personComponent: PersonComponent;

to access the PersonComponent directly from within AppComponent . We need the app-person component in our PersonComponent template even though we don’t show anything in it so that AppComponent can access PersonComponent .

Then we just call the methods directly in AppComponent‘s setPerson method.

Also, we accessed the app-person component within the template with:

<p>{{personComponent && personComponent.person.name}}</p>

to show the person.name property from PersonComponent .

Parent and Children Communicate via a Service

Parent and child components can also communicate via a shared service.

We can create a service and then send the data to an Observable in the service from the child component which the parent component listens to so that it can get the latest data.

To do this, we can write the following:

app.module.ts :

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { PersonComponent } from './person/person.component';  
import { PersonService } from './person.service';

@NgModule({  
  declarations: [  
    AppComponent,  
    PersonComponent  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [PersonService],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

person.service.ts :

import { Injectable } from '@angular/core';  
import { Subject } from 'rxjs';

@Injectable({  
  providedIn: 'root'  
})  
export class PersonService {  
  private personSource = new Subject();  
  personSource$ = this.personSource.asObservable(); 

  announcePerson(person) {  
    this.personSource.next(person);  
  }}

app.component.ts :

import { Component } from '@angular/core';  
import { PersonService } from './person.service';

@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  person = {}; constructor(private personService: PersonService) {  
    personService.personSource$.subscribe(  
      person => {  
        this.person = person;  
      });  
  }  
}

app.component.html :

<app-person></app-person>  
<p>{{person.name}}</p>

person.component.ts :

import { Component } from '@angular/core';  
import { PersonService } from '../person.service';

@Component({  
  selector: 'app-person',  
  templateUrl: './person.component.html',  
  styleUrls: ['./person.component.css']  
})  
export class PersonComponent {  
  constructor(private personService: PersonService) { } 

  setPerson() {  
    this.personService.announcePerson({ name: 'Jane' });  
  }  
}

person.component.html :

<button (click)='setPerson()'>Set Person</button>

In the code above, we have the PersonService , whre we created a new Subject and converted it to an Observable.

In the annoucePerson method, we take in a person object and call next on personSource to broadcast the person object via the personSource$ Observable.

Then in AppComponent , we subscribe to the personSource$ Observable by writing:

personService.personSource$.subscribe(  
      person => {  
        this.person = person;  
      });

to get the latest value of person from the Observable.

In person.component.html , we added a button as follows:

<button (click)='setPerson()'>Set Person</button>

When we click it, we call the setPerson method, which calls the annoucePerson method from the PersonService we have above with the object that we wanted to pass in.

Since AppComponent subscribed to the personSource$ Observable, we should get the latest value of person.name .

Therefore, when we click Set Person, we should get ‘Jane’ displayed on the screen.

Conclusion

We can communicate between parent and child components in many ways.

We can add a template variable on the child component in the parent template and then access its public members on the parent template.

To access a child component’s members in the parent component, we can do it via the ViewChild which references the child component in the parent component.

Lastly, we can create a service that’s shared between the parent and child component, and then called the Subject ‘s next method to broadcast the message via the Observable.

Then the parent subscribes to the Observable and gets the latest value broadcasted from there.