1

我遇到了一个有趣的可能错误,但在这种情况下,这可能是由于我在观察集合时缺少用于删除文档的函数。或者我在滥用observe......很可能就是这种情况!

这是将重现我遇到的问题的示例代码。

在撰写本文时,我正在使用 devel 分支,所以我不确定这是否适用于 0.3.5

观察bug.html

<head>
    <title>observebug</title>
</head>

<body>
    {{> main}}
</body>

<template name="main">
    <h1>Example showing possible bug in Meteor wrt observe</h1>
    <div>
        <p>Try to delete a note. You will notice that it doesn't appear to get deleted. However, on the server, the delete did occur. Refresh the page to see that the delete did in fact occur.</p>
        <h2>Notes:</h2>
        <ul>
            {{#each notes}}
                {{> note_row}}
            {{/each}}
        </ul>
    </div>
</template>

<template name="note_row">
    <li>{{title}} <button name="delete">delete</button></li>
</template>

观察bug.js

// CLIENT

if (Meteor.is_client) {
    Notes = new Meteor.Collection("notes_collection");

    Meteor.autosubscribe(function () {
        Meteor.subscribe("notes_subscription");
    });

    Template.main.notes = function () {
        return Notes.find();
    };

    Template.note_row.events = {
        "click button[name='delete']": function (evt) {
            Meteor.call("deleteNote", this._id, function (error, result) {
                if (!error) {
                    console.log("Note deletion successful.");
                } else {
                    console.log("Error when deleting note.");
                }
            });
        }
    };
}

// SERVER

if (Meteor.is_server) {
    Notes = new Meteor.Collection("notes_collection");

    Meteor.methods({
        "deleteNote": function (note_id) {
            try {
                Notes.remove(note_id);
                return true;
            } catch (e) {
                return false;
            }
        }
    });

    Meteor.publish("notes_subscription", function () {
        var notes = Notes.find({}, {sort: {title: 1}});
        var self = this;

        // What we're doing here is basically making an exact duplicate
        // of the notes collection. A possible use-case for this would be
        // to actually make changes to the copied collection on the fly,
        // such as adding new fields without affecting the original
        // collection.
        var upsertHandler = function (note, idx) {
            note.some_new_field = 100;
            self.set("notes_collection", note._id, note);
            self.flush();
        };

        var handle = notes.observe({
            added: upsertHandler,
            changed: upsertHandler,
            removed: function (note, idx) {
                // As far as I can tell, unset won't remove the document,
                // only attributes of the document. I don't think there's
                // a method to handle actually removing a whole document?
                self.unset("notes_collection", note._id);
                self.flush();
            }
        });

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

    // Add example notes
    Meteor.startup(function () {
        if (Notes.find().count() === 0) {
            Notes.insert({title: "Note #1"});
            Notes.insert({title: "Note #2"});
            Notes.insert({title: "Note #3"});
            Notes.insert({title: "Note #4"});
            Notes.insert({title: "Note #5"});
            Notes.insert({title: "Note #6"});
        }
    });
}

你会看到什么

当您启动此应用程序时,您会看到 6 个示例“注释”,每个示例都有一个删除按钮。我建议打开你的控制台,这样你就可以看到console.logs. 单击任何笔记上的删除按钮。该注释确实被删除了,但是这不会反映给客户端。

我怀疑问题在于我如何利用observe来创建集合的副本(然后我可以在不影响原始集合的情况下对其进行操作)。我觉得我需要一个删除整个文档的函数,而不仅仅是一些属性(unset)。

编辑:请参阅http://observebug.meteor.com/上的示例

4

2 回答 2

1

所以我想通了。我确实需要使用observe,并且没有 Meteor 错误。只是对我试图完成的事情缺乏了解。幸运的是,我在 Meteor 代码本身中找到了一个很好的起点,并编写了 _publishCursor 函数的调整版本,我称之为 publishModifiedCursor。

以下是调整后的项目模板和代码:

观察.html

<head>
    <title>observe</title>
</head>

<body>
    {{> main}}
</body>

<template name="main">
    <h1>Example in trying to publish a modified copy of a collection</h1>
    <div>
        <h2>Notes:</h2>
        <ul>
            {{#each notes}}
                {{> note_row}}
            {{/each}}
        </ul>
        <p><button name="add">add note</button></p>
    </div>
</template>

<template name="note_row">
    <li>
        <strong>Original title:</strong> <em>{{title}}</em><br />
        <strong>Modified title:</strong> <em>{{__modified_title}}</em><br />
        <button name="delete">delete</button>
    </li>
</template>

观察.js

// CLIENT

if (Meteor.is_client) {
    ModifiedNotes = new Meteor.Collection("modified_notes_collection");

    Meteor.autosubscribe(function () {
        Meteor.subscribe("modified_notes_subscription");
    });

    Template.main.notes = function () {
        return ModifiedNotes.find();
    };

    Template.main.events = {
        "click button[name='add']": function (evt) {
            Meteor.call("addNote", function (error, result) {
                if (!error) {
                    console.log("Note addition successful.");
                } else {
                    console.log("Error when adding note.");
                }
            });
        }
    };

    Template.note_row.events = {
        "click button[name='delete']": function (evt) {
            Meteor.call("deleteNote", this._id, function (error, result) {
                if (!error) {
                    console.log("Note deletion successful.");
                } else {
                    console.log("Error when deleting note.");
                }
            });
        }
    };
}

// SERVER

if (Meteor.is_server) {
    Notes = new Meteor.Collection("notes_collection");

    Meteor.methods({
        "addNote": function () {
            try {
                Notes.insert({title: "Note #" + (Notes.find().count() + 1)});
                return true;
            } catch (e) {
                return false;
            }
        },
        "deleteNote": function (note_id) {
            try {
                Notes.remove(note_id);
                return true;
            } catch (e) {
                return false;
            }
        }
    });

    Meteor.publish("modified_notes_subscription", function () {
        // Pull up the original notes_collection
        var notes = Notes.find({}, {sort: {title: 1}});

        // Publish a near identical collection, with modifications
        this.publishModifiedCursor(notes, "modified_notes_collection", function (note) {
            note.__modified_title = getTitleModifiedByServer(note.title);
            return note;
        });
    });

    var getTitleModifiedByServer = function (title) {
        return title + "!!!";
    };

    // Add example notes
    Meteor.startup(function () {
        if (Notes.find().count() === 0) {
            Notes.insert({title: "Note #1"});
            Notes.insert({title: "Note #2"});
            Notes.insert({title: "Note #3"});
            Notes.insert({title: "Note #4"});
            Notes.insert({title: "Note #5"});
            Notes.insert({title: "Note #6"});
        }
    });

    _.extend(Meteor._LivedataSubscription.prototype, {
        publishModifiedCursor: function (cursor, name, map_callback) {
            var self = this;
            var collection = name || cursor.collection_name;

            var observe_handle = cursor.observe({
                added: function (obj) {
                    obj = map_callback.call(self, obj);
                    self.set(collection, obj._id, obj);
                    self.flush();
                },
                changed: function (obj, old_idx, old_obj) {
                    var set = {};
                    obj = map_callback.call(self, obj);
                    _.each(obj, function (v, k) {
                        if (!_.isEqual(v, old_obj[k])) {
                            set[k] = v;
                        }
                    });
                    self.set(collection, obj._id, set);
                    var dead_keys = _.difference(_.keys(old_obj), _.keys(obj));
                    self.unset(collection, obj._id, dead_keys);
                    self.flush();
                },
                removed: function (old_obj, old_idx) {
                    old_obj = map_callback.call(self, old_obj);
                    self.unset(collection, old_obj._id, _.keys(old_obj));
                    self.flush();
                }
            });

            self.complete();
            self.flush();

            self.onStop(_.bind(observe_handle.stop, observe_handle));
        }
    });
}

http://observebug.meteor.com/现场观看。最终效果令人印象深刻,因为它只是一个涉及添加/删除笔记的测试......但是现在它可以工作了!

希望这对将来的其他人有所帮助。

于 2012-05-18T11:41:14.450 回答
0

this.unset需要三个参数,第一个和第二个正确;但是,它需要第三个参数来告知要取消设置哪些属性。这是您的代码中的错误,而不是 Meteor 中的错误。

但是,请注意,当您的回调被调用时,文档已经从集合中删除,并且您只是在处理对象的副本。确切地说,Notes.find({}).count()在回调期间的任何时候显示剩余计数。

// I don't think there's  
// a method to handle actually removing a whole document?

如果您想要两个集合,请创建两个集合。通过只保留一个并尝试执行各种魔术,您正在调用甚至不打算执行您希望它们执行的操作的函数。如果你只是创建

Notes_copy = new Meteor.Collection("notes_collection_copy");

并使用它来跟踪临时笔记,您将不会遇到现在遇到的任何麻烦。

于 2012-05-17T23:33:03.287 回答