最終回!Backbone.js+αでリッチなまとめエディタ作成

JavaScriptでクライアントMVCに挑戦! - 第3回 -

過去2回で、HTMLベースのページに、Backbone.jsを導入していく手順を中心にして、Backboneの主機能であるBackbone.ViewとBackbone.Model、Backbone.Collectionについて学んできました。

最終回は、いままで2回で学んだことを組み合わせ、さらにできるだけBackbone.jsの他の機能(Backbone.Sync、Backbone.Events)JavaScriptで動作するまとめエディタ的なものを作っていきましょう。

JavaScirptで動作するまとめエディタのできあがりイメージ 最初に、タイトル・概要を入れて、その後に、段落を追加していき、その中に文章を入れていけるようなNAVERまとめのエディタのテキストのみが入力できることを想定します。 今回の考え方で、テキスト入力の部分を地図や、Facebookのリンク取得モジュール等に置き換えていくことで、リッチなまとめエディタが作成できます。

できあがりイメージは下記のような形となります。

まとめエディタできあがりイメージ

手順としては、まずこのエディタを管理するデータ構造を作成します。 サーバーから、そのデータ構造クラスを利用して初期データの取得や、変更時の保存を行えるようにします。 その後、そのデータを描画するビューを作成して、実際の画面から変更・追加などができる様にしましょう。

今回のワークで書いたサンプルコードはダウンロードからダウンロードできます。

 

エディタを構築するための準備

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
        <title>まとめエディタ</title>
 
        <!--jQueryも併用できるので読み込む-->
        <script src="lib/js/jquery-1.10.2.min.js"></script>
 
        <!--Backboneの読み込み / underscore.jsに依存-->
        <script src="lib/js/underscore-min.js"></script>
        <script src="lib/js/backbone-min.js"></script>
 
        <!--Twitter Bootstrapを読み込む-->
        <link href="lib/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen"/>
        <script src="lib/bootstrap/js/bootstrap.min.js"></script>
 
        <!-- 2. -->  
    </head>
    <body>
        <!-- 1. HTMLを書いていきます -->
    </body>
</html>

HTMLの宣言については、前回とほとんど同じですので、前回のソースコードをコピーしてきてもらってもかまいません。

では、body要素の中に、エディタのルート要素を書きましょう。1の部分に下記を記述します。

1
<div id="editor"></div>

 

1. まずはデータ構造を作っていきましょう

 

Backbone.CollectionとBackbone.Modelでスキーマを作る

前回のBackbone.CollectionとBackbone.Modelの使い方を少しおさらいします。 Modelは構造そのもので、いわばテーブルのレコード単位の情報を管理するものであると話をしました。Collectionは、そのModelを1つ以上持つもので、テーブル自体であるということでしたね。

今回の場合は、Collection自体が、エディタの1コンテンツを管理するものであり、段落ごとに、Modelが生成されて、Collectionの中で管理されるのが自然ですね。

必要であれば、ModelがSaveされた時点で、サーバーと同期するなどの仕組みは、Backbone.Syncを利用して、作りあげることができます。

1.の部分にModel用とCollection用のJSを読み込みます。

1
<script src="js/app.js"></script>

記述が終わったら、早速中身を書いていきます。

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
var app = app || {};
app.model = app.model || {};
 
app.model.paragraph = Backbone.Model.extend({
 
    defaults: {
        id: null,
        type: 'text',
        value: null
    },
 
    validate: function(attrs, options){
 
        //paragraphの型を指定
        switch(attrs.type){
            case 'text':
                break;
 
            default:
                return '指定されていない型です';
                break;
        }
 
        //文字数の制限を加える
        if(attrs.length >= 100){
            return '段落の文字数は100文字以下である必要があります';
        }
    }
 
});

今回は初めて、validateという、メソッドを追加してみました。これで、modelにaddする時に値を検証して追加するなどの操作をBackbone管理下で行うことができる様になります。

さらに、コレクションも併せて書いていきます。

1
2
3
4
5
6
7
var app = app || {};
app.collection = app.collection || {};
 
app.collection.paragraph = Backbone.Collection.extend({
    //4.
    model: app.model.paragraph
});

ここまでは、前回のワークでもやりましたので、スムーズに定義ができるのではないかと思います。

 

fetchを利用して、サーバーから直接値を取得する

fetchメソッドは、Collection、Model両方共に用意されており、モデル単体、コレクションそれぞれへのデータ投入をサーバーから直接行うことができます。

では、早速書いていきましょう。 2.の部分に引き続き、下記のプログラムを記述していきます。

1
2
3
4
5
6
7
8
9
10
11
<script type="text/javascript">
$(document).ready(function(){
    var m = new app.collection.paragraph;
    m.fetch({
        reset: true
        //3.
    });
 
    return;
});
</script>

