Categories
Express JavaScript Nodejs

Excluding Routes from Calling Express Middleware with Express-Unless

To conditionally skip a middleware when a condition is met, we can use the express-unless package.

In this article, we’ll take a look at how to use it.

Installation

We can install it by running:

npm i express-unless --save

Usage

After installing it, we can assign the unless function exposed by the express-unless and assign it to the unless property of our middleware functions.

For example, we can write:

const express = require('express');  
const unless = require('express-unless');  
   
const static = express.static(__dirname + '/public');  
static.unless = unless;const app = express();  
app.use(static.unless({ method: 'OPTIONS' }));  
app.listen(3000, () => console.log('server started'));

In the code above, we set the unless property of static to the unless function of the express-unless package.

Then we write:

app.use(static.unless({ method: 'OPTIONS' }));

to exclude the static route from running on OPTIONS requests.

If we’re writing our own middleware, we can assign the unless property of our middleware function as follows:

const express = require('express');  
const unless = require('express-unless');  
   
const static = express.static(__dirname + '/public');
const logHostname = (req, res, next) =>{  
  console.log(req.hostname);  
  next();  
}  
logHostname.unless = unless;const app = express();  
app.use(logHostname.unless({ method: 'OPTIONS' }));  
app.listen(3000, () => console.log('server started'));

In the code above, we defined the logHostman middleware to log the hostname. Then we set the unless property of the function to the unless function exposed in the express-unless package.

Options

In the object that we pass into unless , we can pass in the following options:

  • method — this can be a string or an array of strings. If the request method matches then the middleware won’t run.
  • path — this can be a string, regex or an array of string or regex. It can also be an array of object which is URL and method key-value pairs. If the request path or path and method match, then the middleware won’t run.
  • ext — this is a string or array of strings. If the path ends with those extensions then a middleware won’t run.
  • custom — a function that accepts req and returns a boolean. If the function returns true for the given request, then the middleware won’t run.
  • useOriginalUrl — this should be a boolean. Defaults to true . If it’s false , the path will match against req.url instead of req.originalUrl .

Advanced Examples

We can exclude calling the express.static middleware when we have the jpg , html , css or .js extensions as follows:

const express = require('express');  
const unless = require('express-unless');  
const url = require('url');  
const static = express.static(__dirname + '/public');
static.unless = unless;
const app = express();  
app.use(static.unless((req) => {  
  const ext = url.parse(req.originalUrl).pathname.substr(-4);  
  return !['.jpg', '.html', '.css', '.js'].includes(ext);  
}));  
app.listen(3000, () => console.log('server started'));

In the code above, we check the extensions of the req.originalUrl property to see if they the request have the extensions listed in the array and skip the static middleware if they’re there.

We can also mix paths and request methods together as follows:

const express = require('express');  
const unless = require('express-unless');
const static = express.static(__dirname + '/public');  
const logHostname = (req, res, next) => {  
  console.log(req.hostname);  
  next();  
}  
logHostname.unless = unless;  
const app = express();  
app.use(logHostname.unless({  
  path: [  
    '/index.html',  
    { url: '/', methods: ['OPTIONS', 'PUT'] }  
  ]  
}));  
app.listen(3000, () => console.log('server started'));

Then we stop the logHostname middleware from running when we go to index.html or / with the PUT or OPTION request methods.

Conclusion

We can stop middleware from running on certain routes or request methods by using the express-unless package.

To use it, we just set the unless function exposed from the package and set it to the unless property of our middleware function.

Then we can exclude routes by using various conditions by checking the URL and/or request method.

Categories
JavaScript Vue

Introduction to Vue.js Testing

With apps getting more complex than ever, it’s important to test them automatically. We can do this with unit tests, and then we don’t have to test everything by hand.

In this article, we’ll look at how to test Vue.js apps by writing a simple app and testing it.

Getting Started

To get started, we create an app that gets a joke from the Chuck Norris Jokes API.

We start by creating an empty folder, going into it, and running the Vue CLI by running:

npx vue create .

In the wizard, we select Unit Tests, then choose Jest and then proceed.

Now that we have the files generated, we can change some code. We can delete the components folder and replace the code in App.vue with:

<template>  
  <div id="app">  
    <button @click='toggleJoke()'>{{jokeHidden ? 'Show' : 'Hide'}} Joke</button>  
    <p v-if="!jokeHidden">{{data.value.joke}}</p>  
  </div>  
