Wednesday, 20 November 2019

Using Popover Controller in Ionic

We will learn how to use Popover Controller in Ionic. Popover is more versatile compares to Alert Controller or Modal Controller. We can put different input fields and elements and have customised template with two ways data binding. Pretty much like a Component.

On this post we will learn how to create a popover with an input field, a select dropdown with a function and buttons. The popover takes data from main page and returns data to the main page.

This is how the popover looks like:


First, create the popover Component:
import { Component } from "@angular/core";
import { PopoverController, Events } from '@ionic/angular';

@Component({    
    templateUrl: './calculate-rent.component.html',
    styleUrls: ['../base/property.page.scss'],
})

export class CalculateRentComponent {
     // some variables to be bound to the template 
    shouldBeWeeklyRent: number;

    constructor(private popoverCtrl: PopoverController, private events: Events) {
        
    }
     
    popoverEvent() {
        this.events.publish('fromPopoverEvent', this.shouldBeWeeklyRent);
        this.popoverCtrl.dismiss();
    }

    closePopOver() {
        this.popoverCtrl.dismiss();
    }
}
We use Events to pass data back to the caller component. Note this.events.publish(‘[name]’, [data]) on the codes.

The template:
<ion-grid>
    <ion-row>
        <ion-col class="text-header" style="text-align: center">Calculate Weekly Rent</ion-col>
    </ion-row>
    <ion-row>
        <ion-col size="8">Desired rental yield:</ion-col>
        <ion-col size="3">
            <input type="text" class="textinput" style="border:1px solid #b2b2b2; background-color: #fff; padding: 0 3px 0 3px; text-align: right; width: 100%"
                   number-input [(ngModel)]="desiredYield" (ngModelChange)="calculateWeeklyRent()" />
        </ion-col>
        <ion-col size="1"><span style="padding-top: 6px; padding-left: 0px">%</span></ion-col>
    </ion-row>
    <ion-row>
        <ion-col size="8">From which type:</ion-col>
        <ion-col size="3">
            <select [(ngModel)]="desiredYieldType" (ngModelChange)="calculateWeeklyRent()">
                <option value="gross">Gross</option>
                <option value="net">Net</option>
            </select>
        </ion-col>
        <ion-col></ion-col>
    </ion-row>
    <ion-row>
        <ion-col size="8">New weekly rent:</ion-col>
        <ion-col><b>{{shouldBeWeeklyRent | currency}}</b></ion-col>
    </ion-row>
    <ion-row>
        <ion-col></ion-col>
    </ion-row>
    <ion-row>
        <ion-col size="4">
            <button type="submit" class="button custom-button" (click)="closePopOver()">
                Close
            </button>
        </ion-col>
        <ion-col size="8">
            <button type="submit" class="button custom-button" (click)="popoverEvent()">
                Put in Rental Field
            </button>
        </ion-col>
    </ion-row>
</ion-grid>

We then add variables for data binding and the function that will be called on the component.

Then on the main page:
async showCalculateWeeklyRent(ev) {
 const popover = await this.popoverCtrl.create({
  component: CalculateRentComponent, // the popover component that we created
  event: ev,
  componentProps: { // data to be passed
   totalCost: this.model.totalCost,
   totalExpense: this.model.totalExpense,
   totalIncomeWithoutRent: this.model.totalIncomeWithoutRent
  },
  cssClass: 'popoverClass',
 });

 // sync event from popover component
 this.events.subscribe('fromPopoverEvent', (shouldBeWeeklyRent) => {
  this.model.weeklyRent = shouldBeWeeklyRent;
  this.calculateYearlyRent();
 });

 return await popover.present();
}

Note the componentProps property can be used to pass data to popover component. It can contain objects that have more complex structure as well.
We use Events and its subscribe() method to receive data back.

On popover component, to receive data from the main page, we just need to declare local variables then they will be bound automatically with the data passed from the main page. We just need to make sure the variable names are similar.
// codes in main page
async showCalculateWeeklyRent(ev) {
 const popover = await this.popoverCtrl.create({
  . . .
  componentProps: { // data to be passed
   totalCost: this.model.totalCost,
   totalExpense: this.model.totalExpense,
   totalIncomeWithoutRent: this.model.totalIncomeWithoutRent
  },
  . . .
 });

 . . .
}

// codes in popover
export class CalculateRentComponent {
    . . .
    totalCost: number;
    totalExpense: number;
    totalIncomeWithoutRent: number;

    // note that even we don’t need to state the variables in the constructor
    constructor(private popoverCtrl: PopoverController, private events: Events) {
        . . .
    }
    . . .
}

Now the full codes in popover component:
import { Component } from "@angular/core";
import { PopoverController, Events } from '@ionic/angular';

@Component({    
    templateUrl: './calculate-rent.component.html',
    styleUrls: ['../base/property.page.scss'],
})
export class CalculateRentComponent {
    desiredYield: number;
    desiredYieldType: string = 'gross';
    shouldBeWeeklyRent: number;
    totalCost: number;
    totalExpense: number;
    totalIncomeWithoutRent: number;

    constructor(private popoverCtrl: PopoverController, private events: Events) {
    }
    
    calculateWeeklyRent() {
        . . .
        this.shouldBeWeeklyRent = …;
        . . .
    }

    popoverEvent() {
        this.events.publish('fromPopoverEvent', this.shouldBeWeeklyRent);
        this.popoverCtrl.dismiss();
    }

    closePopOver() {
        this.popoverCtrl.dismiss();
    }
}


Reference:
https://edupala.com/ionic-4-popover/