Mind is Software

Ying’s thoughts about software and business

Ionic Notes

This is a study note of Ionic framework based on the Udemy Ionic course and offical documents.

1 What is Ionic

Ionic is a mobile UI library built on top of Angular. An Ionic app is compiled to JS + HTML + CSS files that are further compiled by Apache Cordova to mobile apps for both iPhone and Android. Ionic has a rich component library and tools to build mobile apps.

The next version V4 will use a new standard web compliant web components compiled by the Stencil compiler. These components framework agnostic and can be used (optionally) by Angular, React and Vue. V4 also uses Capacitor, a cross-platform app runtime, that can be used to build native PWA apps that run nativelly on iOS, Android, Electron, and the Web. Capacitor is a successor to Cordova (and PhoneGap).

2 Ionic Project

2.1 Project File Structure

The hooks folder has scripts that will be executed by Cordova build process.

The platforms folder has the platform that the project targets.

The plugins folder has the Cordova plugins for native functions.

The resources folder has resources such as splash screen, images, etc for each platform.

The www folder has the web app that runs inside a browser.

The src folder has the app source code. The src/app has the Angular root module and root component. The src/assets has image resources. The src/pages has Ionic pages. The src/theme has the theme defintions. The src/index.html is the root html file working as a shell for all pages. The src/manifest.json and src/service-worker.js are used to build PWA apps.

The .scss files associated with each component have a global scope. You can use Angular style files that have a local scope.

2.2 Start Process

The body of /src/index.html has an <ion-app></ion-app> tag that is used by /src/app/main.ts to bootstrap the app.

The src/main.ts uses the AppModule defined src/app/app.module.ts.

The AppModule imports IonicModule.forRoot(MyApp) that uses MyApp component defined in src/app/app.component.ts. The IonicModule includes Angular modules such as forms and http. It configs Ionic to use the MyApp component as the root component. It also initializes Cordova. Because Ionic uese pages, not selector or router, for navigation therefore each page component should be declared in entryComponents.

The bootstrap has an entry of IonicApp that is imported from ionic-angular package.

The MyApp component constructor registers a callback for platform.ready() event. The platform could be iOS or Android.

The app.html only has one line <ion-nav [root]="rootPage"></ion-nav> that specifies the root attribute of ion-nav component to the rootPage. The rootPage is set to HomePage in /src/app/app.component.ts.

2.3 Pages

Ionic pages are full-screen Angular components. Ionice uses pages for navigation. Angular router is not used in Ionic V3. Ionic manages a stack of pages to navigate among pages. Only the top page is visible in the screen. Ionic V4 uses Angular router.

When an Ionice app starts, the IonicApp module uses the ion-nav component to initialize a stack of pages. The root attribute points to the root page.

You can define a lazy loading page by define a lazy module for a page. The component page should have an @IonicPage() decorator in addition to @Component({}). In its module file, delcare the component and add IonicPageModule.forChild(PageName) in its imports property. Then for all the places that use the page, use the string version page name as 'PageName'. Below is such an example:

import { NgModule } from "@angular/core";
import { IonicPageModule } from "ionic-angular";
import { UserPage } from "./user";

  declarations: [UserPage],
  imports: [IonicPageModule.forChild(UserPage)]
export class UserPageModule {}

2.4 Page Navigation Lifecycly

When navigate to a page, Ionic triggers the following page lifecycle events that you can hook your functions:

  • ionViewCanEnter: a navigation guard that return a true or false to allow or deny the navigation.
  • ionViewDidLoad: not fired when a page is cached. Used to setup a page when it is loaded. A modal page always fires this event. However, the template is already rendered therefore you need to check if template data is null if data is set in this method or the ionViewWillEnter event.
  • ionViewWillEnter: the page is about to become active.
  • ionViewDidEnter: the page is active. Cached page also fires this event.
  • ionViewCanLeave: a leave navigation guard.
  • ionViewDidLeave: page is not active.
  • ionViewWillUnload: page is about to be unloaded. Not fired if the page is cached in stack. For example, if the page pushes another page then the current page is cached.

The best practices are:

  • Use constructor only for dependency injection.
  • Use ngOnInit to set component properties from static data or navigation data (via NavParams). Template can use the properties set by ngOnInit, i.e., there is no need to use Elvis operator data?.prop to check nullity. It is called only once when a component is created.
  • Use ionViewDidLoad to set properties only once, similar to ngOniInit. A page only fires this event once when it is created. A page loaded from cache (for example, a page is loaded after a top page is popped) doesn’t fire this event. It is ok to use this to set properties of a modal page because modal page is at the top of a stack and is not cached.
  • Use ionViewWillEnter for setting data every time a page is entered. Unlike ngOninit and ionViewDidLoad, it fires even from cached pages.

For both ionViewDidLoad and ionViewWillEnter, component properties are not initialized when a page’s template is rendered. You should use Elvis operator to check the nullity of an object before access its members.

If you add a callback to ionViewCanEnter, the retured value will be the result of the push() method. Pay attention to use these events to initialize component peroperties: the component’s template is created before these events. To Use properties initialized in these event, you should check that the property is not undefined using the elvis operation (?). Usually the ngOnInit() method is a good place to intialize properties.

