gologiusの巣

プログラミングなどの技術メモです。誰かの役に立てるとうれしいです。

Vueっぽく階層式プルダウンを作ったが、もっとスマートに書きたいという話

※Vue始めて3か月くらいです。

とりあえず作ったものを下記リンクに置いておく。

Vueでプルダウンテスト

ソースは下記に置いておく。JSなら動くのでgithubio便利。

gologius.github.io/test/PULLDOWN_SAMPLE at master · gologius/gologius.github.io · GitHub

リンク先を見るのが面倒な人向けにGIFも貼っておく

f:id:gologius:20200606152036g:plain
プルダウンのGIF

なぜ作ったのか

  • 3階層、4階層以降に対応したサンプルがなかった(気がする)
  • 超適当に調べたが、微妙にやりたいこととずれたものだった(気がする)
  • こんなんサイトちゃんと見なくても自力実装できるわwwwww(所感)

感想

1

HTMLはそこそこ綺麗かなと思いました

<div id="pulldown">
    <div v-for="(choices,layer) in selectable_items">

        <div v-bind:id="'layer' + layer" v-show="choices.length >= 1">    
            <h2>第{{layer+1}}階層</h2>
            <select v-bind:id="'pd-'+ layer" v-model="selected_stack[layer]">
                <option v-for="item in choices" v-bind:value="item.id">
                    {{item.name}}
                </option>
            </select>
        </div>

    </div>
</div>

<script src="pulldown.js"></script>

2

現状の選択に紐づく選択肢をJSでフィルターして渡してるんだけど、 HTML側のフィルターで頑張るべきなのか、JS側で頑張るべきなのか、ライブラリの思想としてどっちがいいのかがよくわからない

//現状のユーザー選択(selected_stack)に対して、表示すべき選択肢を取得
selectable_items: function () {
    
    var results = [];
    var layer = 0;
    for (layer = 0; layer <= this.selected_stack.length; layer++) {
        
        //上位層の選択結果を取得
        before_selected_id  = ''
        if (layer >= 1 ) {
            before_selected_id = this.selected_stack[layer-1];
        }

        //該当層かつ、上位層の選択肢に合致するもののみ抽出
        layer_result = MENU_M.filter(function (value) {
            return value.layer === layer && value.before_id === before_selected_id;
        });

        results.push(layer_result);
    }
    return results;
},

3

JS側が汚い。初期化処理とかキモイ。watch使っているんだけど謎ラッパーかまさないとListの検知ができない

computed: {
    //watch関数をまともに動作させるためのラッパー
    //参考:https://qiita.com/haruyanagi17/items/d74c0b9546719ff88c63
    watch_selected_stack: function () {
        return Object.assign({}, this.selected_stack); // ディープコピーしたものを返す
    },
},
watch: {
    //選択肢が入力された際の動作定義
    watch_selected_stack: function (newval, oldval) {
        
        //変更箇所を検知
        var update_flag = false;
        var layer = 0;
        for(layer=0; layer < this.selected_stack.length; layer++){
            
            //変更箇所より後の選択肢は初期化する
            if(update_flag) {
                this.selected_stack[layer] = '';
            }

            //変更されている箇所があれば、フラグを立てる
            if (newval[layer] !== oldval[layer]) {
                update_flag = true;
            }
        }
    },      
}

今後

もうちょいきれいに書く方法探しますわ