豆腐とコンソメ

豆腐とコンソメ

もろもろのプログラム勉強記録

Vue.jsでつくるオブジェクト指向フォーム(1)

Laravelで掲示板を作成している途中だけれども、その素材をもとにVue.jsでオブジェクト指向フォームを作ってみるよ。

www.tohuandkonsome.site

オブジェクト指向フォームってなによ?と思われたあなた。自分もよくわからないんだ。

でも、おお!ってなったので振り返りながら書いていこうと思います。

教材は毎度お世話になっております、Laracastになります。

laracasts.com



準備 

自分は、Laravelをサーバー側のアプリケーションとして用意しています。
そして、以下のような、「タイトル」と「本文」をサーバー側にpostするhtmlを用意しています。 (Laravelなので正確にはbladeだよ!)

create_ajax.blade.php(抜粋)

<!-- ここには書いてないけれども、後述のapp.jsを読み込んでいるよ!-->
<div class="container justfy-center">
    <form class="simple-form" action="/thread" method="post">
        {{csrf_field()}}
       <div class="simple-form__group">
            <label class="simple-form__title" for="title">タイトル</label> 
            <input class="simple-form__input" type="text" name="title">
       </div>
       <div class="simple-form__group">
            <label class="simple-form__title" for="title">本文</label> 
            <input class="simple-form__input" type="text" name="body">
       </div>
       <div class="simple-form__footer">
            <button class="simple-form__submit-btn">Post</button>
       </div>
    </form>
</div>

このhtmlをブラウザでみてみると、こんな感じの画面になります。

f:id:konoemario:20171212213323p:plain
今回利用するフォームのイメージ

続いては、肝心のVue.jsになります。
さきほどのhtml内の.simple-formをエレメントとしてVueインスタンスを作成しています。

app.js

const app = new Vue({
    el: '.simple-form'
});

また、Laravelなのでrequire('vue')してwebpackでコンパイルしていたりしますが、そのへんは割愛しているよ。


とりあえずAjaxでPostするようにしていくよ 

なにはともあれ?とりあえずAjaxでサーバにpostしてみるよ!

まずは、フォームでボタンを押下したときに発生するデフォルトのイベントsubmitをキャンセルして、Vueインスタンスのメソッドを呼ぶ様にしよう。
create_ajax.blade.php(抜粋)

<div class="container justfy-center">
    <!--Submitのデフォルトイベントをキャンセルして、VueインスタンスのonSubmitメソッドを呼ぶ-->
    <form class="simple-form" action="/thread" method="post" v-on:submit.prevent="onSubmit">

Vueインスタンス側には、試しにメソッドが呼べているかどうか確認するために、console.log()で何かしらだしてみる。

app.js

const app = new Vue({
    el: '.simple-form',
    methods:{
        onSubmit:function(){
            console.log("submit!!!");
        }
    }
});

そうすると、こんな感じでボタンを押すたびに、ブラウザのコンソールに文字列が出力されるね!

f:id:konoemario:20171212214222p:plain
submitしたとき

これで、submitのイベントをVueインスタンスのメソッドonSubmitに置き換えることができました。
onSubmitをしたらフォームの入力データを取ってきて、リクエストデータをつくってサーバーにpostするんだな!ってなんとなく見えてきました。


フォームのデータをとってくる:v-model

では、さっそくフォームの入力データをとってきます。

入力データをjavascriptでとってくる場合、formにname属性をつけて、それをdocumentオブジェクトから見て〜みたいなことをしますよね!(今日知った。)

参考にさせていただいたサイト
ピュアな JavaScript でフォーム(form)系要素の値を取得, 設定する方法一覧 | phiary

Vue.jsの場合はv-modelを使って、データバインディングをしていきます。


v-modelを使う。

では、さっそくv-modelをつかってフォームバインディングをしていくよ! html側はこんな感じにフォームのコントロール系のタグと、Vueインスタンスの変数をバインディングしてあげます。

create_ajax.blade.php

<div class="container justfy-center">
    <!--Submitのデフォルトイベントをキャンセルして、VueインスタンスのonSubmitメソッドを呼ぶ-->
    <form class="simple-form" action="/thread" method="post" v-on:submit.prevent="onSubmit">
        {{csrf_field()}}
       <div class="simple-form__group">
            <label class="simple-form__title" for="title">タイトル</label> 
             <!-- v-modelで、フォームのinput系(select、textareaとかも)の要素とVueインスタンスの変数をバインディングする -->
            <input class="simple-form__input" type="text" name="title" v-model="title">
       </div>
       <div class="simple-form__group">
            <label class="simple-form__title" for="title" >本文</label> 
            <input class="simple-form__input" type="text" name="body" v-model="body">
       </div>
       <div class="simple-form__footer">
            <button class="simple-form__submit-btn">Post</button>
       </div>
    </form>
</div>

一方のapp.jsには、バインドするデータをdata配下に定義してあげます。
また、中身を確認するために、さきほどのonSubmitメソッド内にバインドするデータを表示する処理を追加しておきます。

app.js

const app = new Vue({
    el: '.simple-form',
    data:{
        title:'',
        body:'',
    },
    methods:{
        //Formのsubmitイベントが発生したとき
        onSubmit:function(){
            console.log('title='+ this.title);
            console.log('body='+ this.body);
        }
    }
});

