Categories
JavaScript Vue

How to create more complex slots in Vue

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at named and scoped slots.

Named Slots

Sometimes we want to have multiple slots. To distinguish them, then we have to name them.

We can define a component with named slots with the name attribute as follows:

Vue.component("layout", {  
  template: `  
  <div>  
    <header>      
      <slot name="header"></slot>  
    </header>  
    <main>        
      <slot></slot>  
    </main>  
    <footer>        
      <slot name="footer"></slot>  
    </footer>  
  </div>  
  `  
});

Then we can use the component above together as follows:

src/index.js :

Vue.component("layout", {  
  template: `  
  <div>  
    <header>      
      <slot name="header"></slot>  
    </header>  
    <main>        
      <slot></slot>  
    </main>  
    <footer>        
      <slot name="footer"></slot>  
    </footer>  
  </div>  
  `  
});

new Vue({  
  el: "#app"  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <layout>  
        <template v-slot:header>  
          <h1>Header</h1>  
        </template> <p>Main</p> <template v-slot:footer>  
          <p>Footer</p>  
        </template>  
      </layout>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The slot with the name header is filled with:

<template v-slot:header>  
   <h1>Header</h1>  
</template>

The slot with the name footer is filled with:

<template v-slot:footer>  
   <p>Footer</p>  
</template>

The slot with no name is filled with:

<p>Main</p>

If we want to wrap the content for the slot with no name with v-slot:default as follows:

<template v-slot:default>  
  <p>Main</p>  
</template>

Either way, the rendered HTML will be the same.

Scoped Slots

We can use scoped slots to access data from the child component.

To make data from the child component available in the parent, we can use the v-bind directive.

A simple example, to get child component data from the parent would be as follows:

src/index.js :

Vue.component("user", {  
  data() {  
    return {  
      user: {  
        firstName: "Joe",  
        lastName: "Smith"  
      }  
    };  
  },  
  template: `<p>  
    <slot v-bind:user="user"></slot>  
  </p>`  
});

new Vue({  
  el: "#app"  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <user>  
        <template v-slot:default="slotProps">  
          {{ slotProps.user.firstName }}  
        </template>  
      </user>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we have:

<p>  
   <slot v-bind:user="user"></slot>  
</p>

to make user available from within the root Vue instance.

Then in the root template, we have:

<user>  
  <template v-slot:default="slotProps">  
    {{ slotProps.user.firstName }}  
  </template>  
</user>

to access user ‘s data via slotProps slotProps have access to all the data that are made available via v-bind in the child component.

Abbreviated Syntax for Lone Default Slots

If there’s only the default slot with provided content, we can shorten v-slot:default or v-slot on the component as follows:

<user v-slot:default="slotProps">  
   {{ slotProps.user.firstName }}  
</user>

or:

<user v-slot="slotProps">  
   {{ slotProps.user.firstName }}  
</user>

If there’re other named slots, then the syntax above can’t be used because of potential ambiguity.

If we have multiple slots, then we have to write something like the following:

src/index.js :

Vue.component("user", {  
  data() {  
    return {  
      user: {  
        firstName: "Joe",  
        lastName: "Smith"  
      }  
    };  
  },  
  template: `<p>  
    <slot v-bind:user="user" name='first-name'></slot>  
    <slot v-bind:user="user" name='last-name'></slot>  
  </p>`  
});

new Vue({  
  el: "#app"  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <user>  
        <template v-slot:first-name="slotProps">  
          {{ slotProps.user.firstName }}  
        </template>  
        <template v-slot:last-name="slotProps">  
          {{ slotProps.user.lastName }}  
        </template>  
      </user>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we have to name the slots explicitly, and then we can access the child component’s data via v-slot:first-name=”slotProps” and v-slot:last-name=”slotProps” respectively.

Also, we wrapped the slot content in template .

Destructuring Slot Props

We can destructure the properties of the slot props with the destructuring assignment operator.

For example, we can use it as follows:

src/index.js :

Vue.component("user", {  
  data() {  
    return {  
      user: {  
        firstName: "Joe",  
        lastName: "Smith"  
      }  
    };  
  },  
  template: `<p>  
    <slot v-bind:user="user" name='first-name'></slot>  
    <slot v-bind:user="user" name='last-name'></slot>  
  </p>`  
});

new Vue({  
  el: "#app"  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <user>  
        <template v-slot:first-name="{ user }">  
          {{ user.firstName }}  
        </template>  
        <template v-slot:last-name="{ user }">  
          {{ user.lastName }}  
        </template>  
      </user>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, instead of writing slotProps , we changed it to { user } . { user } is the same as slotProps.user .

Conclusion

We can use named and scoped slots to create multiple slots and access data from the child component in the parent respectively.

Named slots prevent ambiguity and lets us use multiple slots.

Also, we can use v-bind in the child component and then use slotProps in the component to access child component’s data from the parent component.

Categories
JavaScript Vue

Vue.js Components — Parent, Child and Root

In this article, we’ll look at how to access various parts of a parent, child, or root component. Also, we look at dependency inject in Vue.js

Element & Component Access

Sometimes we have to access other components from a given component. However, it’s a bad idea to do this often since it gets messy fast.

Accessing the Root Instance

We can access the root instance of from a child component with the $root property.

For example, we can access the root Vue instance’s foo property as follows:

src/index.js :

Vue.component("foo", {  
  template: `<p>{{rootFoo}}</p>`,  
  computed: {  
    rootFoo() {  
      return this.$root.foo;  
    }  
  }  
});

new Vue({  
  el: "#app",  
  data: {  
    foo: "bar"  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <foo></foo>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The code above created a rootFoo computed property from the root Vue instance’s foo component and displayed it inside the foo component.

So we get foo displayed.

The data returned from $root can also be set and its methods can so be called.

So we can write:

this.$root.foo = 2

or:

this.$root.baz()

from any subcomponent in addition to accessing the properties’ values.

Accessing the Parent Component Instance

Similar to $root , we can access the parent component’s properties with the $parent property from a child component.

This shouldn’t be used frequently because it’s hard to trace when the parent’s data was set, so we should pass in props to the child instead of accessing the $parent property directly.

For example, we can use it as follows:

src/index.js :

Vue.component("child", {  
  template: `<p>{{parentFoo}}</p>`,  
  computed: {  
    parentFoo() {  
      return this.$parent.foo;  
    }  
  }  
});

Vue.component("parent", {  
  data() {  
    return {  
      foo: "bar"  
    };  
  },  
  template: `  
    <p><slot></slot></p>  
  `  
});

new Vue({  
  el: "#app"  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <parent>  
        <child></child>  
      </parent>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we accessed the parent component’s foo property from the child component by using this.$parent in the computed property parentFoo. So, we get bar displayed on the screen.

Accessing Child Component Instances & Child Elements

We can access a child component directly in JavaScript. To do this, we can add a ref attribute to the child component and assign a name as the value.

Then we can use this.$refs.name to access the component where name is the name that we set.

For example, we can use it as follows:

src/index.js :

new Vue({  
  el: "#app",  
  mounted() {  
    this.$refs.input.focus();  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <input ref="input" />  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the example above, we added a ref with name input in the template and then we accessed the input element with:

this.$refs.input

And then called focus on it in the mounted hook.

This lets us focus the input as soon as the input element has loaded.

We can also access a child component’s methods as follows:

src/index.js :

Vue.component("base-input", {  
  methods: {  
    focus() {  
      this.$refs.input.focus();  
    }  
  },  
  template: `<input ref='input' />`  
});

new Vue({  
  el: "#app",  
  mounted() {  
    this.$refs.baseInput.focus();  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <base-input ref="baseInput"></base-input>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, our root Vue instance called base-input ‘s focus method when it’s mounted.

Therefore, we see that the input is in focus when base-input loaded.

Dependency Injection

To prevent accessing the parent component and root Vue instance directly, we can use dependency injection to get the component that we want to get.

We can use it as follows:

src/index.js :

Vue.component("foo-button", {  
  methods: {},  
  inject: ["toggleFoo"],  
  template: `<button @click='toggleFoo'>Toggle Foo</button>`  
});

new Vue({  
  el: "#app",  
  data: {  
    foo: ""  
  },  
  provide() {  
    return {  
      toggleFoo: this.toggleFoo  
    };  
  },  
  methods: {  
    toggleFoo() {  
      this.foo = this.foo === "foo" ? "" : "foo";  
    }  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <foo-button></foo-button>  
      {{foo}}  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we added the provide method to return an object with the items that we want to make available to child components.

We made the toggleFoo method from the root Vue instance available to our foo-button component.

Then in the foo-button component, we injected the toggleFoo method from the parent to foo-button and then we call it when click events are triggered on the button.

We included foo-button in the template as usual.

In the end, we should see that we toggle the word foo on the screen when we click on the button.

Conclusion

We can access the root Vue instance with $root , and the parent component with $parent .

To access a child component from a parent, we can assign a ref with a name to the child component and then use this.$refs to access it.

These properties shouldn’t be used too often because they create a mess fast.

To keep accessing external components clean and traceable, we can use dependency injection by defining the provide method to expose certain parts of a component and use inject to get the provided parts.

Categories
Flow JavaScript

JavaScript Type Checking with Flow — Utility 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 the built-in some utility types that come with Flow.

$Keys<T>

The $Keys<T> type gets the properties from a type and uses it as its own type. It’s also useful for creating enums.

For example, we can write:

const fruits = {  
  apple: 'Apple',  
  orange: 'Orange',  
  banana: 'Banana'  
};

type Fruit = $Keys<typeof fruits>;  
let a: Fruit = 'apple';

The code above will define a new type Fruit with the union of the keys of the fruits object. This means that with a variable of type Fruit , we can assign 'apple' , 'orange' or 'banana' to it.

Assigning anything else should get us an error:

let b: Fruit = 'grape';

$Values<T>

The $Values<T> type let us get the types of an object type and create a type from a union of them.

For example, we can write:

type Person = {  
  name: string,  
  age: number,  
};

type PersonValues = $Values<Person>;  
let a: PersonValues = 1;  
let b: PersonValues = 'Joe';

In the code, we defined the Person type, with some properties, and then $Values<Person> takes the types of name and age and create a union from it. So the type of PeronValues is string | number .

$ReadOnly<T>

$ReadOnly<T> gets us a read-only version of type T .

The following:

type ReadOnlyObj = {  
  +foo: string  
};

and:

type ReadOnlyObj = $ReadOnly<{  
  foo: string  
}>;

are equivalent.

This is handy when we want to redefine all the properties of an object type as being read-only.

For example, we can write the following to define a read-only type and object:

type Person  = {  
  name: string,  
  age: number,  
};

type ReadOnlyPerson = $ReadOnly<Person>;

let person: ReadOnlyPerson = {  
  name: 'Joe',  
  age: 10  
}

Then when we try to reassign a property in the person object as follows:

person.name = 'Bob';

We’ll get an error.

$Exact<T>

$Exact<T> let us create an exact type from an object type.

This means that:

type ExactPerson = $Exact<{name: string}>;

is the same as:

type ExactPerson = {| name: string |};

$Diff<A, B>

$Diff<A, B> creates a type with the properties that’s in A but not in B , where both of them are object types. It’s the same as A \ B , which is the set difference between A and B .

For example, if we have 2 types:

type Person  = {  
  name: string,  
  age: number,  
};

type Age = { age: number };

Then we can create an object type which accepts the name property with:

type Name = $Diff<Person, Age>;

When we create an object with the Name type, we require the name property:

let name: Name = { name: 'Mary' };

If the name property isn’t included in the object:

let foo: Name = { age: 10 };

Then we’ll get an error.

$Rest<A, B>

$Rest<A, B> is similar to $Diff<A, B> but the property check is done at run-time. It’s the properties that are part of the rest operator.

It results in the properties of A ‘s own properties that aren’t own properties of B . In Flow, exact object types are treated as having own properties.

For example, we can define a new object type with $Rest<A, B> by running:

type Person = { name: string, age: number };  
const person: Person = {name: 'Jon', age: 42};  
const {age, ...rest} = person;  
(name: $Rest<Person, {|age: number|}>);

Then the name object can’t have a property age anymore since we casted it to the $Rest<Person, {|age: number|}> type. $Rest<Person, {|age: number|}> returns a new type with the age property removed from the Person type.

$PropertyType<T, k>

$PropertyType<T, k> gets the type of key k from type T . Key k is a string. For example, if we create the Person type as follows:

type Person = { name: string, age: number };

Then $PropertyType<Person, ‘name’> will get us the string type:

let foo: $PropertyType<Person, 'name'> = 'name';

Nested lookups also work. For instance, we can write:

type Person = {   
  name: {  
    firstName: string,  
    lastName: string  
  },   
  age: number   
};let foo: <$PropertyType<$PropertyType<Person, 'name'>, 'firstName'> = 'name';

$ElementType<T, K>

$ElementType<T, K> is a type that represents every element in an array, tuple, or object type T that matches the key name K .

For example, if we have the following type:

type Person = {   
  name: string,   
  age: number   
};

Then we can use it to get the type for the name property by writing:

$ElementType<Person, 'name'>

And then we can use it to annotate the type of other variables:

let foo: $ElementType<Person, 'name'> = 'name';

We can use it with tuples as follows:

type Tuple = \[boolean, string\];  
let foo: $ElementType<Tuple, 0> = true;

With dynamic object types, we can pass in the type of the key directly to the second argument. For instance, if we have the following type:

type DynamicObj = { \[key: string\]: number };

Then we can get the type of the property keys with $ElementType by writing:

let x: $ElementType<DynamicObj, string> = 1;

For arrays, we can write the following:

type StrArrayObj = {  
  strings: Array<string>,  
};

let x: $ElementType<$ElementType<StrArrayObj, 'strings'>, number> = 'abc';

The code above works as follows. $ElementType<StrArrayObj, ‘strings’> gets us the type for the strings property of StrArrayObj . Then we apply $ElementType with $ElementType<StrArrayObj, ‘strings’> , and number to get the type of the array elements of strings .

The number is for retrieving the index of the strings array.

Flow has many useful utility types which make creating new types easier. We can get the keys of an object to create a new union type. Also, we can get the types of values and create a new union type from them.

Also, there’s a type to change all the properties to read-only. We can also get the types of object keys, tuples or array entries.

Categories
JavaScript

JavaScript Events Handlers — onfullscreenerror and onpaste

In JavaScript, events are actions that happen in an app. They’re triggered by various things like inputs being entered, forms being submitted, and changes in an element like resizing, or errors that happen when an app is running, etc. We can assign event handler to handle these events. Events that happen to DOM elements can be handled by assigning an event handler to properties of the DOM object for the corresponding events. In this article, we will look at the the onfullscreenerror and the onpaste event handlers.

window.document.onfullscreenerror

The onfullscreenerror property lets us assign an event handler that handles the fullscreenerror event. If the attempt to call the requestFullScreen method fails, then the fullscreenerror is fired and the onfullscreenerror event handler is run. For example, if we have the following code:

document.documentElement.requestFullscreen();document.onfullscreenerror = event => {  
  console.log("onfullscreenerror");  
  console.log(event);  
};

Then the onfullscreenerror handler will be run since the fullscreenerror event is fired. It’s fired because requestFullScreen only works on elements where the user is interacting with it. It can’t happen automatically for security reasons, so it will fail. The error event looks something like the following:

bubbles: true  
cancelBubble: false  
cancelable: false  
composed: true  
currentTarget: null  
defaultPrevented: false  
eventPhase: 0  
isTrusted: true  
path: (3) [html, document, Window]  
returnValue: true  
srcElement: html  
target: html  
timeStamp: 144.4549998268485  
type: "fullscreenerror"

These are the properties that are part of the Event object. An Event object has the following value properties many of which are the ones that are listed above:

  • bubbles — is a read only boolean property that indicates whether the event bubbles up through the DOM tree or not.
  • cancelBubble — is an historical alias to the stopPropagation method. Setting its value to true before returning from an event handler prevents propagation of the event.
  • cancelable — is a read only boolean property indicating whether the event can be cancelled.
  • composed — is a read only boolean property that indicates whether or not the event can bubble across the boundary between the shadow DOM and the regular DOM.
  • currentTarget — is a read only property that references currently registered target for the event. This is the object to which the event is currently slated to be sent to, but it’s possible that this has been changed along the way through retargeting.
  • deepPath — is an array of DOM Nodes through which the event has bubbled.
  • defaultPrevented — is a read only boolean property that indicates whether or not event.preventDefault() has been called on the event.
  • eventPhase — is a read only property that indicates which phase of the event flow is being processed.
  • explicitOriginalTarget — is a read only property that has explicit original target of the event. This property is only available in Mozilla browsers.
  • originalTarget — is a read only property that has the original target of the event, before any retargetings. This property is only available in Mozilla browsers.
  • returnValue — is a historical property introduced by Internet Explorer and eventually adopted into the DOM specification in order to ensure existing sites continue to work. Ideally, you should try to use preventDefault() and defaultPrevented instead for cancelling events and checking if events are cancelled, but you can use returnValue if you choose to do so.
  • srcElement — is anon-standard alias from old versions of Microsoft Internet Explorer for target, which is starting to be supported in some other browsers for web compatibility purposes.
  • target — is a read only propetty that is a reference to the target to which the event was originally dispatched.
  • timeStamp — is a read only that has the time at which the event was created (in milliseconds). By specification, this value is time since January 1, 1970, but in reality browsers’ definitions vary.
  • type — is a read only property that has the name of the event (case-insensitive).
  • isTrusted — is a read only property that indicates whether or not the event was initiated by the browser after user interaction or by a script using an event creation method like initEvent.

The list above is a partial of properties. It only contains the value properties of the Event object.

window.document.onpaste

The onpaste property lets us assign an event handler to handle the paste event, which is triggered when we paste in data from the clipboard into our web page. For example, we can use it like in the following code:

document.onpaste = event => {  
  console.log(event);  
}

We can get the clipboard data that we pasted in our app with the clipboardData property, which is a DataTransfer object that has the getData method. A DataTransfer object has the following value properties:

  • dropEffect — returns a string of the type of drag-and-drop operation currently selected or sets the operation to a new type. The possible values are none, copy, link or move.
  • effectAllowed — returns a string of the type of operations that are possible. The possible value is one of none, copy, copyLink, copyMove, link, linkMove, move, all or uninitialized.
  • files — an FileList object of all the local files available on the data transfer. If the drag operation doesn’t involve dragging files, this property is an empty list.
  • items — is a read only gives a DataTransferItemList object which is a list of all of the drag data.
  • types — is a read only property that has an array of strings giving the formats that were set in the dragstart event

The DataTransfer object that has the following methods:

  • clearData()-remove the data associated with a given type. The type argument is optional. If the type is empty or not specified, the data associated with all types is removed. If data for the specified type does not exist or the data transfer object has no data then this method does nothing. It takes one optional argument, which is a string with the type of data to clear.
  • getData() — retrieves the data for the string of type that’s passed in as the argument, or an empty string if data for that type does not exist or the data transfer contains no data. It takes one argument which is the string with the type of the data to retrieve.
  • setData() — set the data for a given type. It takes 2 arguments. The first is a string for the format of the data that’s dragged and the second argument is the string that has the data for the drag object. If data for the type does not exist then it’s added at the end and it will be the new format. If data for the type already exists, the existing data is replaced in the same position.
  • setDragImage() — set the image to be used for dragging if a custom one is desired. It takes 3 arguments. The first is an image Element object that’s used for the drag feedback image. The second and third arguments are the horizontal and vertical offsets within the image.

To get the pasted text data we can use the getData method with the 'Text' argument like in the following code:

document.onpaste = event => {  
  console.log(event);  
  console.log(event.clipboardData.getData('Text'));  
}

Then we should get the text data that was pasted to our page.

The onfullscreenerror event handler let us handle any occasion where switching to full screen fails, like attempting to switch to full screen automatically without user interaction like trying to call requestFullScreen on the documentElement object. We can use the onpaste event handler to handle paste events triggered by pasting data from the clipboard to our page and get the data by using the event.clipboardData.getData(‘Text’) method call.

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.