Reputation: 73
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!
Upvotes: 2
Views: 2591
Reputation: 12172
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
Upvotes: 2