Backbone.js';燕子';如果另一个事件触发重新渲染,请单击“事件”

Backbone.js';燕子';如果另一个事件触发重新渲染,请单击“事件”,backbone.js,backbone-events,Backbone.js,Backbone Events,我想要实现的是,在表单更改时,应该重新呈现整个视图。这是为了提供刚刚编辑的数据的预览,并在勾选复选框时隐藏表单中的某些元素 当用户编辑字段并单击按钮而不离开该字段时,将同时触发前两个事件:更改,单击。更改处理程序首先更新模型,从而触发表单的重新呈现。当点击事件发生时,什么也没有发生。我想这与重新渲染有关,因为当我注释掉 @model.on 'change', @render, @ 这两个事件处理程序都按应有的方式执行 可能是因为单击目标已从dom中删除,并且添加了一个新按钮,所以没有执行单击处

我想要实现的是,在表单更改时,应该重新呈现整个视图。这是为了提供刚刚编辑的数据的预览,并在勾选复选框时隐藏表单中的某些元素

当用户编辑字段并单击按钮而不离开该字段时,将同时触发前两个事件:更改,单击。更改处理程序首先更新模型,从而触发表单的重新呈现。当点击事件发生时,什么也没有发生。我想这与重新渲染有关,因为当我注释掉

@model.on 'change', @render, @
这两个事件处理程序都按应有的方式执行

可能是因为单击目标已从dom中删除,并且添加了一个新按钮,所以没有执行单击处理程序?我该如何解决这个问题?我以为我写的代码是“惯用的”Backbone.js,但我还在学习:-)

下面是我的代码的简化版本,显示了问题:

您可以在
字段更改
中的更新模型之前增加一点延迟,您可以用
键控
替换
更改
事件。可能有很多解决方法,但最好的方法可能是不要在模型更改时重新渲染整个视图。

让我们添加一些内容,以便我们可以看到发生了什么。首先,我们将使用唯一ID标记“保存”按钮:

render: ->
  id = "b#{Math.floor(Math.random() * 1000)}"
  console.log('button id = ', id)
  #...
然后我们可以看到哪个按钮被按下:

save: ->
  console.log('pressed = ', @$('button').attr('id'))
  #...
我们还将添加一个全局单击处理程序,以查看主干之外的

$(document).on('click', 'button', ->
  console.log('global click = ', @id)
)
实时版本:

对该版本稍作修改,您可能会看到发生了什么:

  • 更改
    的内容
  • 尝试单击“保存”
  • 一旦失去焦点,就会触发更改事件。
  • 该事件调用
    fieldChanged
    ,它执行
    @model.set(…)
  • @model.set
    调用触发主干的事件,特别是视图的
    初始化中的
    @model.on(…)
  • 主干事件将我们发送到
    render
    ,它执行一个
    @$el.html(…)
    ,它同时替换
  • 该调用将杀死视图的
    el
    中的所有DOM元素。但是,这是一个很大的问题,但是,在这个过程完成之前,浏览器需要再次获得控制权
  • 现在我们回到事件队列来处理单击保存。但是我们正在点击的
    是一个僵尸,因为浏览器的工作队列是这样的:处理点击事件,替换3.4中的DOM元素。这里,3.4中的工作尚未完成,因此您正在单击的
    在DOM中处于半死状态,不会响应任何事件
  • 您有两个事件队列相互争斗;您的主干事件正在改变浏览器背后的DOM,而且由于JavaScript是单线程的,浏览器正在丢失并变得混乱

    如果您将
    @$el.html
    调用延迟足够长的时间,以便让浏览器跟上:

    set_html = =>
      @$el.html """
        <input type="text" id="text" value="#{@model.get('foo')}"/>
        <button class="save" id="#{id}">Save</button>
      """
    setTimeout(set_html, 1000) # Go higher if necessary.
    
    如果在save按钮的回调中执行
    @model.save()
    ,则静默更改将被批量验证并发送到服务器。大概是这样的:

    或者跳过
    @model.set
    内的
    字段更改
    ,只需使用
    @model.validate

    fieldChanged: (e) ->
      val = @$(e.currentTarget).val()
      // @model.validate(foo: val) and do something with the return value
    
    并将所有设置内容保留为
    保存

    save: ->
      @model.save(foo: @$('#text').val())
    

    类似这样的内容:

    主干网的全部目的不是让视图在模型更改时重新呈现自己吗?在更新之前添加延迟将在应用新值之前保存模型。。。使用keyup可以在每次按键之后,在用户完成输入之前验证模型。我想了解我的代码失败的原因,以及是否有办法让它像那样工作(自动更新视图)。此外,我的模板中有代码在满足条件时检查某些模型字段和隐藏区域的值,我不想重复。正如您所说,它失败是因为浏览器在单击之前触发更改事件。主干网的整个要点并不是在模型更改时重新渲染视图。谁说的?对于此类任务,您可以使用一些扩展,如或。
    save: ->
      @model.save(foo: @$('#text').val())