ドットインストールで公開されているBackbone.js入門をCoffeeScriptで書きなおしたので公開します

みなさんこんにちは。

最近はやっとJavaScriptをがっつりいじることが出来そうになったので、Backbone.jsに入門してみました。
また、CoffeeScriptも便利だと聞いたのでこれも勉強してみました。

勉強のためにドットインストールで公開されているBackbone.js入門の完成品を参考に、JavaScriptのコードをCoffeeScriptで書きなおしました。

これからCoffeeScriptを勉強しようと思っている人に少しでも役に立てればと思います。

CoffeeScriptもやり始めて1日くらいしかたってないので、ここはCoffeeだとこんな感じに書けるよ!とかあれば教えていただけると幸いですー
ドットインストール便利!いつもお世話になってます!

また、動作環境は以下になります。

CoffeeScript v1.6.3
Backbone.js v1.0.0
jQuery v1.8.3
underscore.js v1.4.3

app.coffeeのコード

以下が今回書きなおしたCoffeeScriptのコードです!
コンパイルすればapp.jsが生成されるのでそれを下のindex.htmlに読みこめば使えるはずです。

app.coffee

jQuery ->

    class Task extends Backbone.Model

        defaults:
            title: 'do something'
            completed: false

        validate: (attrs) ->
            if _.isEmpty attrs.title
                'title must not be empty!'

        initialize: ->
            @on 'invalid', (model, error) ->
                $('#error').html error

    class Tasks extends Backbone.Collection
        model: Task

    class TaskView extends Backbone.View

        tagName: 'li'

        initialize: ->
            @model.on 'destroy', @remove, @
            @model.on 'change', @render, @

        events:
            'click .delete': 'destroy'
            'click .toggle': 'toggle'

        toggle: ->
            @model.set 'completed', !@model.get 'completed'

        destroy: ->
            if confirm 'are you sure?'
                @model.destroy()

        remove: ->
            @.$el.remove()

        template: _.template($('#task-template').html())

        render: ->
            template = @template @model.toJSON()

            @.$el.html template
            @

    class TasksView extends Backbone.View

        tagName: 'ul'

        initialize: ->
            @collection.on 'add', @addNew, @
            @collection.on 'change', @updateCount, @
            @collection.on 'destroy', @updateCount, @

        addNew: (task) ->
            taskView = new TaskView model: task
            @.$el.append taskView.render().el
            $('#title').val('').focus()
            @updateCount()

        updateCount: ->
            uncompletedTasks = @collection.filter( (task) ->
                !task.get 'completed'
            )
            $('#count').html uncompletedTasks.length
            @

        render: ->
            @collection.each (task) ->
                taskView = new TaskView model:task
                @.$el.append taskView.render().el
            , @
            @updateCount()

    class AddTaskView extends Backbone.View
        el: '#addTask'
        events:
            'submit': 'submit'

        submit: (e) ->
            e.preventDefault()
            task = new Task 
            if task.set( {title: $('#title').val()}, {validate: true} )
                @collection.add task
                $('#error').empty()

    tasks = new Tasks([
        {
            title: 'task1'
            completed: true
        }
        {
            title: 'task2'
        }
        {
            title: 'task3'
        }
    ])

    tasksView = new TasksView collection: tasks
    addTaskView = new AddTaskView collection: tasks

    $('#tasks').html tasksView.render().el

index.htmlのコード

index.htmlのコードは以下です!
jqueryとかbackboneとかunderscoreは上のほうにバージョン番号が書いてあるのでそれに合わせて読み込んでくださいー!

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Backbone.js sample</title>
    <style>
    .completed {
        text-decoration: line-through;
        color: gray;
    }
    </style>
</head>
<body>

    <h1>Tasks</h1>

    <form id="addTask">
        <input type="text" id="title">
        <input type="submit" value="add">
        <span id="error"></span>
    </form>

    <div id="tasks"></div>
    <p>Tasks left: <span id="count"></span></p>

    <script type="text/template" id="task-template">
        <input type="checkbox" class="toggle" <%= completed ? 'checked' : '' %>>
        <span class="<%= completed ? 'completed' : '' %>">
        <%- title %>
        </span>
        <span class="delete">[x]</span>
    </script>
    <script src="js/underscore.js"></script>
    <script src="js/jquery.js"></script>
    <script src="js/backbone.js"></script>
    <script src="js/coffee.js"></script>
</body>
</html>

app.coffeeに書きなおす際に困ったこと

今回Coffeeの文法をやったあとに書きなおしてみたんですが、困ったことと言えば

  • jQueryの関数の()を省略しちゃって動かない
  • jQueryでDOMをいじったあとに「@」を書かないと動かない

ってことくらいです。それ以外は結構さらさら書けました。

Coffeeだと()が省略出来るからといって、jQueryの関数まで省略しちゃダメです(´;ω;`)
具体的には35〜37行目の部分とか39〜40行目の部分とかです。

35〜37行目

destroy: ->
    if confirm 'are you sure?'
        @model.destroy()

39〜40行目

remove: ->
    @.$el.remove()

また、Coffeeだと関数内で最後に評価されたものがreturnされてしまうので、DOMをいじったあとそのままにしちゃうとそのDOMをreturnしちゃうのでそこで謎のエラーが起きるってことがありました。
具体的には65〜69行目の部分。

updateCount: ->
    uncompletedTasks = @collection.filter( (task) ->
        !task.get 'completed'
    )
    $('#count').html uncompletedTasks.length
    @

最後に

やっぱCoffeeScriptって便利ですね!
書きなおした際も、元のapp.jsだと200行程度になるんですが、CoffeeScriptで書きなおすと100行程度におさまりました。
あとCoffeeで書いたほうが見やすい! これは癖になるのもわかります。
これからBackbone.jsとかCoffeeScriptとかもっと勉強していこーと思います!
質問やこうしたほうがいいよーっていうのがあれば@DAI199までリプライお願いします!