Václav Zeman
Václav Zeman

Reputation: 616

Meteor method reactivity doesn't work

I'm not experienced in Javascript but I've read a ton of articles about Meteor reactivity but still can't figure out why it is not working in my case.

When a new product is added, I want to be recalculated total cost and use it in the totalCost helper so it's almost real time visible in the browser.

Can someone please take a look at my code and try to figure out some logic error? Everything except the reactivity is working on my computer.

I have got this method in /models/Product.js :

Meteor.methods({
    totalProductCost: function() {
      var pipeline = [
        {$match: {owner: Meteor.userId()}},
        {$group: {_id: null, cost: {$sum: "$cost"}}}
      ];
      var data = Products.aggregate(pipeline)["0"].cost; 
      return (data === undefined) ? 0 : data;
    }
  });

Then I've got layout.js in client folder:

if (Meteor.isClient) {

    var handle = Meteor.subscribe("Products", Meteor.userId());

    ProductManager = {
        _productItems: null,
        _dep: new Tracker.Dependency(),

    getProducts: function () {
        this._dep.depend();
        return this._productItems;
    },

    setProducts: function (value) {
        if (value !== this._productItems) {
            this._productItems = value;
            this._dep.changed();
        }
    },

    getTotalCost: function () {
            return ReactiveMethod.call('totalProductCost');
        }
    }

    // TRACKER

    Tracker.autorun(function () {
        if (handle.ready()) {
            ProductManager.setProducts(Products.find().fetch());
        }
    });

    // HELPERS

    Template.boxOverview.helpers({
        "totalCost" : function () {
            return ProductManager.getTotalCost();
        },
    });
}

Upvotes: 1

Views: 93

Answers (1)

Billybobbonnet
Billybobbonnet

Reputation: 3226

It seems that you used a collection.aggregate in a method. If you need reactivity, you need to use a publication rather than a method (or you need to call the method each time you want to refresh). However, if you use your aggregation inside your publication (I assume you use a package for it) you will loose reactivity as well.

What I would advise you is to use a publication without aggregate function. You calculate your product cost by creating a new field and adding it to your cursor. Once, you do that, if you want to keep reactivity, it is necessary to use to use cursor.observeChanges() or just cursor.observe().

Have a look at this example:

var self = this;

// Modify the document we are sending to the client.
function filter(doc) {
  var length = doc.item.length;

  // White list the fields you want to publish.
  var docToPublish = _.pick(doc, [
      'someOtherField'
  ]);

  // Add your custom fields.
  docToPublish.itemLength = length;

  return docToPublish;                        
}

var handle = myCollection.find({}, {fields: {item:1, someOtherField:1}})
            // Use observe since it gives us the the old and new document when something is changing. 
            // If this becomes a performance issue then consider using observeChanges, 
            // but its usually a lot simpler to use observe in cases like this.
            .observe({
                added: function(doc) {
                    self.added("myCollection", doc._id, filter(doc));
                },
                changed: function(newDocument, oldDocument)
                    // When the item count is changing, send update to client.
                    if (newDocument.item.length !== oldDocument.item.length)
                        self.changed("myCollection", newDocument._id, filter(newDocument));
                },
                removed: function(doc) {
                    self.removed("myCollection", doc._id);                    
                });

self.ready();

self.onStop(function () {
  handle.stop();
});

This is taken from here.

Upvotes: 0

Related Questions