Ionic_Creating Reusable Layouts - smukov/AvI GitHub Wiki

Creating Reusable Component

Similarly as we did with Android Native, we are going to create a reusable component in Ionic2 as well called ProfileHeader. We are going to use this component in My Profile and Contact screens.

Creating the Component

Our plan is to actually create an Angular2 directive, that we'll be able to use in our pages. This directive would output for us a circular image of the user, and the user's name.

We'll start by adding the components directory inside the app/pages directory. Why inside the app/pages? Simply because I'm not yet sure how to add new directory outside of pages, that will contain JavaScript, and that will be properly compiled by gulp.

Inside the components directory we'll create a new file profileHeader.js with the following content:

Source Code

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

@Component({
  selector: 'profile-header',
  template:
  `
  <div class="content-center-left">
    <img src="build/img/hugh.png" style="border-radius: 50%; width: 60px; height: 60px;">
    <span class="profile-header">{{fullName}}</span>
  </div>
  `
})
export class ProfileHeader {
  constructor() {
    this.fullName = 'Dr. House';
  }

  setFullName(fullName){
    this.fullName = fullName;
  }
}

Here we are defining a new Angular 2 directive. It's all pretty intuitive here, except maybe for the selector property in @Component decorator. This property is very important as it is used to identify this component's name. This basically means that when we set selector: 'profile-header', that we are going to use this component in our code like this <profile-header></profile-header>.

If the component was intended to be an attribute we would define it like selector: [profile-header]. And if it was to be used as a class, its definition would be selector: .profile-header.

The {{fullName}} tells Angular where to render the this.fullName property that we defined in component's constructor.

Making the Image Rounded

If you remember when we had to create an image with rounded border in Native Android, it was so complicated that we had to import a 3rd party library to our project.

With ionic, creating a rounded image is pretty straightforward - all you need to do is set style="border-radius:50%;" on the img element.

An alternative approach would be to use <ion-avatar> tag like below, that automatically sets the image to be round, however, it requires some additional styling to make it the size we want it to be:

<ion-avatar style="min-width:6rem; min-height: 6rem;">
   <img src="build/img/hugh.png" style="max-width:6rem; max-height: 6rem; border-radius: 4rem;">
</ion-avatar>

Adding the Component to a Page

We'll add the component to a page:

Source Code

<!-- code omitted for brevity -->

<ion-content padding class="profilePage">
  <profile-header></profile-header>
</ion-content>

However, this won't be enough to render properly the component within a page. We need to do a few more things in the page's controller:

Source Code

import {Component} from '@angular/core';
import {ProfileHeader} from '../components/profileHeader';

@Component({
  templateUrl: 'build/pages/profilePage/profilePage.html',
  directives: [ProfileHeader]
})
export class ProfilePage {
  constructor() {

  }
}

We had to import the ProfileHeader component explicitly, and add it to the directives array for this page, in order for the <profile-header></profile-header> code to be properly complied and rendered out.

Obtaining a reference to the Component

We often want to obtain a reference to the component in order to be able to customize it further, maybe perform some actions on it. We'll obtain a reference to our profile-header component in this section.

Firstly, we need to add a unique identifier to the component

Source Code

<!-- code omitted for brevity -->

<ion-content padding class="profilePage">
  <profile-header #header></profile-header>
</ion-content>

By adding the #header to the directive, header is now the id of this component within the current page. We need to update our page controller and obtain the reference using this id.

Source Code

import {Component, ViewChild} from '@angular/core';
import {ProfileHeader} from '../components/profileHeader';

@Component({
  templateUrl: 'build/pages/profilePage/profilePage.html',
  directives: [ProfileHeader],
  queries: {
    header: new ViewChild('header')
  }
})
export class ProfilePage {
  constructor() {

  }

  ionViewWillEnter(){
    this.header.setFullName("Dr. Gregory House");
  }
}

We are using the AngularJS 2 way to obtain a child of the component using the ViewChild. The header that we are providing to ViewChild is the id that we previously assigned to the component.

Alternative ViewChild Usages

We could as well call ViewChild as ViewChild(ProfileHeader) and not use the id at all.

Or, we can obtain an array of multiple children by calling the ViewChildren(ProfileHeader).

ViewChild undefined in constructor

One pitfall that I encounter while using the ViewChild is that the component that is being obtained is not available within the component's host constructor - i.e. it is undefined.

The best place to start working with a child component is inside the ionViewWillEnter event. This is an Ionic2 lifecycle event. We could also use it in ionViewLoaded event that fires before ionViewWillEnter, however, the loaded event doesn't fire every time - for example if the page is cached, then this even't won't get fired when it is opened again. This doesn't mean that ionViewLoaded event should never be used for this, but you just need to be careful with it, and know what you want to achieve.

Lifecycle events

Lifecycle events are fired during various stages of navigation. They can be defined in any component type which is pushed/popped from a NavController.

  • ionViewLoaded - Runs when the page has loaded. This event only happens once per page being created and added to the DOM. If a page leaves but is cached, then this event will not fire again on a subsequent viewing. The ionViewLoaded event is good place to put your setup code for the page.
  • ionViewWillEnter - Runs when the page is about to enter and become the active page.
  • ionViewDidEnter - Runs when the page has fully entered and is now the active page. This event will fire, whether it was the first load or a cached page.
  • ionViewWillLeave - Runs when the page is about to leave and no longer be the active page.
  • ionViewDidLeave - Runs when the page has finished leaving and is no longer the active page.
  • ionViewWillUnload - Runs when the page is about to be destroyed and have its elements removed.
  • ionViewDidUnload - Runs after the page has been destroyed and its elements have been removed.

Conclusion

To me, creating a reusable component in Native Android was a little more intuitive and a simpler process. I had some pitfalls with ionic, especially with trying to understand how the ViewChild works, as I'm still very new to both Ionic2 and Angular2. We'll see how the two approaches will compare over time, when I start creating and using new components for some real work (e.g. - inside ngFor).

Below you can see how the reusable component looks like inside My Profile page.

Reusable Component

References

Commits

⚠️ **GitHub.com Fallback** ⚠️