</template><script>  
export default {  
  name: "app",  
  data() {  
    return {  
      jokeHidden: false,  
      data: { value: {} }  
    };  
  },  
  beforeMount() {  
    this.getJoke();  
  },  
  methods: {  
    async getJoke() {  
      const res = await fetch("http://api.icndb.com/jokes/random");  
      this.data = await res.json();  
    }, toggleJoke() {  
      this.jokeHidden = !this.jokeHidden;  
    }  
  }  
};  
</script><style>  
#app {  
  font-family: "Avenir", Helvetica, Arial, sans-serif;  
  -webkit-font-smoothing: antialiased;  
  -moz-osx-font-smoothing: grayscale;  
  text-align: center;  
  color: #2c3e50;  
  margin-top: 60px;  
}  
</style>

The code just gets a joke from the API and then display it. Also, it has a button to show and hide the joke.

Our app looks something like the following:

https://thewebdev.info/wp-content/uploads/2020/04/test.png

Creating the Tests

Now that we have something to test, we can actually write the tests.

In the tests/unit folder, we delete what we have then create app.spec.js in that folder.

Then we open the file we created and add:

import { mount } from @vue/test-utils';  
import App from '@/App.vue'
const mockResponse = {  
  "type": "success",  
  "value": {  
    "id": 178,  
    "joke": "In an act of great philanthropy, Chuck made a generous donation to the American Cancer Society. He donated 6,000 dead bodies for scientific research.",  
    "categories": []  
  }  
}

To import the component that we’ll test, the mount function to let the Vue Test Utils build and render the component for testing, and the mockResponse object that we’ll use to set the mock data.

Then we add the skeleton for our test by writing:

describe('App.vue', () => {  
  beforeEach(() => {  
    jest.clearAllMocks()  
  })  
})

We have the string description for our test suite and a callback which we add out tests to.

Inside the callback, we have the beforeEach hook to clear all the mocks by running jest.clearAllMocks() .

We need this because we’ll mock some of the functions in our component later.

Adding our First Test

Next, we write our first test. This test will simulate getting the data from the API and then displaying the joke on the screen.

It won’t actually get the joke from the server since we want our test to run anywhere and at any time. Getting it from the server won’t let us do that.

The API returns something different every time we call it and also it might not always be available.

With that in mind, we write:

it('renders joke', async () => {  
    const wrapper = mount(App, {  
      methods: {  
        getJoke: jest.fn()  
      }  
    });  
    wrapper.vm.data = mockResponse;  
      expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke)  
  })

in the callback we passed into the describe function after the beforeEach call.

The test above calls mount on our App component to build and render the component and returns a Wrapper object to let us access it.

In the second argument, we pass in the options with the methods property so that we can mock the getJoke method with Jest with jest.fn(). We want to mock it so that our test doesn’t call the API.

Once we have the wrapper then we run:

wrapper.vm.data = mockResponse;

to set the mockResponse data to the data property of our component instance.

Once we did that, we check that we get the joke in our mockResponse rendered by writing:

expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke)

since we put our joke in the p tag in our App component.

The expect method and toMatch are from Jest.

Writing Test that Interacts with UI Elements

Writing a test that does something to UI elements like buttons isn’t that much more work.

To test the button that we added to our app actually shows and hides the joke, we write:

it('toggles joke', () => {  
    const wrapper = mount(App, {  
      methods: {  
        getJoke: jest.fn()  
      }  
    });  
    wrapper.vm.data = mockResponse;  
    expect(wrapper.find('button').text()).toMatch('Hide Joke');  
      expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke); wrapper.find('button').trigger('click');  
    expect(wrapper.find('button').text()).toMatch('Show Joke');  
    expect(wrapper.find('p').exists()).toBe(false); wrapper.find('button').trigger('click');  
    expect(wrapper.find('button').text()).toMatch('Hide Joke');  
      expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke);  
  })

The first part:

const wrapper = mount(App, {  
  methods: {  
    getJoke: jest.fn()  
  }  
});  
wrapper.vm.data = mockResponse;

is the same as before. We mock the getJoke function with jest.fn() so that our test won’t call the API. Then set the mock data.

Next, we check the button text by writing:

expect(wrapper.find('button').text()).toMatch('Hide Joke');

and that our mocked joke is shown in the p element:

expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke);

Then we click our button by running:

wrapper.find('button').trigger('click');

And then check for the text of the button and whether the p element is removed by our v-if directive:

expect(wrapper.find('button').text()).toMatch('Show Joke');  
expect(wrapper.find('p').exists()).toBe(false);

Finally, we can do the click again and check if the joke is shown again as follows:

wrapper.find('button').trigger('click');  
expect(wrapper.find('button').text()).toMatch('Hide Joke');  
expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke);

Running the Tests

