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
JavaScript React

Using the React State Hook

React is a library for creating frontend views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we’ll look at how to use the state hook to keep dynamic states in a function component.

Using the State Hook

We can use the React.useState method to create a state with a corresponding function to change the state.

For example, we can write the following code to add a hook to our app:

import React from "react";  
import ReactDOM from "react-dom";
function App() {  
  const[count, setCount] = React.useState(0); 
  return (  
    <div>  
      <button onClick={() => setCount(count + 1)}>Count {count}</button>  
    </div>  
  );  
}

In the code above, we have the React.useState hook with the default 0 passed in. It returns an array with the state, which we called count, and a function to set the state, which is called setCount.

Then in the onClick handler of the button, we can the setCount function to update the count.

Equivalent Class Example

The example above would be the same as the following class:

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = {  
      count: 0  
    };  
  } 

  render() {  
    return (  
      <div>  
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>  
          Count: {this.state.count}  
        </button>  
      </div>  
    );  
  }  
}

In the code above, we have this.state initialized in the constructor with count 0. Then in the button we pass in a click event handler that calls setState to update the count state by incrementing it by 1.

Therefore, this.setState({ count: this.state.count + 1 }) is the same as setCount(count + 1) and count is the same as this.state.count.

Hooks and Function Components

A function component looks like:

const App = props => <p>foo</p>;

or:

function App(props) {  
  return <p>foo</p>;  
}

They were previously known as stateless components. However, now that React has hooks, they can keep their own state. Therefore, they should be called function components.

What’s a Hook?

A hook is a special function that lets us hook into React features. useState in the example above is a hook that lets us add React state to function components.

Declaring a State Variable

useState declares a state variable. In the example above, we have a variable called count but we can call it anything else.

useState has the same capabilities as this.state that we had in a class.

Variables disappear when the function exists but state variables are preserved by React.

The only argument for useState is the initial state. The state doesn’t have to be an object unlike classes.

We can have any type of data as the initial state.

If we want to store multiple values in state, then we’ll call useState for each kind of data.

useState always gives us the current state. It’s not only for creating the initial state.

Reading State

In the example, that we had:

import React from "react";  
import ReactDOM from "react-dom";
function App() {  
  const [count, setCount] = React.useState(0); 
  return (  
    <div>  
      <button onClick={() => setCount(count + 1)}>Count {count}</button>  
    </div>  
  );  
}

We get the current state from count returned by useState .

Updating State

Instead of calling this.setState like we do in class components, we call the setCount function directly.

Therefore, we don’t have to worry about the value of this .

What Do Square Brackets Mean?

The square brackets in:

const [count, setCount] = React.useState(0);

is the destructuring assignment operation, which means that values from an array are assigned to variables depending on the position.

The first element is assigned to the first variable, the second is assigned to the second variable, etc.

const [count, setCount] = React.useState(0);

is the same as:

const countState = React.useState(0);  
const count = countState[0];  
const setCount = countState[1];

Using Multiple State Variables

We will call useState as many times as we need to use multiple state variables.

For example, we can write:

function App() {  
  const [count, setCount = React.useState(0);  
  const [foo] = React.useState({ text: "bar" });  
  return (  
    <div>  
      <button onClick={() => setCount(count + 1)}>Count {count}</button>  
      <p>{foo.text}</p>  
    </div>  
  );

In the code above, we have 2 calls to useState to create 2 states, count and foo .

States can have object values, so we don’t have to create too many states.

Conclusion

We can use the useState hook to get and set dynamic states in a function component.

It returns the current state and a function to update the state in an array.

Then we can use them directly to display data and call the returned function to update the state.

Categories
JavaScript React

Manipulate DOM Elements in External React Components with ForwardRef

React is a library for creating front end views. It has a large ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

To manipulate DOM elements directly with React, we can create a ref, associate it to an element, and then reference that and call its methods to manipulate the DOM item directly.

In this article, we’ll look at how to pass the ref to components outside of its originating component by forwarding refs.

Forwarding refs to DOM components

We can forward refs by creating a ref and then call the forwardRef method to get the ref and then pass it into any place we want.

For example, if we have a Button component that returns a button element, and we want to get the button element from the outside component, we can write the following:

const Button = React.forwardRef((props, ref) => (  
  <button id="custom-button" ref={ref}>  
    {props.children}  
  </button>  
));

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = {};  
    this.buttonRef = React.createRef();  
  } componentDidMount() {  
    this.buttonRef.current.focus();  
  } render() {  
    return <Button ref={this.buttonRef}>Click Me</Button>;  
  }  
}

In the code above, we have a Button component created by using the React.forwardRef method with a callback passed in.

The callback has a props parameter which has the props that are passed in from the parent component, and the ref object which has the ref that’s passed in from the parent component.

Then we set the ref prop of the button element to the ref from the parameter.

Then we can create a new ref in the App component by calling React.createRef in App ‘s constructor. Then we pass that into the ref prop of Button so that we can access the ForwardRef from the button element.

