Javascript Meteor模板更新不一致

Javascript Meteor模板更新不一致,javascript,meteor,meteor-blaze,flow-router,Javascript,Meteor,Meteor Blaze,Flow Router,使用Meteor和blaze模板以及flow router,我发现如果我创建了一个新元素,那么页面不会更新以显示它,但是如果我删除了相同的元素,它会立即消失。以下是模板代码: <template name="EditProject"> ... {{#each currentCounts }} <div class="count-box">{{> CountDelete }}</div> {{/each}}

使用Meteor和blaze模板以及flow router,我发现如果我创建了一个新元素,那么页面不会更新以显示它,但是如果我删除了相同的元素,它会立即消失。以下是模板代码:

<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>
如上所述,单击
.delete count
按钮可删除关联的计数,并使UI更新以显示其已消失。添加计数(通过单击
#add count
)可正确创建计数,但页面不会更新。有一个短暂的闪烁,但仅此而已,刷新页面会显示新的计数。有人能告诉我发生了什么事吗

编辑:以下是评论中要求的订阅:

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);
    })
})
进一步编辑:

当第一次访问此路由时,页面将按其应有的方式呈现,通过
{{{each currentCounts}}
显示数个计数的列表。如果我删除其中一个计数,它会立即从屏幕上消失,但如果我添加一个新计数,它在刷新页面之前不会显示

另一编辑:

根据请求添加侦听器和服务器发布代码(在server/main.js中)。奇怪的是,当再次启动应用程序时,一切都开始正常运行,但在几分钟内,我描述的相同行为又重新出现了

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');
            }
        });

    },

})

进一步的信息:我发现一旦页面被加载,它就会正常工作。但是,如果它被重新加载,那么它就会开始行为不端。因此,我最初没有注意到由于代码的更改,Meteor刷新了页面,因此出现了正确的行为。

为了完整起见,您可能还应该共享方法代码(即
'projects.add count'
'projects.delete count'

尽管如此,我怀疑他们在
Projects
集合中使用
\u id
等于
projectId
counts
数组字段更新文档

在这种情况下,查看您的
'project-counts'
出版物,我们发现它依赖于
Projects.findOne
查询,该查询在服务器端与标准Meteor不起反应

因此,当您添加“计数”时,新文档被添加到
Counts
集合中,其
\u id
很可能正确记录在项目的
Counts
字段中,但这不会重新执行发布代码。因此,游标选择器查询保持不变,并且您的客户机不会在其
计数
集合中收到任何修改

假设方法代码同时加载到客户端和服务器中,客户端将执行存根/模拟,插入此新文档并在本地更新项目的
计数,从而触发UI更新

但是,您的服务器也会执行该方法,这不会导致发布内容发生任何更改,因此
Counts
中的新文档不会发送到客户端,而客户端会将其隐藏。这将创建您观察到的闪烁

现在,当您从
计数
中删除文档时,我们可以假设您特别是从
计数
集合中删除文档,然后更新发布光标,这就是为什么即使未更改发布查询选择器,您的UI也会正确反映删除

最后,当您刷新页面时,您的发布代码将使用
Projects
集合中文档的最新版本进行重新评估,从而生成正确的查询选择器,因此新光标现在将新添加的文档包含在
Counts

然后,为了解决您的问题,您应该尝试使用反应式连接或发布复合包


例如,请参见()

您在哪里订阅了计数集合,它是在editproject模板的route的onWait函数中,还是在模板的onCreated函数中?我已更新帖子以显示计数(和项目)的订阅位置。据我所知,您从项目集合中获取一条记录,然后使用该项目id在计数集合中搜索,我希望计数集合与您订阅的项目计数相同。在Counts集合中,您是否找到返回所有具有_id=project.id的行。您可以尝试使用此hi{{>CountDelete}并查看hi是否打印计数次数。只是为了检查countDelete模板是否存在问题,makerA项目可以有几个计数,并且每个项目中都存储一个计数ID数组。currentCounts应该返回属于该项目的所有计数。显然是这样的,但是在重新绘制UI时有一些有趣的事情正在进行。我已经补充了我的解释。谢谢你提供了一个非常有用的解释。关于
add count
delete count
方法的功能,您确实是正确的,我发现通过添加链接到的包并更新相关订阅,问题得到了解决。
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');
            }
        });

    },

})