Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meteor / Mongo - How do you Reference a Lookup Collection? (de-normalize vs normalize)

I'm a Meteor and mongo noob (now a huge Meteor fan), and am struggling with how to implement lookup collections within Meteor.

Scenario:

  • I have a product with custom colors (user defined/unknown amount)
  • A user can add in their own custom colors, then go to the product page and choose from a colors select box what color they want to use for that product
  • If the user goes back and modifies the color hex value (and saves) on the colors page, the product (on the product page) reflects that new colorHex value

What I'm trying to do:

  • Allow the user to go back and modify a pre-existing color on the colors page
  • Have that color hex value change be reflected on the product page
  • Trying to not duplicate data on different collections (and worrying about updating data across multiple collections/data inconsistencies if update fails part way through)

Initially I set up my product schema to hold the color data as well (de-normalized data), though when the color is updated, it's not being updated on the typography document. I have seen that you can use observe of observeChange to essentially listen to changes, then make additional changes, though this seems more complicated than it needs to be. Is there a way that I can reference the colors collection for all colors added to a specific product so that any change is reflected on the product page?

Below is some of the code that I have been working on. I'm using collection2 and iron:Router if that helps any. I have tried concatenating both collections, though this not what I'm after since the each loop also loops through the colors (not desired).

Any help is appreciated. Thank you in advance! -Chris

ProductPage Helper:

Template.productPageTpl.helpers({
  ownProduct: function() {
    return this.userId === Meteor.userId();
  },

  productItems: function() {
    return ProductCollection.find({productId: this._id});
  },

  colorsItems: function() {
    return ColorsCollection.find({productId: this._id});
  },

  // This is my attempt and although give me back both collections, 
  // I only need to reference the colors.
  productAndColorsItems: function() {
    var product = ProductCollection.find({productId: this._id}).fetch();
    var colors = ColorsCollection.find({productId: this._id}).fetch();
    var productColorsJoin = product.concat(colors);
    console.log(productColorsJoin);
    return _.sortBy(productColorsJoin, function(doc) {return doc.createdAt;});
  }
});

Product Page html:

<template name="productPageTpl">
  <div class="product">
    {{#each productAndColorsItems}}
      {{> productItemTpl}}
    {{/each}}
  </div>
</template>

// All the {{properties}} below reference the ProductCollection except for the {{colorHex}}
// {{colorHex}} is a property on the ColorsCollection
<template name="productItemTpl">
  <div id="product-sentence-js" 
       style="color: #{{colorHex}};
              font-family: {{fontFamily}};
              font-weight: {{fontWeight}};
              font-size: {{fontSize}}{{fontUnit}};
              font-style: {{fontStyle}};
              text-transform: {{fontTextTransform}};
              line-height: {{fontLineHeight}}{{fontLineHeightUnit}};
              padding: {{fontPaddingTop}}{{fontPaddingTopUnit}} {{fontPaddingRight}}       {{fontPaddingTopUnit}} {{fontPaddingBottom}}{{fontPaddingTopUnit}} {{fontPaddingLeft}}{{fontPaddingTopUnit}};
              margin: {{fontMarginTop}}{{fontMarginTopUnit}} {{fontMarginRight}}{{fontMarginTopUnit}} {{fontMarginBottom}}{{fontMarginTopUnit}} {{fontMarginLeft}}{{fontMarginTopUnit}};">
    {{productSentence}}
  </div>
</template>

Colors Collection Schema

ColorsCollection = new Mongo.Collection('colors');

var Schema = {};

Schema.ColorsCollectionSchema = new SimpleSchema({
  productId: {
    type: String,
    label: "Product Id",
    max: 500
  },
  userId: {
    type: String,
    label: "User Id",
    max: 500
  },
  author: {
    type: String,
    label: "Author",
    max: 500
  },
  submitted: {
    type: Date,
    label: "Submitted",
    max: 500
  },
  colorName: {
    type: String,
    label: "Color Name",
    max: 500
  },
  colorHexValue: {
    type: String,
    label: "Color Hex Value",
    max: 500
  },
  colorHexValueId: {
    type: String,
    label: "Color Hex Value Id",
    max: 500
  }
});

ColorsCollection.attachSchema(Schema.ColorsCollectionSchema);

So in short, anytime the user changes a color on the colors page, the color document is updated with a new hex value (within the ColorsCollection), and all products that are using that color now reflect that new color hex value.

like image 876
Centinel3 Avatar asked Nov 18 '14 15:11

Centinel3


1 Answers

Here is the solution I came up with. I welcome any alternative / Meteor best practice methods.

First:

I implemeted reactive publist (package) as outlined in this article (https://gentlenode.com/journal/meteor-6-reactive-and-nonreactive-join-with-mongodb/12). This was a great help as it allows the server to push down changes t a page that has already been rendered.

Second:

I implemented the below code. Essentially I'm finding the specific color that the user has selected to edit by referencing the id that was given to that record by Meteor (which won't change). The value of the select box is set to the color id, and am using the data- value to hold the actual hex value. By no means perfect, though when the user edits a color value the product item is updated instantly, as well as the select box values.

Template.productItemTpl.helpers({
  productItemColorHexValue: function() {
    var allColors =  ColorsCollection.findOne({colorHexValueId: this.productColorHexValueId});
    return allColors.colorHexValue;
  }
});

<template name="productColorsSelectOptionsTpl">
    {{#each productColorsSelectOptions}}
      <option data-colorhexvalue="{{colorHexValue}}" value="{{colorHexValueId}}">{{colorName}}     </option>
    {{/each}}
</template>
like image 60
Centinel3 Avatar answered Oct 23 '22 19:10

Centinel3