Best practice advice - Observable, promises and ordering within a provider


#1

I’ve programmed a few apps with Ionic and recently decided to move to Ionic2. I’m trying to wrap my head around all the new stuff available. I’ve read the doc and quite a few Angular 2 articles but can’t seem to break through the following problem.

I want to create a provider which will keep some application common data available throughout every page (like user parameter).

I want these parameters to be synced between the user devices.

If someone has a tutorial or some docs that would cover those topics it would be great!

Here’s the rough way I’d like to do it but I need to know if this is the right way or what is the best practice:

export class ParameterProvider {

private parameter: string;

constructor(public http: Http, public storage: Storage) {
}

getParameter(): Observable {
if (this.parameter !== null) { //if the parameter is already in memory return it
return Observable.create(observer => {
observer.next(this.parameter);
observer.complete();
});
}
// otherwise look in the storage and return it
return Observable.fromPromise(
this.storage.get(‘Parameter’).then(parameter => {
if (parameter !== null) {
this.parameter = parameter;
}
else {
//since the value was not available in storage, fetch it from server
return this.http.get(‘http://url/parameter’)
.map(res => {
this.parameter = res.json().parameter;
}
}
}

And then the page would need to subscribe to the getParameter() and wait for it to resolve.

I’m having a hard time wrapping my head around the observable returning a promise upgraded to observable that might have to call another observable from http…

Any help is greatly appreciated.


#2

Any time I’m contemplating something that complex, I think to myself “am I sure there isn’t an easier way?”. A good rule of thumb is “be very very reluctant to create Promises or Observables (aside from Subjects as public object properties intended for communication)”. Another is “don’t store bare data fetched asynchronously in services, only in components”. If we follow those two rules, we end up with this:

parameter: ReplaySubject<string> = new ReplaySubject<string>(1);

constructor(public http: Http, public storage: Storage) {
  storage.get('Parameter').then((param) => {
    if (param) {
      this.parameter.next(param);
    } else {
      http.get('url').subscribe((rsp) => {
        this.parameter.next(rsp.json().parameter);
        // you could save it in storage here if you want
      }
      // TODO: error handling
    }
  }
}

class Page {
  parameter: string = "loading...or whatever dummy value makes sense";

  constructor(pp: ParameterProvider) {
    pp.parameter.subscribe((param) => {
      this.parameter = param;
    }
  }
}

#3

Wow! you must have brain implant to be able to process the forum data and deliver potent answers that quick!

Thank you, I will look into ReplaySubject and try to work with what you suggest, it looks so much more workable than what I was planning.


#4

Once you get comfortable, you might want to take a look at https://mobx.js.org/… it allows you to make plain objects reactive… it handles all of the ugliness or observables and promises for you.

it is used on reactjs and there is a port for angular1 and angular2


#5

Is it possible to use rxjs/Subject as well? What would be the difference?

parameter: Subject = new Subject();


#6

The difference is what happens when you subscribe later than next() is called:

subj.next('foo');
subj.subscribe(waiting => nothingHappens);

With a ReplaySubject, even if you subscribe after the next() was called, you would immediately get foo. So I think a ReplaySubject with a stack depth of 1 makes most sense for this situation.


#7

Great! I was thinking what happens if we subscribe after next is called. Now it totally makes sense :grinning: