tohokuaikiのチラシの裏

技術的ネタとか。

VueのAjax通信の前後で共通のLoaderを入れる

ajaxの前後でLoading画面を出すやつです。

なんか、スゲー探してようやく見つけた…。
最初、Vuexを使わないようにしてたら全然できなくて、Vuexを使ったら30分でできて時間返してって感じだった。Vuexは恐れずに使え。

main.js

import Vue from 'vue';
import VueRouter from 'vue-router';
import Vuex from 'vuex'
import router from 'router';

window.Vue = require('vue');
Vue.use(VueRouter);
Vue.use(Vuex)

// loading状態を管理するためのVuex
const store = new Vuex.Store({
    state: {
        loadingCount: 0
    },
    mutations: {
        startLoader: (state) => {
            state.loadingCount++;
        },
        endLoader: (state) => {
            if (state.loadingCount > 0) {
                state.loadingCount--;
            }
        }
    }
});

// メインVue
const app = new Vue({
    el: '#app',
    router,
    store,
    components: {
        'my-vue-loading': MyVueLoading
    },
    computed: {
        loading() {
            return store.state.loadingCount > 0;
        }
    },
    created: function () {
        axios.interceptors.request.use(async config => {
            store.commit('startLoader');
            return config;
        });
        axios.interceptors.response.use(response => {
            store.commit('endLoader');
            return response;
        }, error => {
            store.commit('endLoader');
            throw error;
      });
    }
});

このcreatedに挟むのが分からんかった…

Loaderを使いたい時

router-viewを含むHTMLで

<div v-show="loading">Loading...</div>
<div v-show="!loading"><router-view></router-view></div>

な感じで。

Vuejsのコンポーネントを使ってsyncとかデータのやり取り

登録と編集って同じフォームを使いまわすので、Componentにしてあれこれできないかなと思ってたら、意外とデータを親子のVueでやり取りするのが面倒そうで…

を参考に。

上記の記事の問題点

なんで、子コンポーネントでまたpropsを定義し直さないと…というのが死ぬほど嫌だったので、考えた。ES2015を全然知らないのに考えた。

とりあえずこんなかんじ。

親側 Create.vue

<template>
    <div>
        <order-form
          v-bind.sync="order"
          ></order-form>
    </div>
</template>
<script>
import OrderFrom "@/components/OrderForm";
import defaultOrder from "@/helper/order";
export default {
    components: {
        'order-form': OrderForm,
    },
    data() {
        return {
            order: (new defaultOrder).props(),
        }
    }
}
</script>

子側 OrderForm.vue

<template>
    <div>
            <div class="form-group">
                <label>お名前</label>
                <input type="text" :value="name"
                       @input="$emit('update:name', $event.target.value)"
                       :class="{'form-control':true, 'error': false}">
            </div>
            <div class="form-group">
                <label>メールアドレス</label>
                <input type="text" :value="email"
                       @input="$emit('update:email', $event.target.value)"
                       :class="{'form-control':true, 'error':false}">
            </div>
            <div class="form-group">
                <label>電話番号</label>
                <input type="text" :value="tel"
                       @input="$emit('update:tel', $event.target.value)"
                       :class="{'form-control':true, 'error':false}">
            </div>
    </div>
</template>
<script>
import defaultOrder from "@/helper/order";
export default {
    props: (new defaultOrder()).defs,
    data() {
    },
};
</script>

Orderの定義ファイル

helper/order.js

export default class defaultOrder {
    constructor() {
        this.defs = {
          email: {
            type: String,
            default: ""
          },
          name: {
            type: String,
            default: ""
          },
          tel: {
            type: String,
            default: ""
          },
        };
    }
    
    props(){
        let prop = {};
        for (let p in this.defs){
            prop[p] = this.defs[p].default;
        }
        return prop;
    }
};

みたいな。

でもダメだね。

これ、Modelを一括で渡すには良いけど、他のプロパティが渡せない。 ダメだわ。普通に1個づつ渡すしか無いな。

axiosでParallel実行する時のエラーハンドリングがよくわからない

axiosで並列して全部終わったら…というやつ。

axios.all([
    axios.get('/api/user/'),
    axios.get('/api/image')
]).then(([res1, res2]) => {
    this.user = res1.data;
    this.image = res2.data;
}).catch(err =>{
    console.log(err);
});

ってやると、2つgetしたうちの最初のエラーしかcatchしてくれない。

個々のエラーを見たかったら

axios.all([
    axios.get('/api/user/').catch(err => { /*  error処理 */ }),
    axios.get('/api/image').catch(err => { /*  error処理 */ })
]).then(([res1, res2]) => {
    this.user = res1.data;
    this.image = res2.data;
});

という感じに個々のaxiosにcatchを付ける。でも、これだとthenにいっちゃう。

それが嫌なら

axios.all([
    axios.get('/api/user/').catch(err => { /*  error処理 */ throw(err); }),
    axios.get('/api/image').catch(err => { /*  error処理 */  throw(err);})
]).then(([res1, res2]) => {
    this.user = res1.data;
    this.image = res2.data;
}).catch(err =>{
    console.log(err);
});

ってやると、catchできるけどそれって最初のとあんま変わんないっていう…。

LaravelでEthnaのフォームフィルターみたいなやつ

あれな。Inputの時にPOST値を自動的に全角から半角にしてくれたりするやつ。あれ、すげー便利なんだけど、Laravel本体にはないらしい。

waavi/sanitizerをインストール

composerにあるので https://packagist.org/packages/waavi/sanitizer

$ composer require waavi/sanitizer

でインストール

Filterクラスを作成

App/Http/Requests/Filters ディレクションを作成してクラスを登録。applyメソッドを作る。

<?php
namespace App\Http\Requests\Filters;

use Waavi\Sanitizer\Contracts\Filter;

/**
 * Class Zentohan
 */
class Zentohan implements Filter
{
    /**
     * @param mixed $value
     * @param array $options
     * @return mixed|string|string[]
     */
    public function apply($value, $options = [])
    {
        return mb_convert_kana($value, 'KVa', 'UTF-8');
    }
}

Requestクラスで使う

Validator前にFilterしてくれる。

<?php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Waavi\Sanitizer\Laravel\SanitizesInput;
use App\Http\Requests\Filters\Zentohan;

class UserRequest extends FormRequest
{
    use SanitizesInput;

    /**
     * @return array filters before sanitized
     */
    public function filters()
    {
        return [
            'name'  => 'zentohan',
            'email' => ['zentohan', 'lowercase'],
        ];
    }

    public function customFilters()
    {
        return [
            'zentohan' => Zentohan::class
            ];
    }
}

うーん、便利。

filtersは配列で複数、あるいは|でつないで複数いける。

Vuejsの共通関数の設置

utility.jsとかにして全体で使いたい場合ありますよね。methodsに突っ込みます。

mixin用のファイル作成

my_vue_mixin.js

export default{
  methods: {
    aaa(){
        console.log('123');
    }
  }
}

Vueに仕込む

app.js

import Vue from 'vue';
import myVueMixin from './my_vue_mixin';
window.Vue = require('vue');
Vue.mixin(myVueMixin);

const app = new Vue({
    el: '#app',
});

この2行ね

import myVueMixin from './my_vue_mixin';
Vue.mixin(myVueMixin);

Vueファイルで使う

普通にmethodsのメンバーとして使える。 Foo.vueで

    methods: {
        confirm(){
            this.aaa();
        }

とか。