Together, we have the following test code in app.test.js :

import { mount } from '@vue/test-utils';  
import App from '@/App.vue'
const mockResponse = {  
  "type": "success",  
  "value": {  
    "id": 178,  
    "joke": "In an act of great philanthropy, Chuck made a generous donation to the American Cancer Society. He donated 6,000 dead bodies for scientific research.",  
    "categories": []  
  }  
}

describe('App.vue', () => {  
  beforeEach(() => {  
    jest.clearAllMocks()  
  }) 

  it('renders joke', async () => {  
    const wrapper = mount(App, {  
      methods: {  
        getJoke: jest.fn()  
      }  
    });  
    wrapper.vm.data = mockResponse;  
      expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke)  
  }) 

  it('toggles joke', () => {  
    const wrapper = mount(App, {  
      methods: {  
        getJoke: jest.fn()  
      }  
    });  
    wrapper.vm.data = mockResponse;  
    expect(wrapper.find('button').text()).toMatch('Hide Joke');  
      expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke); wrapper.find('button').trigger('click');  
    expect(wrapper.find('button').text()).toMatch('Show Joke');  
    expect(wrapper.find('p').exists()).toBe(false); wrapper.find('button').trigger('click');  
    expect(wrapper.find('button').text()).toMatch('Hide Joke');  
    expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke);  
  })  
})

Then we run the tests by npm run test:unit .

We should get:

PASS  tests/unit/app.spec.js  
  App.vue  
    √ renders joke (19ms)  
    √ toggles joke (11ms)Test Suites: 1 passed, 1 total  
Tests:       2 passed, 2 total  
Snapshots:   0 total  
Time:        2.102s  
Ran all test suites.

every time that we run our tests since we mocked the data.

Conclusion

Vue CLI creates a project that has unit testing built-in if we choose to include it. This saves us lots of work.

Jest is an easy test runner with lots of features like mocking and expect matchers that we can use.

To test UI components, we use the wrapper object returned by mount , which has the rendered component. Then we can use find to search the DOM for what we want to look for.

If the element exists, we can also trigger events on it by calling the trigger method with the event that we want to fire.

Finally, we have the exists method to check if the element we look for actually exists.

Categories
Express JavaScript

Guide to the Express Response Object

The Express response object lets us send a response to the client.

Various kinds of responses like strings, JSON, and files can be sent. Also, we can send various headers and status code to the client in addition to the body.

In this article, we’ll look at how to set headers and send status codes with other items, including the set method for setting headers, status for setting response status code, type for setting the Content-Type header value, and the vary method for setting the Vary header value.

Methods

res.set(field [, value])

We can use the set method to set response headers before sending a response.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res, next) => {  
  res.set({  
    'Content-Type': 'text/plain',  
    'Content-Length': '100'  
  })  
    .send();  
})  
app.listen(3000);

Then when we make a request to / , we get that the Content-Length header is set to 100 and the Content-Type is set to text/plain .

Note that we have to call send to send the response.

We can verify this with an HTTP client like Postman.

res.status(code)

We can call the status method to add a status code before sending the response.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res, next) => {  
  res.status(400).send('Bad Request');  
})
app.listen(3000);

Then we get Bad Request displayed and the 400 response code.

res.type(type)

The type method sets the Content-Type HTTP header to the MIME type determined by the mime.lookup() method from the node-mime package for the specified type.

If type contains the / character, then it sets the Content-Type to type .

This method doesn’t send the response.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {  
  res.type('.html').send();  
})  
app.listen(3000);

Then we get that the Content-Type response header has the value text/html; charset=utf-8 .

If we have:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {  
  res.type('.png').send();  
})  
app.listen(3000);

Then we get image/png instead of text/html for the value of Content-Type .

res.vary(field)

The vary method adds the field to the Vary response header if it’s not already there.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {  
  res.vary('User-Agent').send();  
})  
app.listen(3000);

Then we have the Vary response header set to User-Agent .

The Vary header lets us to content negotiation, which is a mechanism for serving different kinds of data with the same URL.

The user agent can specify what’s best for the server.

The Vary header indicates which headers it’s used for content negotiation.

Since we have the Vary header set to User-Agent , our app uses the User-Agent header to serve different kinds of content according to the user agent of the browser.

Conclusion

The set method lets us set headers before sending our response. It takes an object with the response headers that we want to send.

status method is for setting a response status code before sending it. We can chain it with the response body.

The type method is for setting the Content-Type header value. The returned MIME-type is determined by the mime.lookup() method from node-mime .

