0

So I've read numerous questions on SO about item conversions in Javascript/Angular but am not finding anything that addresses my issues. I'm pulling from Firestore as an object that can have copies. I then need to convert the object using a 1 to many conversion. The object coming from Firestore is a 'MultipleCard'. This 'MultipleCard' has a property called 'copies' which will be used to signify how many 'Cards' should get created.

  postsCol: AngularFirestoreCollection<MultipleCard>;
  posts: Observable<MultipleCard[]>;

  cardsListObservable: Observable<Card[]>; //Not sure which to use
  cardsList: Card[]; //Not sure which to use

  constructor(private messageService: MessageService,
              private db: AngularFirestore) {
    this.messageService.add('Fetching cards');

    this.postsCol = this.db.collection('cards');
    this.posts = this.postsCol.valueChanges();

 //?? Nothing I do here seems to work correctly. Most operations act on the array itself or do a 1 to 1 conversion
}

Component

<mat-list *ngFor="let card of cardsList | async"> //Or cardsListObservable
  <mat-list-item>{{card.name}}</mat-list-item> 
 </mat-list>

How do I convert Observable into Observable or Card[]? For example I may have an array with the following 'MultipleCard's

 [{ id: 1,
     copies: 3},
    {id: 2, copies:1}]

That should beconverted into an array of 4 'Card' Objects:

[{ id: 1, copyVersion:1},
    { id: 1, copyVersion:2}.
    { id: 1, copyVersion:3},
     { id: 2, copyVersion:1}]

I appreciate any insight!

Edit 1

Tried the following:

this.posts.subscribe((posts) => {
  posts.forEach( post => {
    console.log(post);
    this.cardsList.push(post);
  });
});

But get:

core.js:1350 ERROR TypeError: Cannot read property 'push' of undefined at eval (deck-list.component.ts:40)

Final Updated Code:

  static fromMultiple(multipleCard: MultipleCard): Card[] {
    const data: Card[] = [];
    for (let i = 0; i < multipleCard.copies; i++) {
      data.push(new Card(multipleCard));
    }
  return data;

}

this.postsCol = this.db.collection('cards');
this.posts = this.postsCol.valueChanges();

this.posts.subscribe((posts) => {
  posts.forEach( post => {
    const temp = Card.fromMultiple(post);
    temp.forEach(tempCard => {
      this.cardsList.push(tempCard);
    });
  });
});
easycheese
  • 5,859
  • 10
  • 53
  • 87
  • 2
    What is the actual question / issue? – Zze Jan 29 '18 at 21:43
  • @Zze Upddated with a simplified version of the question – easycheese Jan 29 '18 at 21:51
  • show your service code? – Harry Jan 29 '18 at 21:53
  • @Haris what service code are you referring to? I'm using the AngularFirestore db to pull the objects right in the class I posted above. – easycheese Jan 29 '18 at 22:06
  • You can use `map` to transform your observable to emit an array instead of whatever firebase returns. i.e. `this.posts = this.postsCol.valueChanges().map(x => /*do stuff here*/)`. Also you should be using the async pipe with an observable, i.e. `posts` (`{{ posts | async }}`). Put a breakpoint in your map function - debugging is your friend here. – matmo Jan 29 '18 at 22:11
  • @matmo tried that. Can't seem to get the one too many part working. The map wants to work on the array itself – easycheese Jan 29 '18 at 22:12
  • Seems like step 1 for you is to do a `do` (or `tap` if using the latest rxjs) to figure out what the structure of the items flowing through your observable chain look like. – matmo Jan 29 '18 at 22:44
  • It's just an array of single objects called a 'MultipleCard' @matmo I know the structure because I control the firestore – easycheese Jan 29 '18 at 22:50
  • So what's the problem then? Still unclear on that. – matmo Jan 29 '18 at 22:52
  • @matmo added a bit of an example to hopefully clear it up. The issue is not really related to the database, just a generic observable array conversion question. – easycheese Jan 29 '18 at 22:57
  • Here's some code golf craziness that should do it. Not ideal but I spend a few minutes on it, so enjoy - `posts = posts.reduce((prev, cur) => [...prev, ...((new Array(cur.copies)).fill(cur).map((x, ndx) => ({id: x.id, copyVersion: ndx + 1})))], [])`. In this example, `posts` is the Multicards array you mentioned above (`[{ id: 1, copies: 3}, {id: 2, copies:1}]`) – matmo Jan 29 '18 at 23:17

2 Answers2

2

You can use valueChanges() or snapshotChanges() on the collection to return the Observable from your request.

Here is an example of valueChanges():

this.posts = this.db.collection<MultipleCard[]>('posts').valueChanges();

so this line returns an Observable<MultipleCard[]> which you can then subscribe to and receive the MultipeCard[] array, like so:

this.posts.subscribe((posts) => {
   console.log(posts); //This is the array of posts, not an observable
 });

You do not need to manually subscribe to this in your component as thats what the ASYNC pipe does for you in the markup. You can set your forLoop in your markup like so *ngFor="let post of (posts | async)" and that will also begin the subscription to your Observable data and also close the subscription when this component is destroyed. I hope this can be of help!

You should do some more research on Observable data and the rxjs library that this is utilizing as it will give you more insight on the data you are handling from firebase (specifically firestore).

CozyAzure
  • 8,280
  • 7
  • 34
  • 52
Nicholas Pesa
  • 2,156
  • 2
  • 24
  • 45
  • Check out my edit. This is getting me really close but there is still a problem. I think I tried this before but ran into the same error... – easycheese Jan 29 '18 at 23:15
  • Disregard, I never initialized my array cardsList: MultipleCard[] = []; – easycheese Jan 29 '18 at 23:18
  • I see what you are doing there, but just a heads up after you forEach your posts array and then push each item into the cardsList array, you have two arrays there that are exactly the same. The forEach and push are redundant as you are just recreating the same array. You could just do `this.cardsList = posts;` – Nicholas Pesa Jan 29 '18 at 23:32
  • Yeah I skipped over the part where I will do the conversation and push multiple objects. The key was the subscribe part combined with the for each loop. – easycheese Jan 29 '18 at 23:33
  • this.db.collection gives a Observable an array of arrays. Should now be this.db.collection. – waldo Feb 06 '18 at 23:48
0

You'd either subscribe to your observable stream or promisify it and see what are you getting back. Say a function getData() returns an observable, you can either do

getData().subscribe((card: any) => * do something *) 

or

getData().toPromise().then((card: any) => * do something*)  

I'd suggest doing this and logging in the response (card here). That way you'll have an idea what's happening.

Anjil Dhamala
  • 1,544
  • 3
  • 18
  • 37