Can't update ion-list after promise resolves from SqlStorage

So, I continue down the frustrating path of my first Ionic2/Angular2 app. It’s an alarm app that is really just made to mimic the one that comes standard on an android phone.

I am using SqlStorage to store a list of alarms and then retrieve them. This causes me absolutely no problem on page load. However, I have a little plus button to add a new alarm. When I click this plus button, I can set a breakpoint and see that I am getting back a list of alarms with my newly created alarm in it. I then set this.alarms to that value. But, it is not drawn on the page.

I know Angular 2’s http methods are now returning Observables, and maybe this has something to do with it but I’m pretty lost here. All I want is:

  • User Presses a button
  • Retrieve stuff from SqlStorage
  • Draw it on the screen

It’s step 3 that I cannot get.

Here’s my code for the page:

import {Page} from 'ionic-angular';
import {OnInit} from 'angular2/core';
import {AlarmService} from './alarm.service';

@Page({
  templateUrl: 'build/pages/page1/page1.html',
  providers: [AlarmService]
})
export class Page1 implements OnInit {
  constructor(private alarmService: AlarmService){}

  ngOnInit() {
    this.alarmService.getAlarms().then(alarms => this.alarms = alarms);
  }

  createAlarm() {
    this.alarmService.createAlarm(new Date(), true, true, [0,1,1,1,1,1,0])
        .then(alarms => {
          this.alarms = alarms;
        });
  }
}

It might be helpful to see the AlarmService as well.

Sure thing, I guess I figured since I know it’s returning a promise and I know the data is there it wasn’t needed, but here it is:

import {Injectable} from 'angular2/core';
import {Storage, SqlStorage} from "ionic-angular/index";

@Injectable()
export class AlarmService {
  constructor() {
    this.storage = new Storage(SqlStorage, {});
    this.storage.set("alarms", []);
  }

  createAlarm(time, isActive, isRepeat, daysActive) {
    return this.getAlarms()
        .then(alarms => {
          if (!alarms) alarms = [];
          alarms.push({time, isActive, isRepeat, daysActive});
          return alarms;
        })
        .then(alarms => {
          this.storage.set("alarms", JSON.stringify(alarms));
          return alarms;
        });
  }

  getAlarms() {
    return this.storage.get("alarms").then(alarms => {
      if (!alarms.length) return alarms;
      return JSON.parse(alarms);
    });
  }
}

So, I get the alarms from the database, I push a new alarm onto the list, and then I save those alarms to the database. I could of course cache the alarm list rather than going to the database each time to get the list, but that’s more of an optimize later thing.

And just for confirmation here is a screenshot of a breakpoint in my transpiled code showing the list of alarms coming back:

:thinking:

export class Page1 implements OnInit {
  public alarms: any = null;

I just gave that a shot, but it does not resolve the issue… This obviously declares my alarms upfront as a null object, which is fine, but doesn’t really matter here. It works fine on page load, this seems like a more general issue with re-rendering the dom after a button press/promise resolve.

I appreciate the suggestion but unfortunately I got nothin’ :frowning:

AHA! So…I’m dumb :sweat: this was programmer error.

@rapropos you were so right in asking to see the service, my error lived there. SqlStorage only stores variables as string, so if storage is empty, it will simply store “”. This was causing my getAlarms function to evaluate the length of that string, rather than the array I expected to be stored there.

Although both “”.length and [].length evaluate to false, the problem is I then returned alarms, meaning that instead of returning an empty array I returned an empty string, breaking all the code that added an alarm to the array.

Here’s the updated getAlarms method that now works:

getAlarms() {
    return this.storage.get("alarms").then(alarms => {
      if (alarms === '') return [];
      const alarmObjects = JSON.parse(alarms);
      const alarmObjectListWithDates = [];
      alarmObjects.forEach(item => {
        item.time = new Date(item.time);
        alarmObjectListWithDates.push(item);
      });
      return alarmObjectListWithDates;
    });
  }

Thanks for your time all, sorry it was my fault. :unamused:

1 Like

Glad you figured it out. Is there a reason you’re building a second copy of the alarms array instead of simply replacing the string times with Dates in place, as in something like this?

var alarmObjects = JSON.parse(alarms);
var nalarms = alarmObjects.length;
for( var i = 0; i < nalarms; i++ ) {
  alarmObjects[i].time = new Date(alarmObjects[i].time);
}
return alarmObjects;

The only reason was just me hacking around while trying to get this to work.

The end plan was an Alarm class. Then that alarm class would get a fromJSON method that would deserialize the date. That way all that would be contained within the alarm object. Otherwise ya I would just do:

const alarmObjects = JSON.parse(alarms);
alarmObjects.forEach(item => item.time = new Date(item.time));
return alarmObjects;

Hi, how you bring app from background to foreground?