Header Ads Widget

Ticker

6/recent/ticker-posts

Ionic 5+: Uso de Cordova SQLite y Barcode Scanner para crear un Product Inventory Manager [PARTE 4]

 En la parte anterior, cubrimos cómo agregar paginación a nuestro proveedor de servicios de datos.

En esta parte vamos a empezar a construir nuestra primera página o la página de inicio que contiene botones para navegar a diferentes páginas de la aplicación.

Pero primero hagamos un poco de personalización.

En la primera parte hemos generado diferentes páginas (productos, familias, ubicaciones y transacciones) usando la CLI de Ionic. Si todavía estás usando la versión Ionic 5 entonces no tienes nada, pero si estás usando Ionic 3 necesitas modificar el páginas generadas para que no utilicen la carga diferida porque causará algunos problemas con los componentes personalizados que crearemos más adelante en este tutorial.

¿Cómo deshabilitar la carga diferida para las páginas?

En Ionic 3, las páginas que usan la carga diferida tienen sus propios módulos en la misma carpeta donde existen. También están decoradas con el decorador @IonicPage () para deshabilitar la carga diferida.

Elimina el módulo propio de la página que tiene un nombre como: xxxx.module.ts

Elimina el decorador IonicPage ().

Ahora debe agregar estas páginas al módulo principal de la aplicación:

Vaya a src / app / app.module.ts. Importe las páginas:

import { FamilyListPage } from '../pages/family-list/family-list';
import {FamilyDetailsPage} from '../pages/family-details/family-details';

import { LocationDetailsPage } from '../pages/location-details/location-details';
import { LocationListPage } from '../pages/location-list/location-list';

import { ProductDetailsPage } from '../pages/product-details/product-details';
import { ProductListPage } from '../pages/product-list/product-list';

import { TransactionListPage } from '../pages/transaction-list/transaction-list';
import { TransactionDetailsPage } from '../pages/transaction-details/transaction-details';

Luego agréguelos a las importaciones y matrices entryComponents:

@NgModule({
declarations: [
    MyApp,
    FamilyListPage ,
    FamilyDetailsPage,
    LocationDetailsPage,
    LocationListPage,
    ProductDetailsPage,
    ProductListPage,
    TransactionListPage,
    TransactionDetailsPage
],
imports: [
    BrowserModule,
    ReactiveFormsModule,
    IonicModule.forRoot(MyApp),
],
bootstrap: [IonicApp],
entryComponents: [
    MyApp,
    FamilyListPage,
    FamilyDetailsPage,
    LocationDetailsPage,
    LocationListPage,
    ProductDetailsPage,
    ProductListPage,
    TransactionListPage,
    TransactionDetailsPage
],
providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    {provide: SQLite ,useClass:SQLiteMock},
    DataServiceProvider

]
})
export class AppModule {}

Puedes hacer lo mismo con la página de inicio o no. Dado que no tiene interacción con los componentes personalizados que vamos a construir más adelante en este tutorial, ¡no produce ningún problema!

Ahora estamos listos para seguir construyendo nuestra aplicación móvil.

Abra src / pages / home / home.ts y agregue:

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

import { IonicPage , ModalController } from 'ionic-angular';

import {FamilyListPage} from '../family-list/family-list';

import {LocationListPage} from '../location-list/location-list';

import {ProductListPage} from '../product-list/product-list';

import { TransactionListPage } from '../transaction-list/transaction-list';

@IonicPage()
@Component({
selector: 'page-home',
templateUrl: 'home.html',
})
export class HomePage {
familyListPage = FamilyListPage; 
locationListPage = LocationListPage;
productListPage = ProductListPage;
transactionListPage = TransactionListPage;

constructor(public modalCtrl : ModalController ) {
}

ionViewDidLoad() {
    console.log('ionViewDidLoad HomePage');
}

openModal(page){
    switch(page){
    case 'FamilyListPage':
        this.modalCtrl.create(this.familyListPage).present();
        break;
    case 'LocationListPage':
        this.modalCtrl.create(this.locationListPage).present();
        break;
    case 'ProductListPage':
        this.modalCtrl.create(this.productListPage).present();
        break;
    case 'TransactionListPage':
        this.modalCtrl.create(this.transactionListPage).present();
        break;

    }

}
}

Importamos e inyectamos ModalController para crear y mostrar páginas modales.

