Thank you! Your feedback has been delivered
Thank you! Your feedback has been sent

Rendering an array inside a collection in Meteor

I have a collection of decks, and each deck has a number of cards. The field "cards" is an array holding the IDs of the cards in the deck.

Problem is, I have a card list from which the user can choose cards to add to a deck. When the user chooses a card to add to the cards array inside Decks collection, Deps throws an exception saying "can't create second landmark in same branch" unless I don't use a partial to render the list, which is an issue for me since each card has its own events. Though the data is added properly to the deck since when I refresh the page, the updates appear.

Decks.js

Template.deckList.deck = () ->
  Decks.findOne(_id: Session.get "deck").cards

Deck-list.html

<template name="deckList">
    <section class="deck-list"><h1>deck</h1>
    <ul class="cards">
        {{#each deck}}
            {{> cardInList}}
        {{/each}}
    </ul></section>
</template>

Now I thought of making a separate collection to hold both IDs (card and deck), but that might not work for future collections with the same issues (hand for example in the game collection)

Thanks!

Note This questions has been copied from Stack Overflow. Posted by doctor-penguin. The bounty is provided by CodersClan.

User Gravatar

CodersClan-Team

Posted Nov 3 2013 11:35 UTC

$25



1 Replies


You're on the right track, but if I've understood you correctly you've got a bad design there. You don't want to have to update an array in the deck document every time you add/delete a card. It would be easier for you to leave out the cards field in the deck documents, and instead add a deckId field to the card documents. While MongoDB often encourages nested/embedded fields, Meteor Collections generally work far better with typical relational-database style schemas. Check out this approach to your problem:

Decks.js

Template.deckList.deck = () ->
  Decks.findOne( _id: Session.get "deck" )

Template.deckList.cards = () ->
  Cards.find( deckId: Session.get "deck" )

Deck-list.html

<template name="deckList">
  <section class="deck-list">
    <h1>{{#with deck}} {{title}} {{/with}} Deck</h1>
    <ul class="cards">
      {{#each cards}}
      {{> card }}
      {{/each}}
    </ul>
  </section>
</template>

<template name="card">
  <li>{{foobar}}</li>
</template>

Using this approach, you can simply add/delete cards to/from your decks, and the changes will be automatically reflected in realtime without the need to update an additional document in another database collection.

EDIT: If you want a many-to-many collection instead of a one-to-many, you can modify the publish method on the server to return the cards for a particular deck, and avoid the need to publish that connection table to the client. It might look something like:

// Server publish method
// Return just cards that are in deck "deckId"
Meteor.publish('cards', function (deckId) {
    var cardIds = CardsDecks.find({ deckId: deckId }).map(function (connector) {
        return connector.cardId;
    });

    return Cards.find({ _id: {$in: cardIds } });
});

// Client subscribe method
Meteor.subscribe('cards', Session.get('currentDeckId')); // Get just the cards related to the current deck

Cheers!

User Gravatar

BenjaminRH

Posted Nov 3 2013 11:36 UTC

Solution

This didn't solve your task? Get your own custom solution.

Add a reply

By posting a reply on CodersClan you agree to our Terms & Conditions