Javascript 带主干的异步模板加载和渲染后操作

Javascript 带主干的异步模板加载和渲染后操作,javascript,jquery,backbone.js,backbone-boilerplate,Javascript,Jquery,Backbone.js,Backbone Boilerplate,我使用主干样板来呈现模板,它的fetchTemplate方法缓存呈现的模板 我想在呈现的内容上运行一些额外的代码,比如初始化accordios等等,但是使用异步编译的模板来实现这一点比我想象的要复杂得多 以下是一个例子: Duel.Views.Home = Backbone.View.extend({ template: "/templates/duel_home.jade", render: function() { var view = this; statusapp

我使用主干样板来呈现模板,它的fetchTemplate方法缓存呈现的模板

我想在呈现的内容上运行一些额外的代码,比如初始化accordios等等,但是使用异步编译的模板来实现这一点比我想象的要复杂得多

以下是一个例子:

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')
    });
  }
});
除上述内容外,我还使用了一个视图处理程序,如中所述

这是我做的事情

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

$('#content').html(view.render().el)
 $('#content').html(view.render().el)
但发生的情况是,当模板尚未缓存时,会首先调用render,然后及时调用postdrender。另一方面,当模板已经缓存时,立即呈现模板,调用postrender,但是
view.el
尚未插入DOM,因此$(this.el)是一个空列表,$('#duel new')。每个()都是“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);
  });
},

我读了你提供链接的那篇文章——僵尸一号。很好,据我所知,它已经包含了你问题的答案,所需要的就是搜索它。在反复阅读你的问题之后,我能想到的是,你可能喜欢使用.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();
在视图中,您添加了一个“onAddToDom”方法,当您的视图添加到dom中时,该方法将被调用。这可以用来调用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)
var view = Duel.Views.Home({model: mymodel})
viewHandler('#content').showView(view);
 var view = Duel.Views.Home({model: mymodel})
 viewHandler('#content').showView(view)
这被称为

$('#content').html(view.render().el)
if(view.onAddToDom)
    view.onAddToDom();
它将调用(以前称为)postrender()方法

问题解决了


警告:但请注意,如果直接调用
view.render()
,而不是从
viewHandler('selector')中调用,则此操作将失败(即,onAddToDom——或者我们是否调用postrender()?——将不会被调用)。showView
,因为我们从其中调用onAddToDom。但无论如何,这是不需要的,因为如果我们希望在渲染后调用某些东西,我们可以将其添加到
render()
方法本身。我只是想确保没有任何混淆,所以发出了此警告。

您能尝试更改标记行吗:

view.$el
与创建自己的
$(view.el)
相同,这是主干网0.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')
    });
  }
});
  • 您使用的是什么版本的主干网

  • 您使用的是jQuery、zepto还是其他什么?什么版本

  • 只有当模板已缓存且未异步检索时,才会发生此问题?如果是这样,您能在JSFIDLE中创建一个示例吗

  • 出现问题的浏览器是什么

  • 因此,$(this.el)是一个空列表,$('#duel new')。每个()都是“void”

    请准确定义“空列表”和“无效”的含义。除非有严重的错误,否则jQuery
    $(this.el)
    应该是长度为1的jQuery对象。使用jQuery
    $(“#duel new”)。each()
    应该是一个jQuery对象,长度可能为0

    正如@20100所提到的,如果您的主干版本支持它,您最好使用
    this.$el
    而不是
    $(this.el)

  • 这叫

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

  • 这是我做的事情

    var view = Duel.Views.Home({model: mymodel})
    viewHandler('#content').showView(view)
    
    var view = Duel.Views.Home({model: mymodel})
    viewHandler('#content').showView(view);
    
     var view = Duel.Views.Home({model: mymodel})
     viewHandler('#content').showView(view)
    
    这不应该是新的Duel.Views.Home({model:mymodel})
    ?否则,在构造函数中,
    将是
    双视图


  • 没有必要出现所有这些并发症

    原始的
    fetchTemplate
    返回一个jQuery。如果您不知道jQuery的延迟和承诺,那么您的版本也应该如此,现在是查看它们的好时机。回调无效;)

    通过承诺,一切都变得简单: 在
    初始化中
    获取模板并分配承诺。然后,仅当承诺已履行时才提交,例如:

    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
    ,当然可以遵循相同的模式,即在
    视图中包装代码。templatedFetched.done(…)

    是否可以为fetchTemplate函数添加代码?为什么视图作用于其范围之外的元素?在您的视图中不应该有
    $(“#some id”)
    ,视图应该操作的所有元素都应该位于
    this.el
    this.el
    本身之下。另外,你不能只做
    $('#content').html(view.el);view.render()?另一方面,如果您想要一种更通用的方法,可以使用主干.Events,但对于这个问题,我认为这是一种过火的做法。@Esailija说,您不应该在视图中使用$(“选择器”),这会造成混乱。日分