DOMが準備された後に、paragraphコレクション(app.collection.paragraph)を起動し、次に、fetch()オブジェクトでサーバーからデータを取得し、collectionにデータを投入しています。

読み込むJSONデータを用意します。

data.json(index.htmlと同じディレクトリ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
    "success": true,
    "result": [
        {
            “id”: 1,
            "text": "1つめの段落です",
            "type": "text"
        },
        {
            "id": 2,
            "text": "2つめの段落です",
            "type": "text"
        }
    ]
}

最後に読み込むJSONのURLとパースの仕方を書いていきます。
リファレンスはわかりにくいので、ここで説明をしていきます。

Backbone.collection.urlについて

読み込み先のURLを書いていくことができます。

2つの書き方がありますが、まずは簡単な方から

1
2
3
4
5
app.collection.paragraph = Backbone.Collection.extend({
    //4.
    url: 'data.json',
    model: app.model.paragraph
});

今回はこの書き方でデータを読み込むことができます。

URLをグローバル変数等に格納して、設定を読み込んで使うような場合には、

1
2
3
4
5
6
7
app.collection.paragraph = Backbone.Collection.extend({
    //4.
    url: function(){
        return app.config.apiurl + '/data.json';
    },
    model: app.model.paragraph
});

と言うような形で、URLに関数を指定して、取得したいURLが返却されるように書くこともできます。

Backbone.collection.parseについて

実際にデータを読み込む配列があるプロパティを指定するために、parseを使います。

第1引数として、サーバーからのレスポンスがオブジェクト化されたものが渡ってきますので、実際に読み込む配列を戻り値として返すようにすれば、collection.modelsに自動的に投入してくれるようになっています。

1
2
3
parse: function(res){
    return res.result;
},

簡単ですね。 これで、データの初期化が完了しました。

引き続き、collectionに任意のデータを投入してみましょう。 これは、後にエディターとして、画面操作した値を受け付けて、このプログラムを通して、サーバーに同期されることになります。

3の部分に、下記のプログラムを記述していきます。

1
2
3
4
var res = m.create({
    text: "新しい段落を追加しました",
    type: "text"
});

エントリーポイント部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script type="text/javascript">
$(document).ready(function(){
    var m = new app.collection.paragraph;
    m.fetch({
        reset: true
        //3.
        success: function(){
            var res = m.create({
                text: "新しい段落を追加しました",
                type: "text"
            });
            return;
        }
    });
 
    return;
});
</script>

fetch関数には、オプションとして、reset, removeなどの追加・削除を制御するオプションと、success, complete, errorなどjQueryが持っているコールバックオプションを指定することが可能です。

基本的にjQueryと同じ感覚で利用ができるので、大変覚えやすいですね。

上の例では、m.fetchで、サーバーからJSONを取得して、2件のデータを挿入した後に、successが実行されて、新しい段落が追加されるという流れになります。

データ構造の完成

ここまでで、データ構造が完成しました。 これに画面をつけていくと、いよいよエディターとしてデータ操作できることが想像できますね。

ここまで、合計でまだ、30行も書いていないわけですが、これだけでも立派なアプリケーションの制御が体系的にできるところが、Backbone.jsを利用するメリットではないかと思います。

 

2. 画面を作っていきましょう

今度はHTML部分を書いていきます。
前回から、Bootstrapを利用していますので、それを前提に画面を作っていきましょう。

1
2
3
4
5
6
7
<!-- 2. HTMLを書いていきます -->
<div id="editor" class="container">
    <div id="paragraph" style="min-height: 100px; background-color: #EEE;"></div>
    <div class="text-center" style="margin-top: 10px;">
        <div id="add-btn" class="btn btn-success">段落を追加</div>
    </div>
</div>

少しデザインを整えるためのCSSも書いておきました。

 

段落追加時のイベント発火を仕掛けよう

では、Backbone.Viewを使って、ボタンの追加がクリックされたときに、イベントが発火されるように仕掛けていきましょう。

app.js 上部に、名前空間の宣言をしておきます。

1
app.view = app.view || {};

さらに、Backbone.Viewを拡張したapp.view.editorを作っていきます。 app.jsの下部で、app.collection.paragraphの直後に書いてきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
app.view.editor = Backbone.View.extend({
 
    el: '#editor',
 
    events: {
        'click #add-btn': 'onAdd'
    }, 
 
 
    onAdd: function(){
        console.debug('onAdd');
        return;
    }  
 
});

エントリーポイントに下記を追加して、viewを起動します。

1
var v = new app.view.editor();

ここで、段落を追加ボタンをクリックすると、onAddとコンソールに表示されれば成功です。

実際に段落が表示される仕組みについては、前回・前々回のワークを応用すればできるので、ワークでやることにして、先ほどのfetchと連動して、初期の段落表示をする仕掛けをみていきましょう。

 

読み込んだ情報を表示してみよう

