之後, 將 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
張貼留言