Vue.js+firebaseでJS MVVMフレームワークに入門する(1/2)



##はじめに
「JSフレームワークはサーバサイドも用意しないとアプリっぽいものが作れない」
という思い込みがあり、なかなかモチベーションが上がらずangularjsもチュートリアルを一通り触ったきりだったのですが、
firebase+Vue.jsで試したところ、簡単にWEBアプリが作れて、かなりモチベーションが上がりました。
他の人にも同じ効果があるかも。と思いチュートリアルを書いてみました。

## 目標
chatアプリをつくります。

![chats.gif](https://qiita-image-store.s3.amazonaws.com/0/3844/4ef965fc-688f-1354-7e04-7f1daf0a9a85.gif "chats.gif")


##まずはVue.jsへの入門

###簡単なコード
本家のコードを1ファイルにしただけなんですが・・・。

```html:todo.html
<html id="demo" >
  <head>
    <style type="text/css">
        body {
            font-family: Helvetica Neue, Arial, sans-serif;
        }

        li.done {
            text-decoration: line-through;
        }
    </style>
    <script src="http://Vuejs.org/js/Vue.min.js" charset="utf-8"></script>
    <title v-text="title">now loading.....</title>
  </head>
  <body>
    <h1>{{title | uppercase}}</h1>
    <ul>
        <li
            v-repeat="todos" 
            v-on="click: done = !done"
            class="{{done ? 'done' : ''}}">
            {{content}}
        </li>
    </ul>
    <script charset="utf-8">
        var demo = new Vue({
          el: '#demo', //Viewに対応するDOM要素の設定
          data: {	     //dataにModelをもたせます
            title: 'todos',
            todos: [
                {
                    done: true,
                    content: 'Learn JavaScript'
                },
                {
                    done: false,
                    content: 'Learn Vue.js'
                }
            ]
          }
        });
    </script>
  </body>
</html>
```

```new Vue()```が一番見るべきところだと思います。

コンストラクタの引数にoptionを指定してますが、
```el``` にはViewに対応するDOM要素を設定してます。(今回はhtmlタグについている```#demo```)

```data``` に設定しているのがMVVMのModelに対応するものになります。consoleからこのModelをいじれるのですが、Viewの表示内容が自動で追従していくのは面白いですよ。

htmlコードについてですが、
headタグに付いている ```v-text="title"```って箇所はModelのtitleに対応する値が自動で反映されます。書き方として以下のようにも変更できます。

```html:before
<title v-text="title">now loading.....</title>
```

```html:after
<title>{{title}}</title>
```

h1タグはafter側の書き方になっていますね。ただし```uppercase``` filterで大文字に変更してます。

この中で一番わかりづらいのは```v-repeat```じゃないでしょうか？
これは配列の場合に使用します。todosの各要素分繰り返すのでrepeatってことですね。
なので、その後に続く```done ``` や```content ```はtodosの各要素のフィールドを意味します。
解りづらい場合は以下のようにも記述することができます。

```html:before
        <li
            v-repeat="todos" 
            v-on="click: done = !done"
            class="{{done ? 'done' : ''}}">
            {{content}}
        </li>
```

```html:after
        <li
            v-repeat="todo:todos" 
            v-on="click: todo.done = !todo.done"
            class="{{todo.done ? 'done' : ''}}">
            {{todo.content}}
        </li>
```

after側の書き方のほうが解りやすいかもしれないですね。

最後に```v-on```ですが、
```v-on:click <<処理>> ``` という感じで、書くと該当タグクリック時に処理が実行されるというものです。

###ModelをいじるとViewが合わせて変化するのを目撃する。

先程も記載したことですが、ぜひ試してみてほしいことが一つあります。
Chromeなどからconsoleに入って頂いてModelをいじくってみて欲しいんです。
対応してViewが変化するので面白いです。一応gif載せてみました。

![todo.gif](https://qiita-image-store.s3.amazonaws.com/0/3844/fb9868d7-1a74-8d1c-f2ab-afb87aa63989.gif "todo.gif")







###Chatっぽいもの

ということでChatっぽいものを作ってみました。

```html:chat.html

<html id="app">
  <head>
    <title v-text="title"></title>
    <script src="http://Vuejs.org/js/Vue.min.js" charset="utf-8"></script>
  </head>
  <body>
    <div v-repeat="message:messages">
        {{$index}}
        {{message.from}}
        <input v-model="message.body" value="{{message.body}}" v-on="keyup:changeMessage(message)">
        <button v-on="click:deleteMessage($index)" >x</button>
        <br>
    </div>
    <form id="form" v-on="submit:addMessage">
        from:<br>
        <input v-model="newMessage.from">
        <br>
        message:<br>
        <input v-model="newMessage.body">
        <input type="submit" value="Add Message">
    </form>
  </body>
  <script charset="utf-8">
    var app = new Vue({
        el: '#app',
        data: {
            title:'Hello myChat',
            messages: [],
            newMessage: {
                from: '',
                body: ''
            },
        },
        methods: {
            addMessage: function(e) {
                e.preventDefault(); //submitをキャンセル
                var message = {
                  from:this.newMessage.from,
                  body:this.newMessage.body
                  };
                this.messages.push(message);
                this.newMessage.body = '';
            },
            changeMessage: function(message) {
              console.log("changeMessage:" + message.body);
            },
            deleteMessage: function(index) {
              console.log("deleteMessage:" + this.messages[index].body);
              this.messages.$remove(index);
            }

        }
    })

  </script>
</html>

```

今回は前回から追加されたものがあります。
```var app = new Vue()```のoptionに```methods```が追加されています。
ここで```v-on```にて呼び出せるようにメソッド定義しておきます。

あとは```v-model```がinputタグに追加してありますが、これを設定することによって
input要素とmodelの該当箇所が双方向バインディングされます。
双方向バインディングとは、ModelとViewの値の対応関係を設定することによって
どちらを変えても合わせて片側が追従変化してくれる機能です。
(Modelを変えればViewが変わり、Viewを変えればModelも変わるってことです。)


それと削除については、```this.messages.$remove(index)```という風に```$remove```メソッドで処理を行わないと削除してもModelとViewが追従してくれないようです。

で、この段階ではサーバサイドがないので、Chatとして不完全です。
ということで、サーバサイドを実装しましょう。



## firebaseでサーバサイド

クライアントしかいじっていないのにサーバサイドもいつの間にか動いている。という感じがすごいです。

jsonをまるごと取り込んでくれるので、テーブル定義も不要で使えてしまいます。

さらにjs用のライブラリが提供されているので、
通信周りも隠蔽されWEBAPI設計とか考える必要がなくなります。使った感覚は永続化されたjsonオブジェクトがjsコード内に出現した感じです。




###アカウント作成
まずは、[ここでアカウントを作ります](https://www.firebase.com/signup/)

![Signup_-_Firebase.png](https://qiita-image-store.s3.amazonaws.com/0/3844/ad5e77cf-f606-4cdd-8ddb-4178d3030b50.png "Signup_-_Firebase.png")

アカウントを作成するとダッシュボードが表示されますので、APP NAMEとURLを決めてアプリを作りましょう。
(すでに初期状態でMY FIRST APPが作られてるのでそれを使ってもいいかもです。)

![Account_-_Firebase.png](https://qiita-image-store.s3.amazonaws.com/0/3844/f3aa5af2-36a3-58b7-dd77-4c1b6974b5c9.png "Account_-_Firebase.png")


###実装


URLの部分に記載した識別子を下記サンプルの該当箇所に埋め込んでいただくと早速動き出します。


```html:chat_1.html
<html id="app">
  <head>
    <title v-text="title"></title>
    <!-- //追加箇所 -->
    <script src="https://cdn.firebase.com/js/client/1.0.2/firebase.js" charset="utf-8"></script>

    <script src="http://Vuejs.org/js/Vue.min.js" charset="utf-8"></script>
  </head>
  <body>
    <div v-repeat="message:messages">
        {{$index}}
        <input v-model="message.body" value="{{message.body}}" v-on="keyup:changeMessage(message)">
        from:{{message.from}}
        <button v-on="click:deleteMessage($index)" >x</button>
        <br>
    </div>
    <form id="form" v-on="submit:addMessage">
        from:<br>
        <input v-model="newMessage.from">
        <br>
        message:<br>
        <input v-model="newMessage.body">
        <input type="submit" value="Add Message">
    </form>
  </body>
  <script charset="utf-8">

    //追加箇所
    var baseURL = 'https://<<Create New Appで作成したURL>>.firebaseio.com/',
    Messages   = new Firebase(baseURL + 'messages');
    Messages.on('child_added', function (snapshot) {
        var message = snapshot.val();
        message.id = snapshot.name();
        app.messages.push(message);
    })


    var app = new Vue({
        el: '#app',
        data: {
            title:'Hello myChat',
            messages: [],
            newMessage: {
                from: '',
                body: ''
            },
        },
        methods: {
            addMessage: function(e) {
                e.preventDefault(); //submitをキャンセル

                //変更箇所
                // var message = {
                //   from:this.newMessage.from,
                //   body:this.newMessage.body
                //   };
                // this.messages.push(message);
                Messages.push(this.newMessage)
                this.newMessage.body = ''
            },
            changeMessage: function(message) {
              console.log("changeMessage:" + message.body);
            },
            deleteMessage: function(index) {
              console.log("deleteMessage:" + this.messages[index].body);
              this.messages.$remove(index);
            }

        }
    })

  </script>
</html>
```
な、なんと20行以下の変更でサーバサイド側の対応完了です。まじかー。


firebase用にModelがもう一個追加されたイメージですね。(```Messages```)

```Messages.push``` でサーバサイドのMessagesModelにObjectが追加され、
MessagesModelの要素追加時のコールバックとして以下の関数が実行されるという挙動です。

```javascript:要素追加時に起動
    Messages.on('child_added', function (snapshot) {
        var message = snapshot.val();
        message.id = snapshot.name();
        app.messages.push(message);
    })
```


とは言え、追加には対応しましたが、メッセージの削除と変更は追従してくれません・・・・。
(そもそもchatにそんなのはいらんだろうという話ですが・・・。)

メッセージ内容の削除と変更への対応は[2/2](http://qiita.com/abe00makoto/items/edcaa7cde3c1d431c25d)に書いてみます。