Ionic also has lifecycle hooks for ViewController. The hooks (observables) are willEnter, didEnter, willLeave, didLeave, willUnload. There are two functions: onWillDismiss and onDidDismiss are called when current ViewController will be / was dismissed.

3 Page Navigation

NavController is the base class for navigation components like Nav(ion-nav) and Tab (ion-tab). Each of the component has a single navigation stack. However, the Tabs and Menu Components can have multiple stacks.

3.1 Single Stack Navigation

A page can inject NavController and use its push, pop, or popToRoot method to make a page visible and invisible. To pass parameters to the target page, add an parameter object to the push method. The parameter object can be accessed using the get() method or data property of the injected NavParams object. The ion-nav is the declarative component of NavController.

Instead of calling push, you can use [navPush]="pageName" [navParams]="params" directive in a button to push a page and pass parameters. To pop a page, use navPop directive to pop a page.

By default, pages are cached if they are navigated away from but still in the navigation stack. They are destroyed when removed from the navigation stack via pop() or setRoot().

The push and pop methods take another parameter that configures the navigation. The configuration parameter has the following properties:

  • animate: a boolean flags whether or not animate the transition.
  • animation: a string gives the animation type.
  • direction: a string (forward, back) gives the navigation direction.
  • duration: a number gives the milliseconds the animation should take.
  • easing: a string gives the easing mode for the animation.

3.2 Multiple Stack Navigation

The Tabs(<ion-tabs>) sits at the bottom or top of the screen and usually have multiple Tab(<ion-tab>) that fill its content. Each <ion-tab> uses [root]="rootPage" [rootParams]="myParams" to specify a navigation stack with a specified root page and parameters. Use selectedIndex to specify the selected tab. Use tabsPlacement to set "top" or "bottom" position. Use tabsLayout to configure the tab icon and title.

The Menu(<ion-menu>) element usually is a sibling to the app’s root <ion-nav> element. The menu component’s content property should be set to a local reference variable of the content element. In a menu item, use the Nav’s setRoot() method to create a new navigation stack.

MenuToggle and MenuClose directives are used to open and close a menu. Usually use an ion-button menuToggle in a page’s ion-navbar to toggle menu. The button only appears when the page is a root page. The root page is either the app root page or a page loaded via the setRoot method.

You can also inject the MenuController provider and call its enable and swipeEnable methods with true or false parameter to open or close a menu.

3.3 Navigating from the Root or an Overlay

To use NavController from the app root component, add a reference variable like <ion-nav #myNav [root]="rootPage"> and use @ViewChild('myNav') nav: Nav or @ViewChild(Nav) nav: Nav to get an instance of the NavController.

In an overlay component (popover, modal, alert etc), inject an private appCtrl: App in its constructor, then use this.appCtrol.getRootNav() to get an instance of NavController.

4 Components

4.1 Alert, Modal, Action Sheet, and Toast

An alert is a type of “floating” modal that covers a portion of the screen. It is used for quick actions such as confirmation, notification or quick options. Usually it is injected into a component constructor. An alert has title, subtile, message, inputs and buttons. The inputs can be text box (text, tel, number), radio, and checkbox but they cannot be mixed. A button has text, handler and role (null or cancel). Use data in a handler to get the input value.

A modal is an overlay page over the current page. It is used for making a choice or editing an item of the page below it. When it presents, it is pushed to the app’s root nav. It can be closed by using the dismiss method of ViewController or using pop on the root nav controller. Data can be passed as the second argument to create method of ModalController and can be accessed using NavParams. A modal can emit data that is accessed using a callback in onDidDismiss.

An action sheet slides up from the bottom and covers a portion of the screen. It has a list of buttons that can have a role of default, destructive, and cancel. Dismissing an action sheet without clicking a button cancels it. You shouldn’t navigate from an action sheet. For example, you can use an alert, not a modal, from an action sheet.

A toast shows a notification for a specified period in the specified location (top, middle, bottom) and dismisses automatically.

A popover is similar to modal. It is created from a page that shows information or actions for the current page. It only covers a portion of the screen and its position can be set to event source’s position. It can be dismissed using dismiss method of ViewController and can return data that is accessed via a callback in onDidDismiss.

4.2 List and Button

A lists is used to display data in rows. It has default, no-lines, or inset styles. It can have ion-item-divider or ion-list-header. Rows can be ion-item or ion-button. A row can have multiple lines. A row can have ion-icon, ion-avatar, ion-thumbnail, or ion-label and ion-toggle. And they can be mixed in the same list.

A list can be ordered with reorder="true". Rows must have the same item type. Use ion-item-group if a list has ion-list-header. Use (ionItemReorder)="reorderItem($event) to implement reorder function.

To slide an itme and reveal a button, use ion-item-sliding and ion-item-options inside ion-list.

Buttons can be used by themselves or inside a navbar or a list. Buttons can be default or outline. The shape can be default, full, block, round. There are different sizes: small, default, or large. Buttons can have an icon icon-left, icon-righ, or icon-only. One or more buttons can be grouped inside a ion-buttons component.