This means the this.buttonRef is referencing the button element because we passed it into Button , which passes this.buttonRef into the button element via the callback we passed into React.forwardRef .

Then in the App component, we call:

this.buttonRef.current.focus();

in componentDidMount to focus the button in Button from App .

Forwarding refs in Higher-Order Components

We can also forward refs in high-order components. To do this, we write something like the following:

const Button = ({ forwardedRef, children }) => (  
  <button ref={forwardedRef}>{children}</button>  
);

function logProps(Component) {  
  class LogProps extends React.Component {  
    componentDidMount() {  
      console.log(this.props);  
    } 

    render() {  
      const { forwardedRef, ...rest } = this.props;  
      return <Component forwardedRef={forwardedRef} {...rest} />;  
    }  
  } 

  return React.forwardRef((props, ref) => {  
    return <LogProps {...props} forwardedRef={ref} />;  
  });  
}

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.buttonRef = React.createRef();  
  } 

  componentDidMount() {  
    this.buttonRef.current.focus();  
  } 

  render() {  
    const ButtonLogProp = logProps(Button);  
    return <ButtonLogProp ref={this.buttonRef}>Click Me</ButtonLogProp>;  
  }  
}

In the code above, we have the logProps higher-order component. The logProps component takes a Component .

logProps return a component with the forwarded refs passed in by calling React.forwardRef with a callback that returns LogProps .

In the App component we pass this.buttonRef as the value to ButtonLogProp .

ButtonLogProp is created by calling logProps with the Button component passed in. This means Component is Button in logProp .

Component, which is Button is then returned in the render method of the LogProp component.

At the end of the logProp higher-order component, we return the component with returned from React.forwardRef . The callback we pass into forwardRef returns the LogProps component with the props including the forwarded ref passed in.

All the props will then reach the Button component, which sets the ref of the button element to the this.buttonRef , which is first passed into logProp , then passed into LogProp , and React.forwardRef returns a component with the forwarded ref passed to the Button component, and then into the button element.

In other words, we passed the ref from App , to logProp , to LogProp , then forwarded with React.forwardRef to Button and then to button .

Conclusion

We can use refs from external components by forwarding refs. This lets us manipulate DOM elements from external components within a component.

Forwarding refs also works with higher-order components. All we have to do is to return the component we want to return in the higher-order component in the React.forwardRef callback with the forwarded ref passed in.

Categories
JavaScript Vue

Vue.js Components — Slots and Dynamic Components

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 distributing components with slots and dynamic components.

Distributing Components with Slots

We can pass content in components by putting text between the opening and closing tags.

To do this, we can add a slot to our component with the slot element.

For example, we can use it as follows:

src/index.js :

Vue.component("error-box", {  
  template: `  
    <div v-bind:style="{color: 'red'}">  
      <slot></slot>  
    </div>  
  `  
});

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

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">  
      <error-box>Error</error-box>  
    </div> <script src="src/index.js"></script>  
  </body>  
</html>

The text Error that we put between the error-box tags replace the slot tag, so we get the word ‘Error’ in red displayed.

Dynamic Components

We can switch dynamically between components by using v-bind:is .

The value of v-bind:is can contain either the name of a registered component or a component’s options object.

For example, we can use it as follows:

src/index.js :

Vue.component("tab-foo", {  
  template: "<div>Foo</div>"  
});

Vue.component("tab-bar", {  
  template: "<div>Bar</div>"  
});

Vue.component("tab-baz", {  
  template: "<div>Baz</div>"  
});

new Vue({  
  el: "#app",  
  data: {  
    currentTab: "foo"  
  },  
  computed: {  
    currentTabComponent() {  
      return `tab-${this.currentTab.toLowerCase()}`;  
    }  
  }  
});

In the code above, we created 3 components, and in the root Vue instance, we have the currentTab field so that we can add buttons to change the content in the HTML template.

The currentTabComponent will be updated as this.currentTab changes, which we’ll set the value of with buttons in the template.

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">  
      <button @click='currentTab = "foo"'>Foo</button>  
      <button @click='currentTab = "bar"'>Bar</button>  
      <button @click='currentTab = "baz"'>Baz</button>  
      <component v-bind:is="currentTabComponent"></component>  
    </div> <script src="src/index.js"></script>  
  </body>  
</html>

In the template above, we have 3 buttons with click event handlers to set the currentTab value, which will also set the currentTabComponent property as currentTab changes.

Since we passed currentTabComponent to v-bind:is , the component element will replace the component with the name set in currentTabComponent .

So when we click the Foo button we get Foo, when we click the Bar button we get Bar, and when we click the Baz button we get Baz.

We can pass in an object to v-bind:is as follows:

src/index.js :

new Vue({  
  el: "#app",  
  data: {  
    tabBar: {  
      template: "<div>Bar</div>"  
    }  
  }  
});

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">  
      <component v-bind:is="tabBar"></component>  
    </div> <script src="src/index.js"></script>  
  </body>  
