1

i have angular 7 component which is tied to a model and there is an array inside that model, the array was populated from a service. and it's populated.

the problem is i can't map over the array although it has elements there. when i console it it shows the array has element. then i tried to console typeOf(array) it always gives object although it is an array !!.

i tried using this soluation but it didn't help either.

any help please?

 export class FooModel {  
   foo : Foo  
   bars: Bar[];
 }

export class SomeComponent implements OnInit {
   model: FooModel;

   constructor(private service: ProjectService) {
    this.model = new FooModel();
    this.model.bars = [];
   } 

   ngOnInit() {
     this.service.getFoos().subscribe((result: any) => {
     // data is populated fine
     this.model=  <FooModel>result.data; 
     });

     Console.log(this.model); // the model has data at this point

     const arr = this.model.bars.map(a=> {
        // never comes here
        return a;
     });

     console.log(arr); // nothing is displayed here

    // this works why ??
    const arr2 = [1,2,3].map(s=> {
        return s;
    }
    console.log(arr2); // it displays [1,2,3]
   }
}
Dicekey
  • 405
  • 4
  • 12
  • It looks like `this.model` is only populated once the `subscribe` callback is executed, which would make this a question just a special case of [How do I return the response from an asynchronous call?](https://stackoverflow.com/q/14220321/1715579) – p.s.w.g Feb 14 '19 at 18:20
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Andreas Feb 14 '19 at 18:25
  • What others have tried to explain ... the problem isn't the `.map`, it's *where* you have the `.map` code. It needs to be *within* the body of the function passed to the subscribe method. Otherwise you are attempting to map *before* the data is returned from the response. I have a diagram that walks through this process here: https://stackoverflow.com/questions/46769042/subscribe-to-observable-is-returning-undefined/46782678#46782678 – DeborahK Feb 14 '19 at 18:44
  • @DeborahK when i console log the model it has data, the data is there, my issue is it considers the array as an object despite it's array that's why it can't map over it – Dicekey Feb 14 '19 at 19:29
  • Could you build a simple stackblitz that demonstrates your issue? – DeborahK Feb 14 '19 at 19:46

3 Answers3

2

As the request is asynchronous, you might need to place the logic within the subscribe,

this.service.getFoos().subscribe((result: any) => {
     // data is populated fine
     this.model=  <FooModel>result.data; 
      const arr = this.model.bars.map(a=> {
        // never comes here
        return a;
     });
     console.log(arr);
});
Sajeetharan
  • 216,225
  • 63
  • 350
  • 396
  • the model has data, i have no problem with this issue, my problem is can't map over object i don't know why it consider it object while it's an array? – Dicekey Feb 14 '19 at 18:21
1

subscription is asynchronous so while it is still working the next line operation in the execution stack will be performed in this case the map you have after the subscription meanwhile it is still being populated in the background. You can try mapping in another life cycle hook say viewChecked hopefully it works. #cheers

Monday A Victor
  • 441
  • 5
  • 16
  • my problem is not with data population, i've managed to get the data, but i can't map on it – Dicekey Feb 14 '19 at 18:39
  • I understood what you meant so I think you should populate the data in the OnInit method but map through it in viewInit or viewChecked method.. just implement any of them and do the mapping within the method – Monday A Victor Feb 15 '19 at 09:24
1

Please look at the comments

 export class FooModel {  
       foo : Foo  
       bars: Bar[];
     }

    export class SomeComponent implements OnInit {
       model: FooModel;

       constructor(private service: ProjectService) {
        this.model = new FooModel();
        this.model.bars = [];
       } 

       ngOnInit() {
         this.service.getFoos().subscribe((result: any) => {
         // data is populated fine
         this.model=  <FooModel>result.data; 
         });
        // the following starts to execute even before the model is populated above.
         const arr = this.model.bars.map(a=> {
            // never comes here because this.model.bars is empty at here and the length is 0 and nothing inside map executes
            return a;
         });

         console.log(arr); // nothing is displayed here because it has nothing inside

        // this works why ?? because you are using on an array which has some items.
        const arr2 = [1,2,3].map(s=> {
            return s;
        }
        console.log(arr2); // it displays [1,2,3]
       }
    }

So as Sajeetharan suggested, you have keep it inside subscribe()

dileepkumar jami
  • 2,185
  • 12
  • 15