Importamos diferentes páginas de lista (no estamos usando la carga diferida, por lo que no podemos hacer referencia a las páginas por sus nombres, en su lugar, necesitamos importar sus componentes), luego agregamos un método openModal (página) que crea y presenta un modal para la página que pasamos como parámetro.

Abra src / pages / home / home.html y luego agregue:

<ion-header>
<ion-navbar>
    <button ion-button menuToggle>
    <ion-icon name="menu"></ion-icon>
    </button>
    <ion-title>Product Inventory Manager</ion-title>
</ion-navbar>
</ion-header>

<ion-content>
    <ion-content padding>
    <ion-list>
    <ion-item>
        <button ion-button (click)="openModal('FamilyListPage')" full>MANAGE FAMILIES</button>
    </ion-item>

    <ion-item>
        <button ion-button (click)="openModal('LocationListPage')" full>MANAGE LOCATIONS</button>
    </ion-item>

    <ion-item>
        <button ion-button (click)="openModal('ProductListPage')" full>MANAGE PRODUCTS</button>
    </ion-item>

    <ion-item>
        <button ion-button (click)="openModal('TransactionListPage')" full>MANAGE TRANSACTIONS</button>
    </ion-item>

    </ion-list>
    </ion-content>  
</ion-content>

Ahora debería poder hacer clic en cada botón para abrir un modal para la página correspondiente.

Implementación de páginas de lista (FamilyListPage - LocationListPage - ProductListPage - TransactionListPage)


Puede crear un código que enumere los datos de cada página o podemos hacerlo mejor. Vamos a crear una lista inteligente personalizada que puede enumerar diferentes tipos de datos basados ​​en @Inputs

Creación de una lista inteligente personalizada para mostrar datos de tablas SQLite

Primero comience generando un componente angular personalizado usando la CLI de Ionic:

ionic g component SmartList 

Se creará una carpeta de componentes con una lista inteligente de subcarpetas que contiene los archivos de los componentes.

Si el componente se genera con su propio módulo para carga diferida, comience eliminándolo, luego importe el componente en src / app / app.module.ts y agréguelo a las declaraciones y la matriz entryComponents en el módulo principal de la aplicación

import { SmartListComponent } from '../components/smart-list/smart-list';

@NgModule({
declarations: [
    MyApp,
    SmartListComponent
],
imports: [
    BrowserModule,
    ReactiveFormsModule,
    IonicModule.forRoot(MyApp),
],
bootstrap: [IonicApp],
entryComponents: [
    MyApp,
    SmartListComponent
],
providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    {provide: SQLite ,useClass:SQLiteMock},
    DataServiceProvider

]
})
export class AppModule {}

Ahora implementemos nuestra lista inteligente:

Abra src / components / smart-list / smart-list.html y agregue:

<ion-header>
    <ion-navbar>
        <ion-title></ion-title>
        <ion-buttons end>
        <button ion-button (click)="openAddModal()">Add</button>
        <button ion-button (click)="closeModal()">Close</button>
        </ion-buttons>
    </ion-navbar>
</ion-header>

<ion-content padding>
    <ion-list>

        <ion-item *ngFor="let item of items" >

        <button ion-button (click)="removeItem($event, item)"  item-left icon-only>
            <ion-icon name="remove-circle"></ion-icon>
        </button>

        <h2 item-left>

        </h2>

        <p item-right></p>
        <h3 *ngIf="item.date"></h3>
        <button (click)="openViewModal($event,item)" ion-button clear item-end>View</button>
        <button *ngIf="isSelectable" (click)="selectItem($event,item)" ion-button clear item-end>
            Select
        </button>
        </ion-item>

    </ion-list>

</ion-content>

Abra src / components / smart-list / smart-list.ts y agregue:

importar {Componente, Entrada, Salida, OnInit, OnChanges, EventEmitter} desde '@ angular / core'; importar {ViewController} de 'ionic-angular'; importar {DataServiceProvider, Pager} desde '../../providers/data-service/data-service';

