Spreadsheet-like functionality using Meteor


Let’s say I have many input fields that a user can fill out, and various math is done and results are shown in real time. E.g.:

[] x [] = (non editable value)
[] + [] = …

Since you can’t do math in Spacebars ({{someVal * anotherVal}}), I’m wondering what the best way to do this, because using helpers will make things messy pretty quickly, I think:

  totalSheepCost: function() {
    return Template.instance().sheepQty.get() * Template.instance().sheepPrice.get();

  totalChickenCost: function() {
    return Template.instance().chickenQty.get() * Template.instance().chickenPrice.get();

Or if not using ReactiveVar:

return $('#sheep-qty').val() * $('#sheep-price').val(); //might be a bad idea, or not even possible

There’s got to be a better way…


it’s not meteor specific, but I’d recommend handsOnTable. You can then reactively update specific cells as you like.


Well, I don’t literally mean a spreadsheet. Just a few input fields within a form, formatted with Bootstrap.


Another way of wording what I want to be able to do:

<input id="chicken-count" type="number"> x unit cost ${{chickenCost}} = ${{chickenTotal}}
  chickenCost: function() {
    /* some default value stored in a collection or reactive var */
    return ....;

  chickenTotal: function() {
    /* return the chickenCost * $('#chicken-count').val() */

But imagine there were like 100 different farm animals. :slight_smile: I’m trying to think of an optimized way of doing this.


You can use parameters on your helpers, so you could make a general multiplication helper that accept params.

{{multiply sheepQty sheepCost}}

Have multiply return the product of the params. I’m assuming these values are present in your data context. Don’t forget to check for type.


Hmmmm. Ok, that helps quite a bit. But then where do sheepQty and sheepCost come from? I wish I could just directly reference an <input> element’s value and have that be reactive. I guess I can have a change event watch the inputs, and when they change, it collects the value and … does something.


This is where Angular’s ng-model would come in so handy.

<input ng-model="sheepQty" id="sheep-qty">

And then that sheepQty value is “live” and can be accessed directly.


I don’t know where they would come from, perhaps from a document in a collection or another helper. You could have another helper that accepts an id as a param and finds the value if that element of you don’t want to make a helper for hundreds of fields.


I think a client-side collection might be the way to go. Store all input values as they’re entered into the collection.


Agreed, feed all the logic into the local collection & then use your template to just spit out the values. In generic terms MVC terms, keep as much logic out of the view layer as possible.


This is trivially easy with view models:

    Sheep Qty: <input data-bind="value: sheepQty"><br>
    Sheep Cost: <input data-bind="value: sheepCost"><br>
    Sheep Total: <label data-bind="text: sheepTotal"></label><br>
    Chicken Qty: <input data-bind="value: chickenQty"><br>
    Chicken Cost: <input data-bind="value: chickenCost"><br>
    Chicken Total: <label data-bind="text: chickenTotal"></label><br>
    Grand Total: <label data-bind="text: grandTotal"></label><br>
  sheepQty: 0,
  sheepCost: 5,
  sheepTotal: function() {
    return this.sheepQty() * this.sheepCost()
  chickenQty: 0,
  chickenCost: 1,
  chickenTotal: function() {
    return this.chickenQty() * this.chickenCost();
  grandTotal: function() {
    return this.sheepTotal() + this.chickenTotal();

See http://viewmodel.meteor.com


Cool stuff! Too bad I already wrote my own solution. :slight_smile: Will definitely keep that package in mind for the future though.


I was also just about to recommend viewmodel.

There is also https://atmospherejs.com/deanius/worksheet definitely worth checking out.


As a dinosaur, I’d say, “Don’t forget unobtrusive JS + naming conventions”