Using Capacitor to a CRUD an Array

Hi all,

I am trying to get back into coding after not doing some for years since uni so apologies if this is a basic question. I am trying to store arrays of data entered by the user and photos for my expenses app.

From the documentation (https://capacitor.ionicframework.com/docs/apis/storage/) I get you to need to use

async setObject, async getObject() etc. but I am unsure how I feed my array into this. My homepage.ts is copied below. My inputs come from (click) on. I have also added a picture of my code if that’s simpler to read.

Anh help on this at all would be great. I can get it to work for single bits of data but no arrays.

Thanks,

Dave

import { Component } fro`m '@angular/core';`

import { Plugins, CameraResultType, CameraSource } from '@capacitor/core';

import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';

const { Storage} = Plugins;

@Component({

  selector: 'app-home',

  templateUrl: 'home.page.html',

  styleUrls: ['home.page.scss'],

})

export class HomePage {

  Expense_amount: number;

  Expense_reason: string;

  total_exp_start: number = 0;

  result: number =0;

  expList = [];

  newItem;

  Expense;

  dateTime;

  photo: SafeResourceUrl;

add(){

  this.dateTime = new Date();

  this.result += this.Expense_amount;

  this.newItem = this.Expense_reason + '|| Expense Amount:  £' + this.Expense_amount;

  this.expList.push({Expense: 'Expense Reason:  ' + this.newItem + ' ||   Date of Expense: '+ this.dateTime });

   

}

clear(){

  this.Expense_amount = 0;

  this.Expense_reason ="";

}

  constructor(private sanitizer: DomSanitizer) {}

  async takePicture() {

    const image = await Plugins.Camera.getPhoto({

      quality: 100,

      allowEditing: false,

      resultType: CameraResultType.DataUrl,

      source: CameraSource.Camera

    });

  

    this.photo = this.sanitizer.bypassSecurityTrustResourceUrl(image && (image.dataUrl));

  }

  async setItem() {

    const this.expList = JSON.stringify

  await Storage.set({

    key: this.Expense_reason,

    value: this.expList,

  })

  }

}`Preformatted text`

If it were me, I would use strongly type my objects so that I could make use of intellisense. Also, I believe you actually have to pass an open table into the stringify method in order for it to do something.

I would use Ionic Storage instead for this sort of thing, which would have the added benefit of freeing you from ever needing to manually marshal or unmarshal objects or arrays - it just DWIMs.

Hi,

Thanks so much for the feedback! sorry for the delayed response not had much time to code due to work. First of all, does DWIM’s stand for do what I mean?

Also, I am still struggling a little I can now save and load one set of data but when whenever I load the data it simply wipes the previous one in my list. I am looking to have a table which captures all the saved the data. I have had a look online and can only really find more complex solutions which I cant seem to get to work.

Thanks,

Dave

code below


async save() {

this.storage.set(‘amount’,this.newItem)

this.storage.get(‘amount’).then((val) =>{

this.savedExp = (val);

console.log(‘previous expenses’, val);

this.storage.keys()

Right.

Writes are asynchronous, so you have to be super-meticulous about concurrency control if you’re going to use Storage this way. So if you try to get before the set operation has completed, you’ll get the old value. Largely for this reason (also because I don’t like blocking for no good reason) I have this rule: treat Storage only as a way to communicate with future runs of the app:

Write only to the future, and read only from the past.

This means only reading once, at app start time. Communication within a single app run I always do just with service providers that hold either raw data or (more typically) a Subject that clients can subscribe to to receive updates. If you follow this rule, you can safely ignore the return value of set without hitting the class of bugs you’ve seemingly banged into.

Appologies this is taking a while for me to get my head around this. Are you recommending that:-

  1. On the start of the app I load any previous data
  2. Produce a service which holds the inputs from the user and injects them into a component which in this case is the ionic list.

Sorry if I have missed what you were trying to say. Hopefully, it will all click soon.

Yes.

Sort of yes. I am thinking something like this:

export interface Thingy {...}
export class ThingyService {
  thingies$ = new BehaviorSubject<Thingy[]>([]);
  
  constructor(private storage: Storage) {
    storage.ready()
    .then(() => storage.get("thingies")
    .then(thingies => this.thingies$.next(thingies));
  }

  peek(): Thingy[] {
    return this.thingies$.value;
  }

  watch(): Observable<Thingy[]> {
    return this.thingies$;
  }

  poke(thingies: Thingy[]): void {
    this.thingies$.next(thingies);
    this.storage.set("thingies", thingies);
  }

  push(thingy: Thingy): void {
    let thingies = this.peek(); // clone if aliasing is a problem for you
    thingies.push(thingy);
    this.poke(thingies);
  }
}  

You should be able to inject this service anywhere you need access to the global Thingy[] array. peek gives a snapshot in time, watch updates seamlessly if anybody changes things, push adds a new Thingy to the list, and poke replaces the whole array (this can be made private if desired).

Thanks so much for the help! I am going to give a go tomorrow. One final question I have just stumbled upon well trying to implement it is how to pass my IDs to the service. I have seen a few examples and they will have

Interface apples

ID:string

and they take the user Input by doing a ion input

[[ngModel]]=“apples.ID” is this correct method? thanks again and I hope you enjoy the rest of your day! :smile:

There isn’t really a single “correct” way to get user input. I would suggest going through the Angular forms guide for further grounding in your various options, so you can choose which best fits your situation.