Ionic_Creating Reusable Layouts - smukov/AvI GitHub Wiki
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.
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:
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.
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>
We'll add the component to a page:
<!-- 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:
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.
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
<!-- 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
.
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.
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)
.
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 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. TheionViewLoaded
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.
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.