ぽっちぽちにしてやんよ

技術ネタとかアプリに関する話とか

Backbone.jsでView.renderを書く場合のセオリー

こんにちは、ぽちです. みなさんBackbone.js使ってますか?Marionetteですか?Chaplinですか?それともngが付いちゃうアレですか?

さて今日は、Backbone.jsのお話です.

タイトルは「Backbone.jsでrenderした時に空divを無くす方法」とかにしようと思ったのですが,なんだかそれに限らない気もしたのでブチあげました.

時間がない人へのまとめ

忙しい人のために,一言でまとめるとこうです.

1
2
3
4
5
6
7
8
9
Class HogeView extends Backbone.View
  initialize: ->
    @listenTo @model, ‘change’, render
    do @render
  render:->
    $oldel = @$el
    $newel = $(<render HTML by template engine>)
    @setElement $newel
    $oldel.replaceWith $newel

次の段落からは簡単な形から流れに沿って最終系になるように説明していきます.

ベーシックなよくある書き方

1
2
3
4
5
6
7
8
9
10
11
class HogeView extends Backbone.View
  el: '.hoge'
  initialize: ->
    do @render
  render:->
    @$el.html ich.hoge_tmpl()

<hoge.jade>
.hoge
script#hoge_tmpl(type="text/html")
  p this is hoge

よくあるのだと、こういう書き方しますよね. この場合、elを指定しているので、既に存在する要素にViewを紐付けています.

ここで使っているテンプレートエンジンはICanHaz.jsです.

要素を作成する場合

次は,既存のの要素ではなく,あるボタンをクリックして 要素を作成するケースを考えてみてください. クリック時に始めてclassがnewされ,それによってDOMに追加されるケースです.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Class HogeView extends Backbone.View
  initialize: ->
    do render
  render:->
    @$el.html(<HTML rendering by template engine>)

Class ButtonView extends Backbone.View
  el: '.add-btn'
  events:
    ‘click': 'append'
  append:->
    v = new HogeView()
    @$el.append(v.el)

<hoge.jade>
a.add-btn add
script#hoge_tmpl(type="text/html")
  .hoge
    p this is hoge 

こんな感じですかね.

ここで問題が. Hogeviewにはelを指定していないので,new時にデフォルトで新しい空divがelになります. そのあと,renderで.htmlするので空divの中にテンプレートの内容が入ったものがelになります.

1
2
3
4
5
<div>
  <div class=“hoge”>
    <p>this is hoge</p>
  </div>
</div>

className/tagNameの導入?

これを回避するには,classNametagNameといったpropertyを使うのが一つの方法です.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class HogeView extends Backbone.View
  className: '.hoge'
  initialize: ->
    do @render
  render:->
    @$el.html ich.hoge_tmpl()

class ButtonView extends Backbone.View
  el: '.add-btn'
  events:
    'click': 'append'
  append:->
    v = new HogeView()
    @$el.append v.el

<hoge.jade>
a.add-btn add
script#hoge_tmpl(type="text/html")
    p this is hoge 

ただし,classNameを用いるためにテンプレートの一番親の要素(.hoge)をHogeViewに持ってきたため見通しが悪い.

setElementを使う

そこでsetElementを使います.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class HogeView extends Backbone.View
  initialize: ->
    do @render
  render:->
    @setElement ich.hoge_tmpl()

class ButtonView extends Backbone.View
  el: '.add-btn'
  events:
    'click': 'append'
  append:->
    v = new HogeView()
    @$el.append v.el

<hoge.jade>
a.add-btn add
script#hoge_tmpl(type="text/html")
  .hoge
    p this is hoge 

とすると空divに挟まずに設定できます.

Modelの更新をトリガーにViewを更新する場合

この方法でも,以下のケースのように一度setElementをしたものを更にsetElementで置き換えるようなコードでは正しく動作しません.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class HogeModel extends Backbone.Model
  defaults:
    index: 0
  initialize: ->
    @listenTo Backbone, 'inc', @inc
  inc: ->
    i = @get 'index'
    @set 'index', i+1
class HogeView extends Backbone.View
  initialize: ->
    @index = 0
    @listenTo @model, 'change', @render
    do @render
  render:->
    @setElement ich.hoge_tmpl
      index: @index++

class ButtonView extends Backbone.View
  el: '.add-btn'
  events:
    'click': 'render'
  initialize: ->
    v = new HogeView
      model: new HogeModel()
    @$el.append v.el
  render:->
    Backbone.trigger 'inc'

<hoge.jade>
script#hoge-tmpl(type=“text/html“)
  .hoge
    p this is hoge

最終系

これを解決するにはこのようにしてあげれば良いです.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class HogeModel extends Backbone.Model
  defaults:
    index: 0
  initialize: ->
    @listenTo Backbone, 'inc', @inc
  inc: ->
    i = @get 'index'
    @set 'index', i+1
class HogeView extends Backbone.View
  initialize: ->
    @index = 0
    @listenTo @model, 'change', @render
    do @render
  render:->
    $oldel = @$el
    $newel = ich.hoge_tmpl
      index: @index++
    @setElement $newel
    $oldel.replaceWith $newel

class ButtonView extends Backbone.View
  el: '.add-btn'
  events:
    'click': 'render'
  initialize: ->
    v = new HogeView
      model: new HogeModel()
    @$el.append v.el
  render:->
    Backbone.trigger 'inc'

<hoge.jade>
script#hoge-tmpl(type=“text/html“)
  .hoge
    p this is hoge

Conclusion

1
2
3
4
5
6
7
8
9
Class HogeView extends Backbone.View
  initialize: ->
    @listenTo @model, ‘change’, render
    do @render
  render:->
    $oldel = @$el
    $newel = $(<render HTML by template engine>)
    @setElement $newel
    $oldel.replaceWith $newel

のようにすることで,テンプレートの構造そのままをViewに適用することが出来て,正しく表示も更新されます.

refs

http://stackoverflow.com/questions/11594961/backbone-not-this-el-wrapping

Comments