rootPage won't display until manual window resize following db operation

My app has some initialisation that needs to take place prior to the first page load. Part of this includes a DB call.

If the db call proceeds setting the rootPage, even after setting the page, I need to manually resize the window to get the page to load. Looking in the Chrome’s developer console, the page structure has loaded, it just takes the resize to inject the rootPage. Very odd.

You can reproduce the issue by creating a blank ionic project, and editing the app.component.ts file as per https://gist.github.com/RhinoLance/6382a225282e9ae20d381114749fee2c

Any thoughts on resolving this would be most welcome.

Lance

Bump. Anyone? After a day of debugging, I still can’t get to the root cause of the issue.

Perhaps if anyone could try and reproduce the issue on Chrome at least, then I would know if it’s a common problem or something unique to my setup.

Ta, Lance

This line seems extraordinarily sketchy:
let db = window["openDatabase"]( "test", "1.0", "bobo_test", 50 * 1024 * 1024);

Why are you doing that that way? I guess I don’t really know what database you’re using, just looks really odd and probably not a great way of handling that.

You also nest an observable inside a promise, which while probably not the cause of the problem is not a good way of doing that. What’s logged to the console? That the db call completes?

Hi @rloui, thanks for taking a look.

  1. What’s wrong with the db call? If you mean the window[“openDatabase”] part, I don’t recall, but had something to do with something not working a long time ago. I just re-use that line as is works. :wink:

  2. I’m all ears for a better pattern to get rid of the observable inside a promise. I think you can see what I’m trying to achieve, so open to suggestions.

  3. Yes, the db call completes as expected.

This can’t be true. 100% guarantee there’s a way to do this in a nonblocking way.

I would also just hide the splash screen once the db started rather than setting the root page. I’m not saying that’s necessarily the problem, but seems like that might be a better way.

I would suggest that you use the Ionic Native SQLite plugin, which is all promisified for you: https://ionicframework.com/docs/native/sqlite/. Also if you don’t have huge amounts of data or complex relations the Ionic Storage plugin is simpler to work with. Depends on your use case.

I would also recommend that you do not use transaction(), as it is about to be removed from the plugin: https://github.com/litehelpers/Cordova-sqlite-storage#important-api-deprecation-notice.

All database calls should really live in a service, rather than right in your page.

I would do (and have previously done) something like this (disclaimer I’ve never tried to access sqlite_master, but I assume it works fine here, certainly any tables you create will work. I also have not run this code, just made it up, might need minor tweaks):

@Injectable
export class DatabaseService {
  constructor(private sqlite: SQLite) {}

  private initializeDatabase(): Promise<SQLiteObject> {
    return this.sqlite.create({
      name: 'test',
      location: 'default'
    });
  }

  async getSqlMasterCount(): Promise<number> {
    const db = await this.intializeDatabase();
    const results = await db.executeSql('SELECT count(*) FROM sqlite_master');
    return results.rows.items(0);
  }
}

Then back in your page:

export class MyApp implements OnInit {
  rootPage:any = HomePage;

  constructor(
    platform: Platform, 
    statusBar: StatusBar, 
    splashScreen: SplashScreen,
    databaseService: DatabaseService
  ) {}

  async ngOnInit() {
    try {
      await this.platform.ready();
      await this.databaseService.getSqlMasterCount();
      this.splashScreen.hide();
    } catch(error) {
      console.log('error during initialization');
    }
  }

I hid the splash screen rather than setting the root. If you for some reason really need to reset the root instead of hiding the splash screen, you should use the NavController to set the root: https://ionicframework.com/docs/api/navigation/NavController/#setRoot

this.navController.setRoot(HomePage)

I also use OnInit to do the initialization code instead of the constructor, but you could just call your method from the constructor instead if you wanted.

Finally, if you do set the root rather than hiding the splash screen, be sure to show a loading spinner at least, otherwise your user will think the app is frozen/hanging.

@AaronSterling, Yes, you’re right, I could do it in a non blocking way, but at the expense of increased complexity in the rest of the code base. The compromise was a well considered approach.

@rloui, thanks for a very detailed response.
The example I provided was the most stripped back example I could provide which exhibited the behaviour. The actual code does use dedicated services, SQLite plugin etc. Also, I need a full relational database for my data structures. Thanks for the comments though.

Great heads up re transactions being mothballed too. I wasn’t aware of that.

The async ngOnInit is new to me though. I’ll have a look at it. My current work around if I can’t get it working, is to set the root page early as you suggested, but to a dummy/loading page, then when all init is complete, change to the “actual” first page and hide the splashscreen.

I’ll let you know how I get on.

Lance