3

バックボーンボイラープレートを使用してテンプレートをレンダリングしています。そのfetchTemplateメソッドは、レンダリングされたテンプレートをキャッシュします。

アコーディオンの初期化など、レンダリングされたコンテンツに対して追加のコードを実行したいのですが、非同期でコンパイルされたテンプレートを使用してこれを実行するのは、思ったよりも難しいです。

次に例を示します。

Duel.Views.Home = Backbone.View.extend({
  template: "/templates/duel_home.jade",
  render: function() {
    var view = this;
    statusapp.fetchTemplate(this.template, function(tmpl) {
      $(view.el).html( tmpl({duels: view.collection.toJSON()}) );
      view.postrender();
    });
    return this;
  },
  postrender: function() {
    $('#duel-new').each(function() {
      console.log('Found something')
    });
  }
});

上記のほかに、 http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/で概説されているビューハンドラーを使用します

これは私が次のようなことをします

var view = Duel.Views.Home({model: mymodel})
viewHandler('#content').showView(view)

これは呼び出します

$('#content').html(view.render().el)

ただし、テンプレートがまだキャッシュされていない場合は、最初にレンダリングが呼び出され、時間どおりにポストレンダリングが呼び出されます。一方、テンプレートがすでにキャッシュされている場合、テンプレートはすぐにレンダリングされ、postrenderが呼び出されview.elますが、まだDOMに挿入されていないため、$(this.el)は空のリストであり、$('#duel -new')。each()は「void」です。

もちろん、viewHandlerのrender呼び出しの後にpostrenderメソッドを追加することもできますが、これは同じ問題を引き起こしますが、renderメソッドの最初の呼び出しで発生します。テンプレートはまだコンパイルされていないため、postrenderはその要素が存在する前に呼び出されます。したがって、これらの存在しない要素にハンドラーを定義することはできません。

この問題を適切に克服する方法についてのアイデアはありますか?たとえば、.onを使用した単純なクリックイベントの場合は比較的簡単ですが、たとえば、より一般的な構造については$('#tabs').tabs()どうでしょうか。

私のfetchTemplate関数は次のとおりです。

fetchTemplate: function(path, done) {
  window.JST = window.JST || {};

  // Should be an instant synchronous way of getting the template, if it
  // exists in the JST object.
  if (JST[path]) {
    return done(JST[path]);
  }

  // Fetch it asynchronously if not available from JST
  return $.get(path, function(contents) {
    var tmpl = jade.compile(contents,{other: "locals"});
    JST[path] = tmpl;

    return done(tmpl);
  });
},
4

4 に答える 4

5

これらすべての合併症の必要はありません。

オリジナルfetchTemplateはjQuerypromiseを返します。したがって、jQueryのDeferreds and Promisesについて知らない場合は、バージョンを確認することをお勧めします。コールバックは死んでいます;)

promiseを使用すると、すべてが次のように簡単になりますinitialize。テンプレートをフェッチしてpromiseを割り当てます。次に、約束が満たされた場合にのみレンダリングします。次に例を示します。

Duel.Views.Home = Backbone.View.extend({
    initialize: function () {
       this.templateFetched = statusapp.fetchTemplate(this.template);

    },
    ...

    render: function () {
        var view = this;
        this.templateFetched.done(function (tmpl) {
            view.$el.html( tmpl({duels: view.collection.toJSON()}) );
            ... // All your UI extras here...
        });
    }
});

約束が果たさdoneれると、常にすぐに実行されることに注意してください。$elもちろん、ビューの外側でビューを変更する場合、つまりコードをでラップする場合も、同じパターンに従うことができますview.templatedFetched.done(...)

于 2012-05-12T15:47:16.353 に答える
2

私はあなたがリンクを与えた記事を読みました-ゾンビーズのもの。よかったです。私が見る限り、あなたの質問に対する答えはすでに含まれています。必要なのは検索することだけです。あなたの質問を何度か読んで再読した後に私が考えることができるのは、あなたが.NETの方法を使いたいかもしれないということです(ゾンビーズの記事で示唆されているように)。つまり、次のようなものです。

// in showView method of viewHandler
if (this.currentView) {
    this.currentView.close();
}