先ほどcollectionを作成したところで、fetchした後に、コールバックを実行できるという話をしましたが、そこで、Viewと連動して画面とmodelが同期、さらに通信がはしり、サーバー側に登録されるようにしていきます。

fetchのコールバックとして下記のように書いていきましょう。

1
2
3
4
5
6
7
8
9
10
11
12
m.fetch({
    reset: true,
    success: function(m){
        _.each(m.models, function(v){
            editor.$('#paragraph').append(new app.view.paragraph({
                model: v
            }).render().$el);
        }, this);
 
        return;
    }
});

先ほど記述したsuccessのところをcreateを削って記述していきます。

successの第一引数で、collectionが渡ってきますので、m.models(コレクション一覧)をループさせて、app.view.paragraphを起動して、描画します。

描画したら、editorの子要素である#paragraphに追加するようにします。

これで、テンプレートを通してテキストエリアが描画されて、下記のような画面が表示されます。

初期読み込み状態

この書き方に関してですが、app.view.editor側にメソッドを作成して、View配下のメソッドとして実行することで、処理の隠蔽をすることもできますが、ViewとCollectionの関係を疎にするために今回はこのような書き方をしています。

状況に応じて、どこに書くかということを意識してみるといいと思います。

 

テキストの内容を変更したら自動的にサーバーと同期してみよう

最後に、描画されたテキストボックスの内容を変更する度にサーバーと同期するようにしてみましょう。
サーバーとの同期は、modelのsaveメソッドで行います。

app.model.paragraph

1
2
3
4
5
initialize: function(){
    this.on('change', function(){
        this.save();
    }, this);
},

initializeメソッドを追加して、changeイベントをリッスンするようにします。 モデルの内容が変更された時点で、saveメソッドが実行され、JSONを含んだリクエストがサーバー側に投げられれば成功です。

リクエスト先は path/editor/data.json/1

と言うような形になっているかと思います。最後の1は、そのレコードのidプロパティが利用されたことが分かります。この様にURLは自動的に決定されますので、サーバー側がこの通り設計されてれば、なにも考えなくても、自動的に同期する仕掛けが完成できます。

リクエストをカスタマイズする場合には、先ほど利用したModel.urlやModel.syncをカスタマイズしていきます。

いかがでしたでしょうか?

今回は、Backbone.Collection、Backbone.ModelとBackbone.Syncを中心にViewとの組み合わせで簡単なアプリが完成しました。

全2回では、HTMLが書かれている前提でのBackbone.jsの導入の仕方を説明してきました。第3回では、ウェブアプリを作るための基礎と、Backbone.jsが持っている通信周りの機能を一通り説明しました。

Backbone.jsはまだまだ若いフレームワークですので、これから仕様も変化していくと思いますが、基本的なクライアントMVCモデルの考え方や、今回のView、Collection、Modelの組み合わせによるデータとビューの疎結合を実現した形のでの実装は、どのフレームワークでも考え方としては共通しています。

JavaScriptでの画面作りに関しては、今後ますます重要になってきますし、さらに大規模なシステムで使われていきます。今回の取り上げたようなフレームワークを通じて、シンプルにアプリケーションを書いていくことを心がけてもらえればと思います。

 


[第1回]Backbone.jsでクライアントMVCに挑戦!

いまや、Webサービスのクライアント側を司る言語として揺るぎない地位を獲得したJavaScript。jQueryに代表されるJavaScriptライブラリはプラグインも充実していて、見た目にリッチなアプリケーションやウェブページが簡単に制作できる様になってきました。ただ、Webアプリケーションの構造をしっかりと書いていくには、かなりのスキルが必要です。…

[第2回]スマートにデータをHTMLに。Backboneで簡単アプリデータ管理

まずは、Backbone.ModelとBackbone.Collectionの説明をしていきます。 2つとも、Webアプリ上でデータを扱う機能です。関係性としては、Backbone.Modelの集合体が、Backbone.Collectionだと考えてください。Modelでは、基本的には、1レコード(1オブジェクトという言い方がいいかもしれません)のデータを管理します。…

type IT Academyのトップページへ

簡単!エンジニア体験ゲーム
簡単!エンジニア体験ゲーム

その他の条件で探す

typeでは職種や勤務地、仕事探しで譲れないこだわりの条件など、様々な切り口から自分の働き方に合った求人を探すことができます。気になるキーワードやテーマから転職・求人情報をチェックしてください。

転職活動を進める

あなたの転職活動をサポートする、typeの各種サービスをご案内します。

  • スカウト

    スカウト

    匿名だから安心!あなたに興味を持った企業の採用担当から直接メールが届くサービスです。

  • オファーDM

    オファーDM

    あなたが登録した情報と近い内容の募集条件の企業から、メールが届くサービスです。

  • 検討中リスト

    検討中リスト

    興味を持った求人を保存しておくことができ、気になる求人を一覧にて比較検討できます。