この状態で、ブラウザを開いて、フォームに値を入力してボタンを押してみましょう。

こんな感じで、コンソールに入力された内容が出力されると思うんだ!

f:id:konoemario:20171212220058p:plain
フォームの入力内容がVueインスタンスのdataに反映されている

イメージにするとこんな感じだね!

f:id:konoemario:20171211235836p:plain
v-modelのイメージ(1)


逆にVueインスタンスのdataの値を変更したら、その内容がブラウザに表示されるのかしら。
試しにやってみよう。

submitを行う以外にもうひとつ確認用のボタンを追加すると、

create_ajax.blade.php(抜粋)

       <div class="simple-form__footer">
            <!--試しに、titleの値を変更するmethod、changeTitleをボタンクリックのイベントでよんでみる -->
            <button button="button" class="simple-form__submit-btn" v-on:click="changeTitle">change!</button>
            <button class="simple-form__submit-btn">Post</button>
       </div>

ブラウザはこんな感じになって、

f:id:konoemario:20171212220952p:plain
確認用の画面

app.jsに値をかえるメソッドchangeTitleを書いてあげます。

app.js

    el: '.simple-form',
    data:{
        title:'',
        body:'',
    },
    methods:{
        //Formのsubmitイベントが発生したとき
        onSubmit:function(){
            console.log('title='+ this.title);
            console.log('body='+ this.body);
        },
        //titleの値を変えてみる(あとで消す)
        changeTitle:function(){
            this.title="change!"
        }
    }
});

これで、ブラウザで確認用のボタンを押すと、

f:id:konoemario:20171212221130p:plain
確認用の画面の結果

こんな感じで、ブラウザ側の表示もちゃんとかわることがわかるね!

こちらもくどいですが、イメージにするとこんな感じですかね。

f:id:konoemario:20171212212359p:plain
v-modelのイメージ(2)


axiosを使ってサーバにpostする

フォームの入力データがバインディグで簡単に取得できることがわかったので、さっそくデータをサーバーにpostしてみよう。

純粋なjavascriptだとXMLHttpRequestオブジェクトを使って〜みたいなことをするかと思います。

ためになる記事
JavascriptのAjaxについての基本まとめ - Qiita

が、ここではより簡単にAjaxを利用できるaxiosを使っていきます。

axiosの導入自体は割愛しちゃうんだよ!
とりあえずだったらCDNを使っちゃえばいいからね!Laravelだと標準で用意されているよ!

こんな感じで、axios.postで送信先のurlと送信するデータを書いていくよ。
ここでは固定の文字列hellopinkyを送ってるね。

app.js

    el: '.simple-form',
    data:{
        title:'',
        body:'',
    },
    methods:{
        //Formのsubmitイベントが発生したとき
        onSubmit:function(){
            axios.post('/thread',{
                title:'hello!',
                body:'pinky!'
            })
        },
    }
});

データバインディングしたデータを送るのであれば、`this.title'みたいに定義しちゃえばいいね!

app.js

    el: '.simple-form',
    data:{
        title:'',
        body:'',
    },
    methods:{
        //Formのsubmitイベントが発生したとき
        onSubmit:function(){
            axios.post('/thread',{
                title: this.title,
                body: this.body
            })
        },
    }
});

ええい、定義してあるものは全部送るんじゃ!というのであればthis.$dataってしとくと、dataで定義しているデータを全部送っちゃうよ。
恐ろしい子!

app.js

    el: '.simple-form',
    data:{
        title:'',
        body:'',
    },
    methods:{
        //Formのsubmitイベントが発生したとき
        onSubmit:function(){
            axios.post('/thread', this.$data);
        },
    }
});


ちょっと横道に入る

データを送るっていってるけど、何を送ってるんだよ!と思った方がもしかしたらいるかもしれません。
Chromeをデペロッパーツールで開いて、Networkをみるとリクエストだったりレスポンスの中身が見れるから、ぜひ見てみよう!

簡単に抜粋すると、サーバーにはこんな感じでリクエストを送っているよ。

HTTPリクエストの中身

//HTTPリクエストヘッダーがあって
POST /thread HTTP/1.1
Host: homestead.app
Connection: keep-alive
Content-Length: 34
Pragma: no-cache
Cache-Control: no-cache
//(省略)

//ヘッダーに一行あけて、リクエストbodyの内容がセットされているね。
{"title":"hello!","body":"pinky!"}

本当に参考程度だったね!


レスポンスを受け取ろう

今、axiosでpostするぜ!って処理になってるんだけれども、リクエストを投げっぱなしで結果がわからないね、

f:id:konoemario:20171213194952p:plain
レスポンスを処理しよう

なので、こんな感じにレスポンスを受け取ってあげよう。

app.js(抜粋)

        onSubmit:function(){
            axios.post('/thread', this.$data)
            //HTTPリクエストが成功したとき
            .then(response => console.log(response.data))
            //HTTPリクエエストが失敗した時
            .catch(error => console.log(error.response));
        },

ちなみにHTTPリクエストが失敗って、何をもって失敗なんだろうかという点だけれども、公式ドキュメントをさらっと読むと、デフォルトだとHTTPステータスコートが200番台のときは成功みたい(あってるかな?)

やばい!最後がなんか投げやりになってる、ちっとも話が進まない!というところで次回に回します。