Is there any way to force re-render of DOM object?


#1

This should be fairly simple. I made an html table (grid) where the horizontal axis is the months of the year and the vertical axis is various data points. Some of those input boxes are editable, and when I have a change event, it updates the database. The problem is that I also need it to re-render those boxes (and other boxes in the column) whenever there is a change.

Now, if I modify the database directly, the changes are reactive, as I’d expect. When I enter the change through the UI, they are not.

Here is some relevant code.

HTML:

<table>
        <tr>
            <td>Numerator</td>
            {{#each periods}}
            <td>
                <input
                    class="table-item-input"
                    data-itemid="{{_id}}"
                    data-datatype="numerator" data-dept="{{dept}}"
                    data-timestamp="{{timestamp}}" data-measure="{{measure}}"
                    value="{{dataPointValue dataType="numerator" timestamp=timestamp }}">
            </td>
            {{/each}}
        </tr>
</table>

Javascript:

Template.dataEditor.helpers({
periods: function () {
        var ret = [];

        for (var i = 0; i < 12; i++)
        {
            ret.push({
                timestamp: Date.UTC(2015, i, 1),
                measure: Session.get('current-measure')._id});
        }

        return ret;
    },
dataPointValue: function (kw) {
        var dp = DataPoints.findOne({
            timestamp: kw.hash.timestamp,
            measureID: measureID,
            deptID: deptID
        });

        return dp[kw.hash.dataType];
    },
}

#2

when you data enter via the UI why not updated the data in your collection? Then the templates will react.

It looks like periods is not based on a reactive data source. It’s just something you call once. What action triggers it to re-run? What data changes and when? You are not showing your dataeditor template. does it include event handlers?


#3

Periods is not a reactive source, just a simple loop, run once. The
datapointvalue helper is reactive, as it is based on the DataPoints
collection.

I was hoping there was some sort of javascript that I could call on a DOM
change event that would trigger the reactivity.


#4

as @maxhodges asked, why not update the DataPoints collection via DOM events?

for example,

Template.dataEditor.events({

'change input.table-item-input': function(e) {

   $el = $(e.currentTarget);
   var newValue = $el.val();
   var itemId = $el.data('itemid');
   DataPoints.update(itemId, /* etc. */); // or use a method call
}
});

once you do that, your helper should re-run and return the updated data point(s)

Also, it’s imperative that you leverage the correct software design of Meteor’s reactivity, which is de-coupling setters (your UI events) and getters (your helper)


#5

Sorry, maybe I wasn’t clear. I have an event handler which looks like this

"change .table-item-input": function (event) {
    updateDataPoint(event);
}

which goes to this function

var updateDataPoint = function (e) {

    var updateObj = {};
    var newValue = isNumeric(e.target.value) ? +e.target.value : e.target.value;
    updateObj[e.target.dataset.datatype] = newValue;

    var searchObj = {
        deptID: e.target.dataset.dept,
        measureID: e.target.dataset.measure,
        timestamp: +e.target.dataset.timestamp
    };

    var itemID = e.target.dataset.itemid;
    if (!itemID) {
        var dp = DataPoints.findOne(searchObj);
        if (dp)
            itemID = dp._id;
    }

    if (!itemID) {
        itemID = DataPoints.insert(searchObj);
    }

    DataPoints.update(itemID, {$set: updateObj});

}

and the crazy thing is that this actually works. I am able to update the database with this function, but it refuses to update the UI with the database changes.


#6

i’ve tried to recreate what you’ve given us, but a lot of pieces are missing which I’d have to guess about. I posted it here as a temp project which I can delete upon request.

OK so you are returning “periods” in your template’s Each periods loop, but in that data context you are trying to access _id, dept, and and datapoint. As far as I can see periods only has two fields: “timestamp” and “measure”.

I’m not sure of the relationship here. But if periods contain dataPoints, then you need to loop though the dataPoints inside each period, perhaps doing a lookup on the datapoint by period timestamp?

maybe something like this?

{{#each period}}
   {{> datapoint period=period}} //pass the current period to your helper so you can query for the relevant datapoints
{{/each}}

Presently it doesn’t make a lot of sense to me. Feel free to do pull request if you want to add more code and I can reassess the issue.


#7

I wish I could say what I changed, but just after I looked at MaxHodges reply, my program started working as expected.

Max:Thanks so much for your enormous investment of time in this problem. I wish there was a learning point here, but I guess not. :<


#8

Glad to hear it’s working! Hope you have a good experience with Meteor!