The vary method for setting the Vary header value. The Vary header is used for content negotiation, which is serving different content according to the field specified by the Vary header’s value with the same URL.

Categories
Flow JavaScript

JavaScript Type Checking with Flow — Union Types

Flow is a type checker made by Facebook for checking JavaScript data types. It has many built-in data types we can use to annotate the types of variables and function parameters.

In this article, we’ll look at how to use union types to create variables that accept several different types.

Union Type Syntax

The general syntax for defining a union type is as follows:

Type1 | Type2 | ... | TypeN

We separate each type with the vertical bar | .

Also, we can break them into multiple lines by using leading | :

type Bar =  
  | Type1  
  | Type2  
  | ...  
  | TypeN

We can create union types out of other union types:

type Numbers = 1 | 2;  
type Fruit = 'apple' | 'orange';type Foo = Numbers | Fruit;

Functions

If we use union types in functions, we have to handle every possible type of the union type of the parameter. For example, we have to write something like:

function foo(value: number | boolean | string):  number {   
  if (typeof value === 'number') {  
    return 1;  
  } else if (typeof value === 'boolean') {  
    return 2;  
  }  
  return Number(value);  
}

If we skip the handling of any type in the union type of the parameter like the following code:

function foo(value: number | boolean | string):  number {   
  if (typeof value === 'number') {  
    return 1;  
  } else if (typeof value === 'boolean') {  
    return 2;  
  }    
}

then we’ll get an error.

Unions & Refinements

With union type parameters, it’s useful to handle each type individually. We can do this with the typeof operator to check for the type and handle each type as follows:

function foo(value: number | boolean | string): number {   
  if (typeof value === 'number') {  
    return 1;  
  }   
  else if (typeof value === 'boolean') {  
    return 2;  
  }   
  else if (typeof value === 'string') {  
    return Number(value);  
  }  
  return Number(value);  
}

Disjoint Unions

We can create a union type out of different object types. For example, if we have the following objects:

{ foo: true, bar: 'bar' }  
{ foo: false, bar: 'baz' }

We can create a union type out of these 2 objects by creating a type alias for each and then union them. So we can write:

type Foo1 = { foo: true, bar: 'bar' };  
type Foo2 = { foo: false, bar: 'baz' };
type Foo = Foo1 | Foo2;

Then we can create variables of type Foo as follows:

let foo1: Foo = { foo: true, bar: 'bar' };  
let foo2: Foo = { foo: false, bar: 'baz' };

The union of 2 object types with fixed values will allow our variables to take on either of the 2 types in the union.

Union of object types with properties that aren’t exact will create a union of the 2 types for the properties.

For example, if we have:

type Foo1 = { foo: true, bar: string };  
type Foo2 = { foo: true, bar: number };  
type Foo = Foo1 | Foo2;

Then Foo will allow bar to be either a string or a number, while foo will always be true . So we can create variables like the following:

let foo1: Foo = { foo: true, bar: 'bar' };  
let foo2: Foo = { foo: true, bar: 2 };

If we have 2 types with different properties, then the union of 2 types let us include all the properties of the object if we have a variable with the union of the 2 types.

For example, if we have:

type Foo1 = { foo: true, bar: string };  
type Foo2 = { baz: false, a: number };
type Foo = Foo1 | Foo2;

Then we can define a variable with all the properties as follows:

let foo: Foo = { foo: true, bar: 'bar', baz: false, a: 1 };

With union types, we can also pass in extra properties that aren’t included in the types that form the union. So given:

type Foo1 = { foo: true, bar: string };  
type Foo2 = { baz: false, a: number };
type Foo = Foo1 | Foo2;

So we can write:

let foo: Foo = { foo: true, bar: 'bar', baz: false, a: 1, b: 2 };

Disjoint Union with Exact Types

We can restrict the addition of new properties by creating a union out of exact types as follows:

type Foo1 = {| foo: true, bar: string |};  
type Foo2 = {| baz: false, a: number |};
type Foo = Foo1 | Foo2;

Then writing:

let foo: Foo = { foo: true, bar: 'bar', baz: false, a: 1, b: 2 };

will give us an error.

With unions of exact types, we can either have properties from one or the other. So given these types:

type Foo1 = {| foo: true, bar: string |};  
type Foo2 = {| baz: false, a: number |};
type Foo = Foo1 | Foo2;

We can either have:

let foo: Foo = { foo: true, bar: 'bar' };

or:

let foo: Foo = {  baz: false, a: 1 };

With union types, we can create variables that can take on the value of several types. The types that form a union can primitive or object types.

We can also form unions of exact types to prevent adding additional properties that aren’t listed in the types or including properties from all the types.

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.