2
votes

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!

1
This question has been sponsored by CodersClan! Get expert support for your personal code problems with bounties.BenjaminRH

1 Answers

2
votes

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!

Note: This was originally answered on CodersClan