Forcing synchronous behavior on a custom Class


#1

Hi all - hope someone can shed a little light, or at least give me a shove down the right path :slight_smile: .

I have a custom Class, “Election”, that takes an XML filename as a parameter, uses http.get to retrieve it, converts it from XML to JSON, then creates a few more objects based on parsing the contents. A large XML file can take nearly 10 seconds to process. However, the html template (let’s call it electionPage) has already rendered with no data because the Election object was empty when it rendered.

How can I force the html template to render AFTER the Election object has been completely created and is ready to use? Is the proper way to do this to make my Election object return a Promise, and then I could do this:
let election = new Election(“file.xml”).then(navController.push(electionPage));

?

If so, how do I create the Promise? I’ve seen that other objects implicitly return them (i.e. http), but can’t find anything on creating the Promise in my constructor myself.

Should I be using Observable, instead?

Thanks,

Bret


#2

Create a loading… message and use *ngIf in the template

Or even nicer, like facebook, dummy fields

Josh morony made a nice tutorial on this


#3

Thanks for the reply, Tommertom. I have put up a spinner and tried the ngif method. If I’m not mistaken, the ngif method can only evaluate variables that are set at that moment and never go back and recheck. For instance:

<div *ngIf="electionObjectReady">
 <code here>
</div>

That section will not be in the page when the Election object is not ready. Nothing will cause it to be dynamically re-eevaluated when the object becomes ready, though, will it? I could, of course, push the page onto the stack of the NavController, but I’d have to know when the Election Object was ready - a catch 22 at this point.

I’ll go see if I can find Josh’s tutorial.

Thanks for the pointer! :slight_smile:

Regards,

Bret


#4

Unless you are doing subversive weird things, simply assigning true to electionObjectReady will automatically trigger reevaluation.


#5

I am mistaken about the tutorial. Cant find it.

But it basically is dummy data with a css class that sets text color and background color to gray until you loaded the data

And u can control the ngIf condition in your ts code, including conditional styling

All basic angular.io stuff


#6

BTW, if at all possible, I would look for ways to do this XML => JSON munging server-side, and deliver only ready-to-eat JSON to the mobile app. Server-side resources are limited only by cost and relatively significantly cheaper (in all senses of the word) than doing things on-device.


#7

Thanks, @rapropos - I appreciate the advice. The files I’m using are put out by NIST in XML format now, though they’re going to JSON soon. I can also convert them myself. I plan to handle JSON in the future, but I have bigger fish to fry at the moment :wink: .

Trying both yours and @Tommertom’s suggestions now…

Thanks again,

Bret


#8

@rapropos - when do you know that things are ready? If I do this:
a();
b();
c()
electionObjectReady = true;

The boolean gets set immediately, before a, b, and c may finish. It’s the asynchronous nature of Angular that’s got me…


#9

The general concept is that a(), b() and c() must return futures of some sort (Promises or Observables). The most straightforward syntax depends on what ways these operations provide for notifying on completion.

Given what you’ve said so far, Promises seem to be a better fit, because each stage of the operation is a one-time process, not an ongoing relationship. If the operations are independent, you can use Promise.all() to run them in parallel and know when all are completed. If they are interdependent, you will need to chain like so:

a().then(ares => b(ares)).then(bres => c(bres)).then(() => { 
 this.electionObjectReady = true;
});

If these operations don’t provide any sort of future, and they have a callback mechanism instead, this is virtually the only situation where you would need to manually instantiate Promises in an Ionic app.

promisifiedA(): Promise<AResult> {
  return new Promise((resolve, reject) => {
    callbackifiedA(food, (result) => resolve(result), (err) => reject(err));
  });
}

#10

I’d rather use the http.get he is using to fetch the data. That gives the place to set the electionObjectReady=true stuff (in the subscribe method).

Where there data collection is done in a provider that returns the Observable, consumed by the page object that subscribes to it and sets the variable

Never let a good observable go to waste (by transforming into promise or even creating new ones).


#11

If the pipeline goes cleanly back that far, that’s a great suggestion. In that case, we would want either forkJoin or mergeMap depending on the interconnection of the operations.


#12

I think I’ve tried that:
this.setContests();
this.printContestNames();
this.getContestNames();
this.ready = true;

The ready=true; gets set before the other 3 methods finish. They should be promisified and chained, too, I guess.


#13

you are indeed way deep in async hell, thinking imperatively…

we need more code to suggest you alternatively. The data loading code and the component code.

And if you are up to it, show it in Stackblitz, so we can fix it on the spot :slight_smile:

here btw is the tutorial…

https://blog.ionicframework.com/improved-perceived-performance-with-skeleton-screens/


#14

Thanks for the link, @Tommertom.

I’ve taken up enough of both you guys’ time already. Looks like I need to immerse myself in Promises and figure this out.

Thanks for hopping in and giving this some thought! :slight_smile:

Regards,

Bret


#15

true.
we will start charging soon :slight_smile:

Good reads:

https://angular.io/guide/architecture
https://angular.io/guide/http
https://angular.io/guide/practical-observable-usage