0

Using Meteor with blaze templates and flow router, I find that if I create a new element then the page doesn't update to show it, but if I delete the same element it disappears immediately. Here's the template code:

<template name="EditProject">
    ...
    {{#each currentCounts }}
        <div class="count-box">{{> CountDelete }}</div>
    {{/each}}
    ...
    <btn class="btn waves-effect waves-light h-button" id="add-count">Add Count</btn>
    ...
</template>

<template name="CountDelete">
    <div class="card blue-grey lighten-2 count-box">
        <div class="card-content white-text">
            <span class="card-title">
                {{ name }}
            </span>
            {{ notes }}
        </div>
        <div class="card-action">
            <btn class="btn waves-effect waves-light delete-count">
                <i class="mdi mdi-delete"></i>
            </btn>
            <a class="btn waves-effect waves-light" href="/edit_count/{{ _id }}">
                <i class="mdi mdi-pencil"></i>
            </a>
        </div>
    </div>
</template>

The source of currentCounts is this:

Template.EditProject.helpers({
    currentCounts: function() {
        var projectId = FlowRouter.getParam('projectId');
        const project = Projects.findOne(projectId);
        var counts = Counts.find({
            _id: { $in: project.counts }
            },
            {
                sort: { sort_order: -1 }
            }
        );
        return counts;
    }
})

As mentioned, clicking on a .delete-count button deletes the associated count and also causes the UI to update to show that it has gone. Adding a count (from a click on #add-count) creates the count correctly but the page does not update. There's a brief flicker, but that's all, and refreshing the page causes the new count to show up. Could anyone suggest what's going on?

Edit: Here's the subscription, as requested in a comment:

Template.EditProject.onCreated(function() {
    var self = this;
    self.autorun(function() {
        var projectId = FlowRouter.getParam('projectId');
        self.subscribe('single-project',projectId);
        self.subscribe('project-counts',projectId);
    })
})

Further edit:

When first accessing this route the page renders as it should, showing a list of several counts via the {{#each currentCounts}}. If I delete one of those counts it instantly disappears from the screen but if I add a new one it doesn't show up until I refresh the page.

Another edit:

Listeners and server publication code (in server/main.js) added as requested. Oddly, when starting the application again everything started behaving as it should, but within a few minutes the same behaviour I've been describing reasserted itself.

Meteor.publish('project-counts', function projectPublication(projectId) {
    let project = Projects.findOne({_id: projectId, knitter: this.userId});
    return Counts.find({_id: { $in: project.counts }});
});
Meteor.publish('single-project', function projectPublication(projectId) {
    return Projects.find({_id: projectId, knitter: this.userId});
});

'click #add-count'(event) {
    //TODO: Check for duplicate count name
    var projectId = FlowRouter.getParam('projectId');
    var countName = $('#new-count').val();
    var countNotes = $('#new-count-notes').val();

    if (!countName) {
        $("#errors-go-here").empty();
        Blaze.renderWithData(Template.EditProjectErrors, {
            errors: ['You must supply a name for the new count.']
        }, $("#errors-go-here")[0])
        $('.modal').modal();
        $('#validation-errors').modal('open');
        return;
    }

Template.EditProject.events({
    ...
    Meteor.call('projects.add-count',projectId,countName,countNotes, function(error) {
            if (error) {
                console.log("Count add error: " + error);
                Materialize.toast('Failed to add count \'' + countName + '\'!', 3000, 'orange darken-4');
                return false;
            } else {
                Materialize.toast('Count \'' + countName + '\' added!', 3000, 'blue-grey');
                $('#new-count').val(null);
                $('#new-count-notes').val(null);
                // Running this makes the missing count show up, but this is clearly not the right way to do it...
                //location.reload(); 
            }
        });
    },
    ...
)}



Template.CountDelete.events({
    'click .delete-count'(event) {
        var self = this;
        var projectId = FlowRouter.getParam('projectId');
        var countId = self._id;
        const count = Counts.findOne(countId);
        const name = count.name;

        Meteor.call('projects.delete-count',projectId,countId, function(error) {
            if (error) {
                console.log("Count add error: " + error);
                Materialize.toast('Failed to delete count \'' + name + '\'!', 3000, 'orange darken-4');
                return false;
            } else {
                Materialize.toast('Count \'' + count.name + '\' deleted!', 3000, 'blue-grey');
            }
        });

    },

})

Further information: I found that once the page is loaded it behaves as it should. However, if it is reloaded then it starts misbehaving. So, I'd originally not noticed the correct behaviour happening as the page had been refreshed by Meteor due to changes to the code.

knirirr
  • 1,860
  • 4
  • 23
  • 37
  • Where have you subscribed to the counts collection, is it in the editproject template's route's onWait function or in the template's onCreated ? – Vinay Prabhakaran Apr 02 '17 at 15:00
  • I've updated the post to show where the counts (and projects) are subscribed. – knirirr Apr 02 '17 at 15:07
  • from what i understand, you are getting one record from projects collection and then you are searching in Counts collection with that project id and i hope the counts collection is the same as project-counts that you are subscribing to. In the Counts collection are you finding to return all rows with _id = project.id – Vinay Prabhakaran Apr 02 '17 at 15:26
  • can you try with this
    hi {{> CountDelete }}
    and see if hi prints count times. Just to check if countDelete template is the problem maker
    – Vinay Prabhakaran Apr 02 '17 at 15:29
  • A project can have several counts and an array of count IDs is stored in each project. currentCounts is supposed to return all the counts belonging to that project. Clearly it does, but there's some funny business going on with the UI redrawing. I've added to my explanation. – knirirr Apr 02 '17 at 15:29
  • Printing "hi" shows that {{#each currentCounts}} is showing the correct number of counts on load and updates to show the new correct number when one is deleted. It does not update when one is added. – knirirr Apr 02 '17 at 15:34
  • @knirirr: you should probably share your event listeners code (add and delete) – ghybs Apr 03 '17 at 04:59
  • + server publication code – ghybs Apr 03 '17 at 05:07
  • I've added the relevant parts as requested; whilst doing this it seemed to work for a short time, but the problem reasserted itself quickly. This is very odd. – knirirr Apr 03 '17 at 07:49

1 Answers1

1

To be complete, you should probably have also shared the methods code (i.e. 'projects.add-count' and 'projects.delete-count').

That being said, I suspect they update the counts array field of document with _id equal to projectId in Projects collection.

In that case, looking at your 'project-counts' publication, we see that it depends on a Projects.findOne query, which is not reactive on server-side with standard Meteor.

Therefore what happens when you add a "count" is that a new document is added into Counts collection, and its _id is very probably correctly recorded in the project's counts field, but this does not re-execute your publication code. Therefore the cursor selector query is unchanged, and your client does not receive any modification in its Counts collection.

Assuming your method code is loaded in both client and server, the client performs a stub / simulation that inserts this new document and locally updates the project's counts, triggering a UI update.

But then your server executes the method as well, which leads to no change in the publication, therefore the new document in Counts is not sent to the client, which hides it back. This creates the flicker that you observe.

Now when you delete a document from Counts, we can assume that in particular you remove it from Counts collection, which then updates the publication cursor, that is why your UI correctly reflects the deletion, even though the publication query selector was not changed.

Finally, when you refresh the page, your publication code is entirely re-evaluated, using the latest version of your document in Projects collection, which leads to a correct query selector, hence your new cursor now includes the newly added document in Counts.

Then to solve your issue, you should try to use reactive joins or publish composite packages.

E.g. see https://stackoverflow.com/a/32920733/5108796 (Meteor.publish: publish collection which depends on other collection)

Community
  • 1
  • 1
ghybs
  • 47,565
  • 6
  • 74
  • 99
  • Thanks for a very useful explanation. You are indeed correct about the functioning of the `add-count` and `delete-count` methods, and I found that the issue was solved by adding the package to which you linked and updating the relevant subscriptions. – knirirr Apr 03 '17 at 12:20