问题 在Meteor中,当另一个字段发生变化时,如何更新一个数据库字段?


一般问题:在Meteor中,实现在更新模型时触发的业务逻辑的最佳方式是什么 - 例如,用于更新相关字段或验证或...

具体示例:我想在“Lists”集合中添加“slug”字段 流星todos的例子。每当更改列表名称时,slug都需要自动更新。

这就是我所拥有的......我正在观察 一切 更改为列表以查看是否需要创建/更新其slug。这是一个 共享 models.js(运行服务器和客户端,以获得延迟补偿的好处):

// Lists -- {name: String}
Lists = new Meteor.Collection("lists");

var listsObserver = Lists.find().observe({
    added: updateSlug,
    changed: updateSlug
});

function updateSlug(doc, idx) {
    var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();
    if (slug !== doc.slug) {
        console.log("Updating slug for '" + doc.name + "' to " + slug);
        Lists.update(doc._id, {$set: {slug: slug}});
    }
}

(和最初的todos示例一样,server / publish.js发布了所有 Lists.find() 作为“列表”,client / todos.js订阅该集合。)

上面的代码似乎有效,但不知何故对我来说不太合适。问题:

  1. 观察列表集合是否合理?这好像是 它可能效率低下 - 任何 更改为列表文档将触发此代码。
  2. 我应该在客户端进行不同的(模拟)更新,还是可以让它 同样的Mongo / Minimongo更新同时运行?
  3. 我需要打电话吗? listsObserver.stop() 在某些时候处置观察员? 若然,何时?

(我刚刚开始使用Meteor,所以也许我对其他环境的偏见正在泄漏。这里隐含的元问题是,我是否以正确的方式思考这个问题?)


13118
2017-09-17 17:38


起源

嗯......刚刚找到 这个评论关于Meteor数据验证的旧问题。似乎你应该为模型编辑实现RPC风格的Meteor.methods,然后阻止客户端的minimongo模型自动同步回服务器。 (希望这不是推荐。自动,实时,双向同步是Meteor的一大吸引力。) - medmunds


答案:


我建议使用Collection-Hooks包。它使用钩子之前和之后扩展了收集操作。这比拥有大量Observes或ObserveChanges更好,特别是在收集观察的开销可能变得非常大的服务器上。

这适用于客户端和服务器。如果您在客户端上实现它,您将获得更新本地集合(延迟补偿)的好处,并且更改将被推送到服务器,因此无需再次执行此操作。

您还可以获得仅使用一个MongoDB操作而不是两个或更多操作的好处,就像您使用observes或observeChanges一样。

您可以像这样使用它:

var beforeInsertSlug = function(userId, doc) {
    var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();
    if (slug !== doc.slug) {
        console.log("Updating slug for '" + doc.name + "' to " + slug);
        doc.slug = slug;
    }
};

var beforeUpdateSlug = function(userId, doc, fieldNames, modifier, options){
    if(modifier && modifier.$set && modifier.$set.doc && _.isString(modifier.$set.doc.name)){
        var slug = (modifier.$set.doc.name || '').replace(/\W+/g, '-').toLowerCase();
        if (slug !== doc.slug) {
            console.log("Updating slug for '" + modifier.$set.doc.name + "' to " + slug);
            modifier.$set.doc.slug = slug;
        }
    }

};

Lists.before.insert(beforeInsertSlug);

Lists.before.update(beforeUpdateSlug);

你可以在这里找到包裹: https://atmospherejs.com/matb33/collection-hooks


11
2018-01-23 06:33



哇我甚至都不知道这个存在! - portforwardpodcast
这正是我希望在Meteor核心中找到的。 - medmunds


我在服务器代码中做了类似的事情。基本上将此代码放在Meteor.methods()中,以及您希望对Lists Collection进行的任何其他检查和更新。

虽然下面的代码看起来有点混乱,但从var slug开始的行很难理解:

Meteor.methods({
   myupdate: function (doc) {

     var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();

     if (slug !== doc.slug) {
        console.log("Updating slug for '" + doc.name + "' to " + slug);
        Lists.update(doc._id, {$set: {slug: slug}});
     }
   }
});

1
2017-12-19 18:02



Meteor如何知道如何运行 myupdate 每次有人试图修改列表的名称?或者每个可能修改列表名称的程序员都必须知道 也 呼叫 myupdate 之后? (我正在寻找可以自动运行的东西,并且是防弹的。) - medmunds
在什么情况下你会修改列表名称?即将更改写入数据库?如果是这种情况,只需将上述代码与主要编写相结合即可。如果不让我知道,解决方案可能是使用observeChanges()将代码放入Publish()。 - Giant Elk


实现此目的的一种方法是定义自定义模板函数并在正在更改的模板中触发它。例如:

在client.js中

Template.myTemplate.custom_function_to_update = function() {
    // do my update code.  i.e. MyCollections.Update(...);
}

在带有模板的html文件中

<template name="myTemplate">
    <!-- Normal template code -->
    {{ custom_function_to_update }}
</template>

每次模板“myTemplate”更新时,它都会调用您的方法。


0
2017-09-21 21:14



但模板代码只运行客户端,并且只针对该特定模板,对吧?因此,在不触发业务逻辑的情况下,更改很容易进入模型。使用todo示例,如果只有该模板负责使list.slug保持最新,那么修改list.title的任何其他代码都会留下不同步的slug。 - medmunds
这也将为每个打开该模板的客户端运行一次更新,这不是理想的。 - Thaum Rystra