0

I have this code snippet below that is not working. The call to the service at the end makes an http call that shows up in the network logs that data has been returned, but that exact data is not being used for the loop in the form valuChanges snippet. What's even more surprising is that the same call to the service is being made in the constructor of my component and that works fine.

const values = this.testService.getPosts();

this.form.valueChanges.subscribe((val) => {
      const temps: testModel[];
      temps = this.testService.getPosts();
      cn = val['name'];
      format = val['cs'];

      // The console.log below mysteriously outputs data, and shows
      // There is data in the array variable
      console.log(temps);

      if (format == 'a') {
        this.testModel = temp;
      } else {
        for (let i = 0; i < temps.length; i++) {
          // The log below prints out no data
          // Also when I go ahead and debug the array is empty. Even
          // the network output shows returned data
          console.log(temps[i]);
      }
   }
});

This is how the getPosts method looks like in testService

getPosts() {
    const tempModel: testModel[] = [];
    this.dataSource.getPosts().subscribe(response => {
      if (response) {
        let value = response;

        value.forEach(tp => {
          const temp = new testModel();
          Object.assign(temp, tp);

          tempModel.push(temp);
        });
      }
    });

    return tempModel;
  }
m..
  • 329
  • 1
  • 2
  • 11
  • Try logging `temps` to the console – SiddAjmera Aug 27 '18 at 19:42
  • what ? no ! `val` has nothing to do with it, `temps = this.testService.getPosts();` what does getPosts do, and don't you dare telling me it's an http request. – Stavm Aug 27 '18 at 19:44
  • @Stavm - it's an async call - I guarantee it. – tymeJV Aug 27 '18 at 19:46
  • Can you please create a stackBlitz so that it's a bit clear? Also you should consider naming your variables so that they make more sense as to what they store. – SiddAjmera Aug 27 '18 at 19:47
  • @Stavm I added the service method. – m.. Aug 27 '18 at 19:52
  • @SiddharthAjmera I am trying abstract away the problem. As this is something am doing for a client. – m.. Aug 27 '18 at 19:54
  • You're returning `tempModel` even before it is populated. – SiddAjmera Aug 27 '18 at 19:56
  • as suspected. you're ignoring the async nature of http requests. – Stavm Aug 27 '18 at 19:57
  • @Stavm How would you best correct this? – m.. Aug 27 '18 at 19:59
  • Possible duplicate of [How do I return the response from an Observable/http/async call in angular2?](https://stackoverflow.com/questions/43055706/how-do-i-return-the-response-from-an-observable-http-async-call-in-angular2) – Reactgular Aug 27 '18 at 19:59
  • @SiddharthAjmera How would also best correct this? – m.. Aug 27 '18 at 19:59
  • simply redesign it. subscribe in your component NOT in your service. – Stavm Aug 27 '18 at 20:01
  • @m.. You're correct, however you have tell the code to run "when the data is available". Since that happens in a delayed fashion, your code needs to wait for that event. In Angular/rxjs that is done via `.subscribe`. It helps me to think of it as "subscribing" to a "data available" event – Vlad274 Aug 27 '18 at 20:07

2 Answers2

1

The return statement should either be inside the subscription, or you should use map instead of subscribe. Something along the lines of this:

getPosts() {
  const tempModel: testModel[] = [];
  return this.dataSource.getPosts()
    .map(response => {
      if (response) {
        let value = response;
        value.forEach(tp => {
          const temp = new testModel();
          Object.assign(temp, tp);
          tempModel.push(temp);
        });
        return tempModel;
      }
      return tempModel;
  });
}

Once you do this, you'll have to subscribe to the what's returned from here so that you get the unwrapped value. Something along the lines of this:

this.form.valueChanges.subscribe(val => {
  this.testService.getPosts().subscribe(tempModel => {
    const temps: testModel[];
    cn = val['name'];
    format = val['cs'];
    format === 'a' ? this.testModel = temp : temps.forEach(temp => console.log(temp));
  });
});
SiddAjmera
  • 38,129
  • 5
  • 72
  • 110
1

Your getPosts method does not wait for your service call to complete before returning, hence you get an empty array. As it's currently written, your code says declare an empty array -> start http request -> return current array.


Your service method should not (and cannot) return an array. Instead, it should return a Promise or Observable for the caller to handle.

getPosts() {
    return this.dataSource.getPosts().map(response => {
      const tempModel: testModel[] = [];
      if (response) {
        let value = response;

        value.forEach(tp => {
          const temp = new testModel();
          Object.assign(temp, tp);

          tempModel.push(temp);
        });
      }

      return tempModel;  
    });
  }

Then you need to invoke the call in a way that waits for the return

this.form.valueChanges.subscribe((val) => {
      const temps: testModel[];
      cn = val['name'];
      format = val['cs'];

      this.testService.getPosts().subscribe(temps => {
        console.log(temps);

        if (format == 'a') {
          this.testModel = temp;
        } else {
          for (let i = 0; i < temps.length; i++) {
            console.log(temps[i]);
        }
      }
   }
});
Vlad274
  • 6,514
  • 2
  • 32
  • 44