</html>

We passed in the tabBar object as the value of v-bind:is directive.

DOM Template Parsing Issues

Some HTML elements have restrictions on what can be inside them. For example, ul must have li elements inside them.

We can nest whatever element we want with the is attribute.

For example, we can use it as follows:

src/index.js :

Vue.component("list-item", {  
  template: "<div>Foo</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">  
      <ul>  
        <li is="list-item"></li>  
      </ul>  
    </div> <script src="src/index.js"></script>  
  </body>  
</html>

The code above sets the list-item component as the component inside the li element.

This limitation doesn’t apply if we’re using string templates from one of the following sources:

  • String templates (e.g. template: '...')
  • Single-file (.vue) components
  • <script type="text/x-template">

Conclusion

We can get the content of items in between the opening and closing tags of our component with the slot element.

The component element let us change the component dynamically by passing in a string name for the component.

We can pass an object or a string with the component name to v-bind:is .

Categories
JavaScript Vue

Introduction to Vue.js Directives

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 how to manipulate the DOM with Vue.js directives.

Basics

Vue lets us register or own custom directives, in addition to using built-in ones like v-for and v-if .

It’s useful for low-level DOM manipulation that can’t easily be done with components.

For example, we can make one as follows:

src/index.js :

Vue.directive("focus", {  
  inserted(el) {  
    el.focus();  
  }  
});
const vm = 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">  
      <input v-focus />  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we defined the focus directive, which is referenced by adding a v- prefix to it.

The focus directive calls focus on the DOM element when it’s inserted into the DOM. The inserted method is a hook that handles the situation when the element that has the directive applied is inserted into the DOM.

The el parameter has the DOM element, which is why we can call the focus method on it.

Therefore, when we load the page, we’ll see that the input element is focused. This is the work of the focus directive.

The code above registered the directive globally. We can also register the directive locally as follows:

src/index.js :

const focus = {  
  inserted(el) {  
    el.focus();  
  }  
};

const vm = new Vue({  
  el: "#app",  
  directives: {  
    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 v-focus />  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The focus directive will then only be available to the component that it’s registered in.

Hook Functions

A directive can have the following hook functions. They’re all optional.

  • bind — called only once when the directive is first bound to the element. It’s used for one-time setup work
  • inserted — called when the bound element is inserted into its parent node. It guarantees the parent node presence.
  • updarted — calls after the containing component VNode has updated but possibly before children have updated. VNode is a node in the virtual DOM, it stands for virtual node.
  • componentUpdated — called when the containing component’s VNode and VNodes of its children are updated
  • unbind — called only once when the directive is unbound from the element

Directive Hook Arguments

The hooks above take the following arguments:

el

The element the directive is bound to. It can used to manipulate the DOM

binding

An object containing the following properties:

  • name — name of the directive without the v- prefix
  • value — value passed to the directive. For example, if we have v-foo='1' then the value is 1 .
  • oldValue — the previous value. This is only available in update and compoentUpdated . It’s available whether or not the value has changed.
  • expression — the expression of the binding as a string. For example, if we have v-foo='1 + 2' then it’s 1 + 2 .
  • arg —the argument passed into the directive. For example, if we have v-foo:bar then the arg is ‘bar‘.
  • modifiers — an object with modifiers. If we have v-foo.bar.baz then it would be { bae: true, baz: true } .

Everything other than el are read-only.

For example, we can display the data that are passed from the parameters as follows:

src/index.js :

const foo = {  
  inserted(el, binding, vnode) {  
    const stringify = JSON.stringify;  
    el.innerHTML = `  
      name:   
        ${stringify(binding.name)}  
      <br>  
      value:    
       ${stringify(binding.value)}  
      <br>  
      expression:  
        ${stringify(binding.expression)}  
      <br>  
      argument  
       ${stringify(binding.arg)}  
      <br>  
      modifiers:  
        ${stringify(binding.modifiers)}  
      <br>  
      vnode keys:  
       ${Object.keys(vnode).join(", ")}`;  
  }  
};

const vm = new Vue({  
  el: "#app",  
  directives: {  
    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">  
      <div v-foo:bar.baz="1 + 2"></div>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then we get:

name: "foo"  
value: 3  
expression: "1 + 2"  
argument "bar"  
modifiers: {"baz":true}  
vnode keys: tag, data, children, text, elm, ns, context, fnContext, fnOptions, fnScopeId, key, componentOptions, componentInstance, parent, raw, isStatic, isRootInsert, isComment, isCloned, isOnce, asyncFactory, asyncMeta, isAsyncPlaceholder

Conclusion

We can define our own Vue directives to manipulate DOM elements by binding them to specific DOM elements and then run the elements’ methods.

It works by running hooks when certain during certain stages of virtual node manipulation by Vue.

The hooks take the DOM element object as the first argument and a binding object which has the data about the directive like name, the value passed in, modifiers and arguments.