Monday 28 October 2019

Getting and Listing Latest Data in Angular

We will look on how to get and have up to date records to be displayed on a view in Angular 8. Assume we divide our codes with repository, service and presentation layers.

First, on repository, we have our codes returning a Promise:
getAllItems() : Promise<ItemInfo[]> {
 return new Promise((resolve, reject) => {
  this.dbInstance.executeSql("SELECT * FROM Item", [])
   .then((rs) => {
    . . .
    resolve(itemsList);
   })
   .catch((err) => reject(err));   
 });
};

We also have this on the repository layer that will be used by service layer to know that the database is ready to be used:
private dbReady: BehaviorSubject<boolean> = new BehaviorSubject(false);

constructor(private plt: Platform) {
 this.plt.ready().then(() => {
  this.initialise()
   .then(() => {
    this.dbReady.next(true);
   })
   .catch((err) => console.error(err)); 
 });
}

getDatabaseState() {
 return this.dbReady.asObservable();
}

Then on the service. Notice that we will be using BehaviorSubject type and its next() method to announce to listeners that there's a change.
export class ItemService {

    // this is a handy variable used to keep the latest data in memory
    private _itemsData: ItemInfo[] = [];

    private _items: BehaviorSubject<ItemInfo[]> = new BehaviorSubject<ItemInfo[]>([]);

    // getter that will be used by the presentation layer to get the items
    get items(): Observable<ItemInfo[]>  {
        return this._items.asObservable();
    }

    constructor(private databaseService: DatabaseService) {
        this.databaseService.getDatabaseState().subscribe(ready => {
            if (ready) {
                this.databaseService.getAllItems()
                    .then((items) => {
                        this._itemsData = items;

                        this._items.next(this._itemsData);
                    })
                    .catch(error => {
                        console.error(error);
                    });
            }
        });
 }

    addItemDetails(item: ItemForm) {
        return new Promise((resolve, reject) => {
            this.databaseService.insertItemDetails(item)
                .then((newItemId) => {                   
                    this._itemsData.push(new ItemInfo(. . .));
                    this._items.next(this._itemsData);
                    resolve();
                })
                .catch(error => {
                    console.error(error);
                    reject('Error: item cannot be inserted into database.')
                });
        });
    }

    editItemDetails(item: ItemForm) {
        return new Promise((resolve, reject) => {
            this.databaseService.updateItemDetails(item)
                .then(() => {
                    for (let i of this._itemsData) {
                        if (i.itemId == i.itemId) {
                            . . .
                        }
                    }
                    this._items.next(this._itemsData);
                    resolve();
                })
                .catch(error => { reject("Error: item cannot be updated in database."); });
        });
    }

    // similarly when deleting, the local variable _itemsData will need to be updated then call the BehaviorSubject next() method to tell the listeners that there is a change.
}

On our presentation (component .ts file), we use:
export class ListItemsPage implements OnInit {
    items: Observable<ItemInfo[]>;

    constructor(private service: ItemService) {
    }

    ngOnInit() {
        this.items = this.service.items;
    }
}

Finally, on the template .html page, we have:
<ion-list>
 <ion-item *ngFor="let i of items | async">
  <ion-label>
   <div>{{i.name}}</div>
  </ion-label>
 </ion-item>
</ion-list>