之後, 將 Backbone 範例 Todos 改用 CoffeeScript 重寫練習, 果然一開始就踩到地雷。
改寫之後, 是完全不會動的, 後來發現 initialize 方法根本沒被觸發, 但 constructor 有被觸發, 把 constructor 註解後就可以了。也就是使用 initialize 方法, 而不用 constructor。
另外, Backbone 原本的繼承寫法是呼叫 extend 方法, 除了改用 extends 繼承寫法之外, 其初設物件 (第一個參數), 可以放到 initialize 方法裡做初始化的動作。
2012-04-02 補個程式碼
bb_todos.html ----------------------------
<!DOCTYPE html>
<html>
<head>
<title>Backbone Demo: Todos</title>
<link href="todos.css" media="all" rel="stylesheet" type="text/css"/>
<script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"
type="text/javascript" charset="utf-8"></script>
<script src="http://documentcloud.github.com/backbone/test/vendor/json2.js"></script>
<script src="http://documentcloud.github.com/backbone/test/vendor/jquery-1.7.1.js"></script>
<script src="http://documentcloud.github.com/backbone/test/vendor/underscore-1.3.1.js"></script>
<script src="http://documentcloud.github.com/backbone/backbone.js"></script>
<script type="text/coffeescript" src="backbone-localstorage.coffee"></script>
<script type="text/coffeescript" src="todos.coffee"></script>
</head>
<body>
<!-- Todo App Interface -->
<div id="todoapp">
<div class="title">
<h1>Todos</h1>
</div>
<div class="content">
<div id="create-todo">
<input id="new-todo" placeholder="What needs to be done?" type="text" />
<span class="ui-tooltip-top" style="display:none;">Press Enter to save this task</span>
</div>
<div id="todos">
<ul id="todo-list"></ul>
</div>
<div id="todo-stats"></div>
</div>
</div>
<ul id="instructions">
<li>Double-click to edit a todo.</li>
<li><a href="../../docs/todos.html">View the annotated source.</a></li>
</ul>
<div id="credits">
Created by
<br />
<a href="http://jgn.me/">Jérôme Gravel-Niquet</a>
</div>
<!-- Templates -->
<script type="text/template" id="item-template">
<div class="todo <%= done ? 'done' : '' %>">
<div class="display">
<input class="check" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
<div class="todo-text"></div>
<span class="todo-destroy"></span>
</div>
<div class="edit">
<input class="todo-input" type="text" value="" />
</div>
</div>
</script>
<script type="text/template" id="stats-template">
<% if (total) { %>
<span class="todo-count">
<span class="number"><%= remaining %></span>
<span class="word"><%= remaining == 1 ? 'item' : 'items' %></span> left.
</span>
<% } %>
<% if (done) { %>
<span class="todo-clear">
<a href="#">
Clear <span class="number-done"><%= done %></span>
completed <span class="word-done"><%= done == 1 ? 'item' : 'items' %></span>
</a>
</span>
<% } %>
</script>
</body>
</html>
backbone-localstorage.coffee ----------------------------window.s4= ->
(((1+Math.random())*0x10000)|0).toString(16).substring(1)
window.guid= ->
"#{s4()}#{s4()}-#{s4()}-#{s4()}-#{s4()}-#{s4()}#{s4()}#{s4()}"
class window.Store
constructor: (@name) ->
store = localStorage.getItem @name
@data = (store && JSON.parse(store)) || {};
save: -> localStorage.setItem @name, JSON.stringify(@data)
create: (model)->
if ! model.id
model.id = model.attributes.id = guid()
@data[model.id] = model
@save()
model
update: (model)->
@data[model.id] = model
@save()
model
find: (model)->
@data[model.id]
findAll: ->
_.values @data
destroy: (model)->
delete @data[model.id]
@save()
model
Backbone.sync = (method, model, options)->
store = model.localStorage || model.collection.localStorage
switch method
when 'read'
resp = if model.id then store.find(model) else store.findAll()
when 'create'
resp = store.create model
when 'update'
resp = store.update model
when 'delete'
resp = store.destroy model
if resp
options.success resp
else
options.error "Record not found"
todos.coffee ----------------------------$ = jQuery
$ ->
class window.Todo extends Backbone.Model
defaults: ->
done: false
order: Todos.nextOrder()
toggle: ->
@save done: ! @get("done")
class window.TodoList extends Backbone.Collection
initialize: ->
@model = Todo
@localStorage = new Store "todos"
done: ->
@filter (todo)-> todo.get 'done'
remaining: -> @without.apply this, @done()
nextOrder: ->
return 1 if ! @length
@last().get('order') + 1
comparator: (todo)->
todo.get 'order'
class window.TodoView extends Backbone.View
tagName : "li"
events :
"click .check" : "toggleDone"
"dblclick div.todo-text" : "edit"
"click span.todo-destroy" : "clear"
"keypress .todo-input" : "updateOnEnter"
template : _.template $('#item-template').html()
initialize: ->
@model.bind 'change', @render, this
@model.bind 'destroy', @remove, this
render: ->
$(@el).html @template @model.toJSON()
@setText()
@
setText: ->
text = @model.get 'text'
@$('.todo-text').text text
@input = @$('.todo-input')
@input.bind('blur', _.bind(@close, this)).val text
toggleDone: ->
@model.toggle()
edit: ->
$(@el).addClass 'editing'
@input.focus()
close: ->
@model.save {text: @input.val() }
$(@el).removeClass 'editing'
updateOnEnter: (e)->
@close if e.keyCode is 13
remove: ->
$(@el).remove()
clear: ->
@model.destroy()
class window.AppView extends Backbone.View
el : $('#todoapp')
statsTemplate : _.template $('#stats-template').html()
events :
"keypress #new-todo": "createOnEnter"
"keyup #new-todo": "showTooltip"
"click .todo-clear a": "clearCompleted"
initialize: ->
@input = @$('#new-todo')
Todos.bind 'add', @addOne, @
Todos.bind 'reset', @addAll, @
Todos.bind 'all', @render, @
Todos.fetch()
render: ->
@$('#todo-stats').html @statsTemplate
total: Todos.length
done: Todos.done().length
remaining: Todos.remaining().length
addOne: (todo)->
view = new TodoView {model: todo}
$('#todo-list').append view.render().el
addAll: ->
Todos.each @addOne
createOnEnter: (e)->
text = @input.val()
return if not text or e.keyCode isnt 13
Todos.create text:text
@input.val('')
clearCompleted: ->
_.each Todos.done(), (todo)->todo.destroy()
false
showTooltip: (e)->
tooltip = @$('.ui-tooltip-top')
val = @input.val()
tooltip.fadeOut
clearTimeout @tooltipTimeout if @tooltipTimeout
return if val is '' or val is @input.attr('placeholder')
show = -> tooltip.show().fadeIn()
@tooltipTimeout = _.delay show, 1000
window.Todos = new TodoList
window.App = new AppView
3 則留言:
1. constructor 還是可以用, 不過要手動呼叫父類別的建構子。
2. 感覺初始化物件還是放在屬性比較好。
3. 所有的定義請放在 $ -> 底下。
4. 改寫完了還是有些 bugs...
改好了, 補回誤刪的 @ 就好了。
改寫成 CoffeeScript 後的內容:
https://docs.google.com/document/d/12Kgs_jJ4hpeHmMjtWg_IxGxa3CFjx7Coyqs7k4BKvK0/edit
張貼留言