@Component ({selector: 'smart-list', templateUrl: 'smart-list.html'}) clase de exportación SmartListComponent implementa OnInit, OnChanges {

@Input() public tableName :string  ;
@Input("pageTitle") pageTitle ;
@Input("detailsPageName") detailsPageName; 
@Input("isSelectable") isSelectable : boolean = false; 
@Input("needsRefresh") needsRefresh : boolean = false;

@Output("onAdd") addHandler = new EventEmitter() ;
@Output("onView") viewHandler = new EventEmitter<any>() ;

items : Array<Object> ;
pager : Pager;
selected : any;
constructor(private viewCtrl : ViewController , public dataService : DataServiceProvider) {
}
public closeModal(){
    this.viewCtrl.dismiss();
}
public selectItem(e,item){
    this.viewCtrl.dismiss(item);
}    
removeItem(e,item){
    //console.log("remove item from " + this.tableName);
    this.dataService.remove(this.tableName,item).then((r)=>{
    this.fetchData();
    })
}
ngOnChanges(){
    console.log("listing " + this.tableName);
    if(this.needsRefresh)
    {
        this.fetchData();
    }

}

ngOnInit(){
    console.log("listing " + this.tableName);
    this.fetchData();
}
fetchData(){
    this.dataService.list(this.tableName).then((o)=>{
    this.pager = <Pager>o;
    this.pager.initialPage().then((oo : Array<Object>)=>{
        this.items = oo;
        console.log(JSON.stringify(this.items));
    });

    })
}

public openAddModal(){
    this.addHandler.emit();
}

public openViewModal(e,item){
    this.viewHandler.emit(item);
}
}

El componente personalizado toma 4 @Inputs para la configuración

El nombre de la tabla SQLite desde donde obtener datos:

@Input () public tableName: cadena;

El título para mostrar en la página:

@Input ("pageTitle") pageTitle;

El nombre de la página de detalles correspondiente:

@Input ("detailsPageName") detailsPageName;

Un booleano basado en el cual mostramos un botón de selección en la lista:

@Input ("isSelectable") isSelectable: boolean = false;

Un booleano basado en el cual le decimos al componente que necesitamos actualizar su pantalla:

@Input ("needRefresh") needsRefresh: boolean = falso;

El componente tiene dos @Outputs o eventos

Este evento personalizado es disparado por el componente cuando hacemos clic en el botón Agregar de la página.

@Output("onAdd") addHandler = new EventEmitter() ;

Este evento personalizado es disparado por el componente cuando hacemos clic en el botón de vista de los elementos de la lista:

@Output("onView") viewHandler = new EventEmitter<any>() ;

El componente también declara dos variables:

elementos: Matriz which holds the items to display by the list

pager : Pager which is the pager object that can be used to fetch paginated data for the SQLite database .

The fetchData() method is used to get the pager object returned from the list method of injected DataService and then fetch the first page data and assign the page rows to items array :

fetchData(){
    this.dataService.list(this.tableName).then((o)=>{
    this.pager = <Pager>o;
    this.pager.initialPage().then((oo : Array<Object>)=>{
        this.items = oo;
        console.log(JSON.stringify(this.items));
    });

    })
} 

The component implements two life cycle hooks ngOnInit and ngOnChanges .

On Init we call fetchData() for the first time .

On Changes we check the needsRefresh variable .If it's true we call fetchData again to refresh data .

The closeModal() method closes the current modal by using the .dismiss() method of injected ViewController .

public closeModal(){
    this.viewCtrl.dismiss();
}

The removeItem() removes an item from SQLite table using remove method of injected DataServiceProvider then call fetchData() to refresh data .

removeItem(e,item){
    //console.log("remove item from " + this.tableName);
    this.dataService.remove(this.tableName,item).then((r)=>{
    this.fetchData();
    })
}    

openAddModal() method emits an addHandler custom event to parent component .This method is called when we click on the Add button .

public openAddModal(){
    this.addHandler.emit();
}

openViewModal() method emits an viewHandler custom event ,with the corresponding item as parameter ,to parent component .This method is called when we click the View button of each list item .

public openViewModal(e,item){
    this.viewHandler.emit(item);
}   

Adding Infinite Scroll to Our Smart List

So far the list can display data of the first page from a specified table but if we have more data rows how can we tell the component to fetch the next pages and display them ?

We can use the Ionic Infinite Scroll Component .

Open src/components/smart-list/smart-list.html then add to the bottom of the ion-content:

<ion-content padding>
<ion-list>
    <! --- -->
</ion-list>
<ion-infinite-scroll (ionInfinite)="doInfinite($event)">
    <ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>

</ion-content>

Next on src/components/smart-list/smart-list.ts add doInfinite() method :

public doInfinite(infiniteScroll:any) {

        console.log("Going infinite");
        this.pager.nextPage().then((oo : Array<Object>)=>{

        for(let i = 0;i < oo.length ; i++)
        {
            this.items.push(oo[i]);
        }
        infiniteScroll.complete();

    })     
}

So when the user arrives at the bottom of the screen doInfinite() gets called which calls our pager nextPage() method and append the page rows to the items array .

Now lets see how to use our Smart List to display paginated data from SQLite tables in our pages .

Implementing FamilyListPage

Open src/pages/family-list/family-list.html delete everything then add :

<smart-list 
    pageTitle="Families" 
    tableName="families" 
    (onAdd)="openAddModal()" 
    (onView)="viewItem($event)" 
    [isSelectable]="isSelectable" 
    [needsRefresh]="needsRefresh">
</smart-list>

Now we need to add an implementation for openAddModal() and viewItem() methods which get called when the two custom events are fired.

We also need to add two member variables isSelectable and needsRefresh .

So open src/pages/family-list/family-list.ts

import { Component } from '@angular/core';
import { NavController, NavParams , ModalController } from 'ionic-angular';
import { FamilyDetailsPage } from '../family-details/family-details';

@Component({
selector: 'page-family-list',
templateUrl: 'family-list.html',
})
export class FamilyListPage {

isSelectable : boolean = false;
needsRefresh : boolean = false;

constructor(public modalCtrl : ModalController ,  public navParams : NavParams) {
    this.isSelectable = this.navParams.get("isSelectable") || false;
}

public openAddModal(){
    this.needsRefresh = false;
    let modal  = this.modalCtrl.create(FamilyDetailsPage);
    modal.onDidDismiss((data)=>{
        this.needsRefresh = true;
    });
    modal.present();  
}  

public viewItem(e){
    console.log(e);
    this.needsRefresh = false;
    let modal  = this.modalCtrl.create(FamilyDetailsPage , {item : e});
    modal.onDidDismiss((data)=>{
        this.needsRefresh = true;
    });
    modal.present();        
}

}

the viewItem() method creates and present a modal for family details page and pass item to view or edit as parameter to the page .After dismissing the modal we need to refresh the smart list data because the user may edit the item .Actually this method needs some tweaking to only refresh data only when the user has modified the item .

Why do we need to set needsRefresh to false prior to create the modal ?

That's because we might have needsRefresh set to true if ,for example , have opened the view/edit modal twice . So we need to always re-set needsRefresh to false in order to trigger change detection for the component .

The openAddModal() method creates and open a family details modal page to add a family item .After dismissing the modal we also need to refresh list data so we set needsRefresh to true .This method also needs some more tweaking since the user may dismiss the modal without actually adding any item ,in this case we don't have to trigger change life cycle hook of smart component since no data is added .

Implementing LocationListPage

Like the FamilyListPage we can do the same with the LocationListPage .

So open src/pages/location-list/location-list.html

<smart-list 
    pageTitle="Locations" 
    tableName="locations" 
    (onAdd)="openAddModal()"  
    (onView)="viewItem($event)" 
    [isSelectable]="isSelectable" 
    [needsRefresh]="needsRefresh">
</smart-list>

For LocationListpage.ts It has the same implementation as FamilyListPage .You just need to swap

let modal  = this.modalCtrl.create(FamilyDetailsPage);

with

let modal  = this.modalCtrl.create(LocationDetailsPage);

And of course importing LocationListpage instead of FamilyListPage :

import { LocationDetailsPage } from '../location-details/location-details';

For ProductListPage add

<smart-list 
    pageTitle="Products" 
    tableName="products" 
    (onAdd)="openAddModal()" 
    (onView)="viewItem($event)" 
    [isSelectable]="isSelectable" 
    [needsRefresh]="needsRefresh" >
</smart-list>

For TransactionListPage add

<smart-list 
    pageTitle="Transactions" 
    tableName="transactions" 
    (onAdd)="openAddModal()" 
    (onView)="viewItem($event)" 
    [isSelectable]="isSelectable" 
    [needsRefresh]="needsRefresh">
</smart-list>

Conclusion


We have implemented different list pages for our products ,families ,locations and transactions .

On the next tutorial part we are going to see how to implement details pages for the same tables so we can add ,view and modify items

Ionic 5+ : Using Cordova SQLite and Barcode Scanner to build a Product Inventory Manager [PART 5]


DMCA.com Protection Status

Publicar un comentario

0 Comentarios