0

I suppose that the answer will be very obvious, but still it evades me. I'm new on working with observables, and now I'm facing issues assigning a value from one. I had success if I define it (this._apps) as an Observable and asking from the view to the service using subscribe (But for my taste is was way convoluted (three levels inside a map just to return another observable with the array and then another function to subscribe the previous to assign the variable and another subscription in the view to finally show the information), inefficient and on top of that I could not get it "right" again). The task is very simple. Given the class Application

export class Application {
  name: string;
  baseUrl: string;
  deprecated: boolean;
}

And the service (just the relevant code)

private _apps: Application[] = [];

constructor(private _http: HttpClient) {
  this.getAllApplications().subscribe(apps => {
    console.log('Apps subscriber');
    this._apps = apps;
    console.log('Apps subscriber Ends ' + apps);
  },
  err => {
    console.log(err.status); // 401
    console.log(err.error.error); // undefined
    console.log(JSON.parse(err.error).error); // unauthorized
  });
}

private getAllApplications() {
  return this._http.get<Application[]>('http://development:4300/api/v1/apps');
}

From the constructor the function which gets the information from WebAPI is triggered, and the remote call is successful, but the variable this._apps is an empty array if I try to call it from anywhere in the code. I could not determine the type of the parameter "apps" in the subscribe function, but for some reason it cannot be assigned and the best answer given is that it is a function (See my first update) in one of my tries. Currently it returns in the console "[object Object]", but apps[0] gives undefined, so it is an empty Array.

This is the console output, just starting the application:

Angular is running in the development mode. Call enableProdMode() to enable the production mode. Refreshing apps cache calling http://development:4300/api/v1/atbc-apps Apps subscriber Apps subscriber Ends [object Object]

I was trying this solution among many others that I forget (to use the more modern HttpClient instead the Http I used before), so what I'm doing wrong?

Update 1

I changed the constructor to this:

constructor(private _http: HttpClient) {
  this.getAllApplications().subscribe(apps => {
    console.log('apps length ' + apps.length);
    this._apps = apps;  // Remember private _apps: Application[] = [];
    console.log('Apps subscriber Ends ' + apps.toString);
  },
    err => {
      console.log(err.status); // 401
      console.log(err.error.error); // undefined
      console.log(JSON.parse(err.error).error); // unauthorized
    });
}

and the declaration of the function called into this:

private getAllApplications(): Observable<Application[]> {
   // the exactly the same as before
}

And now I got from the console this:

apps length undefined

Apps subscriber Ends function () { if (this instanceof Promise) { return PROMISE_OBJECT_TO_STRING; } return originalObjectToString.apply(this, arguments); }

That is the function I was talking about. Any ideas about why even though there is no errors (nor at compile time, neither at runtime), the returning object is not a real Application array?

George
  • 35
  • 1
  • 10

2 Answers2

0

Change this line:

private _apps: Application[] = [];

to:

_apps: Application[] = [];

Which will default to making it public. Then this line will see it:

this._apps = apps;
JBrooks
  • 9,901
  • 2
  • 28
  • 32
  • I did it, but the problem is the same. On top of that this._apps is visible inside the service (and it is no used outside, and it is accessible through public methods); otherwise, I could not compile the code. In fact running it does not give any error before or with your change, but neither assign it. Thanks for your time. – George Jul 12 '18 at 23:00
0

At the end I suppose is a mindset to work with Observables, and I tried to build a kind of cache, so the only way I could do it (let me know if there is a better way) was using the view to fill-out the cache. I could not do it from the service itself because the calling the function from the view is synchronous and to fill out the array is async. So I had to create a public setApplicationCache procedure which is filled out after calling the service from the view, it call the setApplicationCache( Application[] ) function and the rest works because it takes just the cache to do filtering and other operations or use it from other pages w/o calling the database again and again.

This is the code from the first view called (main page)

ngOnInit() {
  this._myService.getAllApplications().subscribe(arrObjApps => {
  this._myService.setApplicationsCache(arrObjApps)
  this.listApps = this._myService.getApplications(true);
});    

And the service has this functions:

  private _apps: Application[] = [];

  getAllApplications(): Observable<Application[]>  {
    return this._http.get('http://development:4300/api/v1/atbc-apps').pipe(
      map( (response: Response) => {
        let results = response.json().data.map( app => {
        return new Application(app.name, app.baseUrl, app.deprecated);
      });
      return results;
    })
  );
}

getApplication(appName: string): Application {
  return this._apps.find(app => app.name == appName);
}

getApplications(onlyActives: boolean): Application[] {
  if (onlyActives) {
    return this._apps.filter(app => app.deprecated == false);
  } else {
    return this._apps;
  }
}

And as I stated the solution should be obvious. Just again the async mindset required to work with observables.

George
  • 35
  • 1
  • 10