6

レコードのコレクションを取得してテンプレートに配置し、それらをレンダリング{{#each}}して、最後の DOM ノードがレンダリングされるまで読み込みアイコンを表示したいと考えています。

私の問題は、更新/再描画される最後のDOMノードとしてレンダリングされた最後のアイテムで状態を照会/コールバックを起動する方法が見つからないことです。

私のHTMLファイルでは、次のようになります。

<template name="stuff">
    {{#each items}}
        <div class="coolView">{{cool_stuff}}</div>
    {{/each}}
</template>

そして、クライアント側の JS ファイルで:

// returns all records of Stuff belonging to the currently logged-in user
Template.stuff.items = function () {
    Session.set('loading_stuff', true);
    return Items.find({owner: Meteor.userId()}, {sort: {created_time: -1}});
};

Template.stuff.rendered = function() {
    // every time something new is rendered, there will have been loading of the DOM. 
    // wait a short while after the last render to clear any loading indication.
    Session.set('loading_stuff', true);
    Meteor.setTimeout(function() {Session.set('loading_stuff', false);}, 300);
};

Session 変数loading_stuffは Handlebars ヘルパーでクエリされ、ロードしているクラスの名前が返されます (ローダー アイコン GIF 付き) true

私が厄介なことをする理由Meteor.setTimeoutは、テンプレートにレンダリングされたすべてのアイテムの後にTemplate.rendered呼び出されるからです。そのため、まだロード中であることを再確認する必要がありますが、次のものをロードするか、すべてのレンダリングを終了するまでしばらく待ちます。loading_stufffalse

Meteor の適切な方法で、すべての DOM 更新 (特定のテンプレートまたはページ全体) の最後に確実にクエリ/コールバックを行うにはどうすればよいですか?

どうもありがとう。

編集

コールバックを使用したソリューションは部分的にしか機能しません:subscribe() onReady()

テンプレートは、レンダリングが開始される前に複数回 (2 ~ 3 回) 呼び出されるように見えますが、テンプレートがレンダリングに依存するサーバーからデータが返された後です。これは、データのロードが「完了」しているが、DOM はまだレンダリング中であることを意味します。これを適切に行うMeteor.autorun()ための信頼できるフックがどこかにあるかどうかを試してみます。それまでの間、私の最初の質問はまだ残っていると思います:

テンプレート/ページ全体のレンダリングがいつ終了したかを知るにはどうすればよいですか?

最終編集:

結局のところ、DOM の構造に基づいて、開発者は堅実な DOM を用意し、状態を確認したい要素に適切なコールバックをできるだけ適切にアタッチする必要があります。気候に逆らうような簡単なことのように思えるかもしれませんが、私にとっては、テンプレートの最終要素がレンダリングされたことを知る唯一の方法は、テンプレートの最後に特別な ID を持つ要素をレンダリングして、テンプレートの終わりを知らせることです。レンダリングして.livequeryコールバックをアタッチします。今後、この状態をチェックするためのより統一されたグローバルなサポートが Meteor に組み込まれることを願っています。

4

7 に答える 7

4

テンプレートの要素が DOM に書き込まれるのを待っているときに、JQuery の livequery プラグインが機能しないことがわかりました。これは、プラグインが追加のような JQuery メソッドをリッスンするためですが、Meteor は JQuery を使用しません。呼び出されたオブジェクトを使用LiveRangeして DOM に書き込みます。これで動作するように、livequery JavaScript ファイルを変更しました。

ファイルの下部近くに、 を呼び出す行がありますregisterPlugin()。次のように変更しました。

$.livequery.registerPlugin([
    {object: $.fn, methods: ['append', 'prepend', 'after', 'before', 'wrap', 'attr',    'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove', 'html']},
    {object: LiveRange.prototype, methods: ['insertBefore', 'insertAfter']} 
]);

次に、registerPlugin() メソッドを次のように変更しました。

registerPlugin: function(registration) {
    for (var i = 0; i < registration.length; i++) {
        var object = registration[i].object;
        var methods = registration[i].methods;

        $.each( methods, function(i,n) {
            // Short-circuit if the method doesn't exist
            if (!object[n]) return;

            // Save a reference to the original method
            var old = object[n];

            // Create a new method
            object[n] = function() {
                // Call the original method
                var r = old.apply(this, arguments);

                // Request a run of the Live Queries
                $.livequery.run();

                // Return the original methods result
                return r;
            }
        });
    }
}

これにより、オブジェクトでまたはが呼び出されたときに要素が存在するかどうかがチェックさinsertBefore()れ、以前と同様に JQuery で引き続き機能します。insertAfter()LiveRange

于 2013-01-30T03:51:59.127 に答える
3

livequeryのようなjqueryプラグインを使用して、特定のセレクターのdom挿入をフォローアップできます。

$('.my-rendered-element').livequery(function () {
    console.log('I've been added to the DOM');
});

また、画像が読み込まれ、すべてが適切にレンダリングされていることを確認する必要がある場合は、この他のプラグインimagesLoadedなどの他のユーティリティを使用して、次のような操作を行うことができます。

$('.my-rendered-element').livequery(function () {
    $(this).imagesLoaded(function() {
        console.log('My images have been loaded');
    });
});

これは回避策であり、Meteorとうまく統合できない可能性がありますが、これら2人の人は多くの状況で非常に役立つことがわかりました。

于 2013-01-28T15:26:24.297 に答える
2

いくつかのオプションがあります:

  1. 最後のコメントで述べたように、リアクティブ コンテキストを作成し、そこで変更を処理できます。
  2. 特別な値を作成できます。_id = "END" を入力し、テンプレート レンダリングでその値を探します。
  3. Meteor.call私のお気に入り: コレクションを使用する代わりに、 andMeteor.methods呼び出しのペアの結果としてテンプレートを設定します。テンプレートが自動的に再レン​​ダリングされるように、結果をリアクティブ変数に入れることもできます。

既にお気づきかもしれませんが、 のonReadyイベントは、すべてのデータが送信されたときではなくsubscribe()、 publish メソッドが呼び出されたときに発生します。ready()

上記の #3 の簡単な例を次に示します。

クライアントで:

  Meteor.call('get_complete_email',id, function(err,doc) {
    if (err === undefined) {
      // Note current_compose is reactive
      current_compose.set(new _ComposePresenter(action, doc));
    } else {
      log_error('compose','get complete email ',err);
    }
  }); 

サーバー内:

 Meteor.methods({
   'get_complete_email': function(id) {
     return Emails.findOne({_id:id, user_id:Meteor.userId}, body_message_fields);
   }
 });

プレゼンターまたはビューアーのコード: ( datatemp 変数は削除できます。これはレガシーであり、まだリファクタリングされていません)。

Template.compose_to_cc_bcc_subject.prefilled = function(part) {
  if (Current && Current.compose()) { 
    var data = Current.compose();
    if (data == undefined || data[part] == undefined) { return; }
    return data[part]; 
  } else {
    return;
  }
}  

明らかに、current_compose、Current、および独自のオブジェクトを少し異なる方法で接続する必要があります。

于 2013-01-28T13:49:01.527 に答える
2

私の場合、部分的な解決策は次のとおりです。

onComplete()コレクションのサブスクリプションにコールバックを追加しますItems。これは、読み込みプロセスの正式な終了です。そのため、 のハックなsetTimeouts はもう必要ありません。データが照会されたときに ( で) に設定し、次にsubscribe 関数のコールバックで に設定するTemplate.renderedだけです。loading_stufftrueTemplate.stuff.itemsfalseonComplete()

サーバ:

Meteor.publish('items', function (some_param) {
    return Items.find({some_field: some_param});
});

およびクライアント:

Meteor.subscribe('items', some_param, function onComplete() {
    Session.set('loading_stuff', false);
});

...

Template.stuff.items = function () {
    Session.set('loading_stuff', true);
    return Items.find({owner: Meteor.userId()}, {sort: {created_time: -1}});
};

データがサーバーからいつ到着したか、および DOM がいつレンダリングを終了したかを知ることは 2 つの別個の問題であるため、これは部分的な解決策です。

于 2013-01-27T16:14:06.600 に答える
0

私はそれをさらに別のDIY方法で行います。.renderedインデックス/ランクをカーソルにマップし、コールバックで同じクエリの数に対してそのインデックスをチェックします。

items: function() {   
   return Items.find({whatever: whatever}).map(function(item, index) {
      item.rank = index + 1;
      return item;
   });
}

Template.stuff.rendered = function() { 
  if(this.data.rank === Items.find({whatever: this.data.whatever}).count()) {
     //  Do something cool like initializing a sweet
     //  JS plugin now that ALL your template instances 
     //  are all rendered
     $("#coolplugindiv").coolJSPluginInit();
  }
}

サーバーに少し負担がかかると確信していますが、機能します。私は興味がtemplate.lastNodeあり、それを使用して同じ効果を得る方法があるかどうかを考えました.

于 2015-07-28T20:21:35.927 に答える
0

onRendered次のテンプレートのブロックでコールバックを実行すると便利です。

<template name="stuff">
  {{#each items}}
    <div class="coolView">{{cool_stuff}}</div>
  {{/each}}
  {{> didMyStuffLoad}}
</template>

それから:

Template.didMyStuffLoad.rendered = function () {
  Session.set('loading_stuff', false);
}

この onRendered ブロックが実行される前に、すべてが DOM にロードされます。

于 2016-04-28T13:25:24.470 に答える