3

私が達成したいのは、フォームの変更時にビュー全体を再レンダリングする必要があるということです。これは、編集したばかりのデータのプレビューを提供し、チェック ボックスにチェックを入れたときにフォーム内の特定の要素を非表示にするためです。

ユーザーがフィールドを編集し、フィールドを離れずにボタンをクリックすると、最初の 2 つのイベント (変更、クリック) が同時に発生します。変更ハンドラーは最初にモデルを更新します。これにより、フォームの再レンダリングがトリガーされます。クリック イベントのターンになると、何も起こりません。コメントアウトすると、再レンダリングに関係していると思います

@model.on 'change', @render, @

両方のイベント ハンドラーが正常に実行されます。

クリック ターゲットが dom から削除され、新しいボタンが追加されたため、クリック ハンドラーが実行されていない可能性があります。どうすればこれを修正できますか? 私が書いたコードは「慣用的な」Backbone.jsだと思っていましたが、まだ学んでいます:-)

これは、問題を示す私のコードの簡略版です: jsbin

4

2 に答える 2

2

何が起こっているのかを確認できるように、いくつか追加してみましょう。まず、一意の ID で [保存] ボタンをマークします。

render: ->
  id = "b#{Math.floor(Math.random() * 1000)}"
  console.log('button id = ', id)
  #...

そして、どのボタンが押されたかを見ることができます:

save: ->
  console.log('pressed = ', @$('button').attr('id'))
  #...

<button>バックボーンの外部を監視するグローバル クリック ハンドラーも追加します。

$(document).on('click', 'button', ->
  console.log('global click = ', @id)
)

ライブ版: http://jsbin.com/oviruz/6/edit

そのバージョンを少しいじってみると、何が起こっているかがわかるかもしれません:

  1. の内容を変更します<input>
  2. [保存] をクリックしてみてください。
  3. がフォーカスを失うとすぐに<input>、変更イベントがトリガーされます。
    1. そのイベントはfieldChangedを呼び出します@model.set(...)
    2. この@model.set呼び出しは、バックボーンのイベント、特に@model.on(...)ビューのinitialize.
    3. Backbone イベントは、と の両方を置き換えるrenderを行う に送信します。@$el.html(...)<input><button>
    4. このhtml呼び出しは、ビューの .xml 内のすべての DOM 要素を削除しますel。しかし、これは大きな問題ですが、このプロセスが終了する前に、ブラウザーは再び制御を取得する必要があります。
  4. ここで、イベント キューに戻り、 [保存] のクリックに対処します。しかし、<button>ブラウザの作業キューが次のように見えるため、クリックしているのはゾンビです: クリック イベントを処理し、3.4の DOM 要素を置き換えます。ここでは3.4からの作業が完了していないため、<button>クリックしている は半分が DOM 内にあり、半分が死んでいて、どのイベントにも応答しません。

互いに競合する 2 つのイベント キューがあります。あなたのバックボーン イベントはブラウザの背後で 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.

期待どおりの動作が得られます。しかし、それはひどく、恐ろしく、厄介で、恥ずべきことです。

これらの DOM 要素でイベントを処理している間に DOM をいじることは危険をはらんでおり、自分自身を傷つける複雑な方法にすぎません。

renderフィールドが変更されたときにフィールドを検証し、ビューをモデルのイベントにバインドする場合は、手動で検証を行い、サイレントコール"change"を使用する必要があると思います。set

fieldChanged: (e) ->
  field = @$(e.currentTarget)
  @model.set({ foo: field.val() }, { silent: true })
  // @model.validate(@model.attributes) and do something with the return value

[保存] ボタンのコールバックでa を実行する@model.save()と、サイレントな変更がまとめて検証され、サーバーに送信されます。このようなもの: http://jsbin.com/oviruz/7/edit

@model.setまたは、内部をスキップして次fieldChangedを使用します@model.validate

fieldChanged: (e) ->
  val = @$(e.currentTarget).val()
  // @model.validate(foo: val) and do something with the return value

のすべての設定を残しますsave

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

このようなもの: http://jsbin.com/oviruz/8/edit

于 2012-10-06T19:10:40.473 に答える
0

でモデルを更新する前に少し遅延を追加できます。イベントを でfieldChange置き換えることができます。多くの回避策があるかもしれませんが、おそらく最善の方法は、モデルの変更時にビュー全体を再レンダリングしないことです。changekeyup

于 2012-10-06T17:57:34.073 に答える