1

興味深いバグが発生した可能性がありますが、この場合、コレクションを監視するときにドキュメントを削除するために使用できる関数がないことが原因である可能性があります。または私は誤用observeしています...これは非常によくあることです!

これが私が抱えている問題を再現するサンプルコードです。

この記事の執筆時点ではdevelブランチを使用しているので、これが0.3.5で機能するかどうかはわかりません。

observebug.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>

observebug.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.log。メモの削除ボタンをクリックします。メモは削除されますが、これはクライアントに反映されません。

問題はobserve、コレクションのコピーを作成するためにどのように活用しているかにあると思います(元のコレクションに影響を与えることなく操作できます)。一部の属性だけでなく、ドキュメント全体を削除する関数が必要な気がします(unset)。

編集:http://observebug.meteor.com/で実際の例を参照してください

4

2 に答える 2

1

だから私はそれを理解しました。私は確かにを使用する必要がありましたobserve、そしてその端にMeteorのバグはありません。私が成し遂げようとしていたことに何が関係していたのかについての理解の欠如。幸運なことに、私はMeteorコード自体の中に素晴らしい出発点を見つけ、publishModifiedCursorと呼んでいる_publishCursor関数の調整されたバージョンを作成しました。

調整されたプロジェクトテンプレートとコードは次のとおりです。

observe.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>

observe.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には3つのパラメーターが必要で、最初と2番目のパラメーターは正しいです。ただし、設定を解除する属性を指定するには、3番目のパラメーターが必要です。これはコードのバグであり、Meteorのバグではありません。

ただし、コールバックが呼び出された場合、ドキュメントはすでにコレクションから削除されており、オブジェクトのコピーで作業しているだけであることに注意してください。正確にNotes.find({}).count()は、コールバック中の任意の時点での残りのカウントを明らかにします。

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

2つのコレクションが必要な場合は、2つのコレクションを作成します。1つだけを保持し、あらゆる種類の魔法を実行しようとすることで、実行したいことを実行することさえ意図されていない関数を呼び出していることになります。作成するだけの場合

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

一時的なメモを追跡するためにそれを使用すると、現在発生している問題に遭遇することはありません。

于 2012-05-17T23:33:03.287 に答える