this.currentView = view;
this.elem.html( this.currentView.render().el );
if ( this.currentView.onAddToDom )  // THIS IS THE IMPORTANT PART.
    this.currentView.onAddToDom();

ビューで、ビューがdomに追加されたときに呼び出される'onAddToDom'メソッドを追加します。これを使用してpostrender()メソッドを呼び出すか、postrender()の名前を「onAddToDom()」に変更することができます。このようにして、問題は解決されます。どのように?説明は次のとおりです。

ビューを次のように再定義できます。

Duel.Views.Home = Backbone.View.extend({
  template: "/templates/duel_home.jade",
  render: function() {
    var view = this;
    statusapp.fetchTemplate(this.template, function(tmpl) {
      $(view.el).html( tmpl({duels: view.collection.toJSON()}) );
    });
    return this;
  },
  onAddToDom: function() {
    $('#duel-new').each(function() {
      console.log('Found something')
    });
  }
});

今、あなたが次のようなことをするとき

var view = Duel.Views.Home({model: mymodel})
viewHandler('#content').showView(view);

これは呼ばれます

$('#content').html(view.render().el)
if(view.onAddToDom)
    view.onAddToDom();

これは(以前は)postrender()メソッドを呼び出します。

問題が解決しました。

警告view.render():ただし、onAddToDomを内部から呼び出すため、内部からではなく直接呼び出された場合、これは失敗します(つまり、onAddToDom-またはpostrender()を呼び出す必要がありますか?-は呼び出されません)viewHandler('selector').showView。ただし、とにかく、これは必要ありません。レンダリング後に何かを呼び出したい場合は、それをrender()メソッド自体に追加できるからです。混乱がないことを確認したかったので、この警告を出しました。

于 2012-05-09T13:53:31.647 に答える
0

マークされた線を変更してみてください:

view.$el独自に作成するのと同じです$(view.el)。これはBackbone0.9.0+のシンタックスシュガーです。

$('#duel-new')DOMツリー全体を精査し$('#duel-new', this.$el)ますが、現在のビューの範囲内でのみチェックするため、DOMトラバーサルに費やされる時間が大幅に短縮されます。

これはあなたの特有の特定の問題を修正する必要はないかもしれませんが、私自身は何の問題もありませんでした

Duel.Views.Home = Backbone.View.extend({
  template: "/templates/duel_home.jade",
  render: function() {
    var view = this;
    statusapp.fetchTemplate(this.template, function(tmpl) {
      view.$el.html( tmpl({duels: view.collection.toJSON()}) ); // change this
      view.postrender();
    });
    return this;
  },
  postrender: function() {
    $('#duel-new', this.$el).each(function() { // change this
      console.log('Found something')
    });
  }
});
于 2012-05-10T11:04:55.923 に答える
0
  1. どのバージョンのバックボーンを使用していますか?

  2. jQuery、zepto、または他のものを使用していますか?どのバージョンですか?

  3. この問題は、テンプレートがすでにキャッシュされていて、非同期で取得されていない場合にのみ発生しますか?その場合、jsfiddleで例を作成できますか?

  4. 問題が発生しているブラウザは何ですか?

  5. したがって、$(this.el)は空のリストであり、$('#duel-new')。each()は「void」です。

    「空のリスト」と「無効」の意味を正確に定義してください。何かがひどく台無しにされていない限り$( this.el )、jQueryでは長さが1$( '#duel-new' ).each()のjQueryオブジェクトである必要があります。jQueryでは、おそらく長さが0のjQueryオブジェクトである必要があります。

    @ 20100で述べたように、Backboneバージョンがそれをサポートしている場合は、this.$elの代わりにを使用することをお勧めします$( this.el )

  6. これは呼び出します

     $('#content').html(view.render().el)
    

    jQuery.html()は文字列引数を受け入れるものとしてのみ文書化されているため、jQueryを使用する場合はこれは良い考えではないと思います。

  7. これは私が次のようなことをします

     var view = Duel.Views.Home({model: mymodel})
     viewHandler('#content').showView(view)
    

    これはいけませんnew Duel.Views.Home( { model : mymodel } )か?それ以外の場合、コンストラクター内では、thisになりますDuel.Views

于 2012-05-10T18:52:17.800 に答える