Vue.js[タグ]のような要素数が不特定のデータを投稿するフォーム

自由な数だけ言葉を追加する「タグ付け」のようなことができるVue.jsのフォームの作り方。

イメージとして既存のサービス上げると、

ニコニコ動画↓

WordPress↓

今ワードプレスと聞いてマウントを取りたくなったウェブエンジニアの人はお帰りください。

既存のコンポーネントを使うなら

自分で作らなくてもCSSフレームワーク側で用意されていることもある。

例えばBootstraVueというフレームワークなら元から用意されている。
Form Tags | Components | BootstrapVue

後から気づいたわ。

作ってみた

既存のコンポーネントに頼らず、あえて自力で作るならどうするか解説。

仕様

・タグは最大5個まで(変更可能)

・空のタグは入力不可(追加しても何も起こらない)

・×ボタンで削除可能

・データベースへ配列のデータとして投げる

・データベースのタグを保存するカラムはjson型

今回の環境

・vue2

・bootstrap-vue(CSSフレームワーク)

前提条件

・vue導入済み

・テーブルにjson形式のデータを格納できるカラムがある

変数初期化

//Post.vue
data() {
    return {
        tag: null,//これから入力するタグが入る
        tags: [],//入力されたタグ軍が入る
        disabled: false,//特定条件でフォームを無効化する
    }
},

html

//Post.vue
<div>

    <div>
        <input type="text" v-model="tag" v-on:keydown.enter="addTag" placeholder="タグ"
            :disabled="disabled" />
         <b-button @click="addTag"  :disabled="disabled">追加</b-button>
    </div>

    <span v-for="tag in tags" :key="tag.id" class="badge mb-3 mb-sm-0 badge-secondary"
        style="margin-right:.5em;">
        <b-button @click="deletTag(tag)" variant="secondary" pill style="font-size:75%;padding:initial;">
            <font-awesome-icon :icon="['fa', 'times']" />
        </b-button>
        {{tag}}
    </span>

</div>

5行目のinputはタグを入力するためのもの。

エンターを押したり、追加ボタンを押したりすると、追加メソッドが発動する。

10行目v-for="tag in tags" :key="tag.id"はforeach的なことをして、入力されたタグを全部表示している。

もちろんはじめは何も入力されていないので何も出ない。

タグの先頭に表示される×ボタンをクリックすると、削除メソッドが発動して選んだタグが消える。

メソッド

//Post.vue
methods: {
    addTag() {
        if (this.tag && (this.tags.indexOf(this.tag)) == -1) {
            this.tags.push(this.tag)
              //入力値は空じゃないし、重複はないから追加する
        } 

        this.tag = null

        if (this.tags.length >= 5) {
            this.disabled = true
        }
    },
    deletTag(tag) {
        let index = this.tags.indexOf(tag);
        this.tags.splice(index, 1)

        if (this.tags.length < 5) {
            this.disabled = false
        }
    },
}

addTag関数とdeleteTag関数、それぞれを解説。

addTag関数

//Post.vue
addTag() {
    if (this.tag && (this.tags.indexOf(this.tag)) == -1) {
        this.tags.push(this.tag)
        //入力値は空じゃないし、重複はないから追加する
    } 

    this.tag = null

    if (this.tags.length >= 5) {
        this.disabled = true
    }
},

inputに入力されたtagの値が、下記の2つの条件の両方

・空じゃない

・tagsの中に重複するもの賀存在しない

を満たすと、

配列tagsに入力値tagの内容を追加する。

空だったり、既に存在する値を入力した場合は何も起こらない。

追加ボタンを押したりしても。空のカラムに戻るだけ。

もしタグの数の合計(配列tagsの要素数)が5個になったら、変数disabledをtrueにして、inputとボタンを触れなくする。

↓さっき登場した該当箇所のhtml

//Post.vue
<input type="text" v-model="tag" v-on:keydown.enter="addTag" placeholder="タグ"
      :disabled="**disabled**" />
 <b-button @click="addTag"  :disabled="**disabled**">追加</b-button>

太字で記した変数disabledがtrueになり、inputとb-buttonがグレーアウトするとい仕組み。

deleteTag関数

//Post.vue
deletTag(tag) {
    let index = this.tags.indexOf(tag);
    this.tags.splice(index, 1)

    if (this.tags.length < 5) {
        this.disabled = false
    }
},

×ボタンが押されたら、そのタグを消すメソッド。

×ボタンを押すと、ブラウザから消したいタグの値がdeleteTag関数にわたってくる。

上の画像でいうと「チー牛」とか。

3行目 let index = this.tags.indexOf(tag); にて、そのタグが配列tagsにおける何番目の要素数かを特定する。

tagsの中身が

[”チー牛”,陰キャ”,”メガネ”,拳で”,”21歳”,]

だったとする。

「チー牛」は要素数で言うと0番目にあたる。

0 チー牛”,

1 陰キャ”,

2 ”メガネ”,

3 “拳で”,

4 ”21歳”,

this.tags.splice(index, 1)

で該当の要素数の値を配列tagsから削除する。

参考にしたのはこのサイト

https://pisuke-code.com/js-remove-element-by-value/#:~:text=配列から値指定で削除のまとめ,-最後に2&text=indexOf('apple')%3B,で削除すればOK。&text=もし重複値を含む,function(v){ return !

投稿する

あとは、apiなどでtagsの内容がデータベースの任意のカラムにポストできればOK。

今回はフォームの作り方なので、この辺は割愛する。

投稿画面にはそんなにバチバチにバリデーションかけなくても大丈夫。

配列形式がフロント側で成り立っている状態でデータベースにポストするので、,などの文字列が含まれてても問題なく保存できる。

ちなみにバックエンドがLaravelの場合だが、モデルで該当のjsonカラムを許可してあげないと書き込めなかった。

//中略
class Stock extends Model
{
    protected $guarded = ['id'];

    protected $casts = [
        'tags' => 'json',
    ];

//中略

}

protected $guarded = ['id'];って書いてるから、idカラム以外は全部自由に書き込み出来たと思ったのだが・・・

呼び出し方

配列として呼び出してあげるには、バックエンドで整形してあげないといけない。

explode(',', $this->tags)

のように、,で区切られた配列であることを宣言する。

めっちゃ雑やけど、Laravelならこんなかんじ。

//プロジェクトディレクトリ\app\Http\Resources\StockResource.php
<?php

namespace App\Http\Resources;

use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;

use App\Models\Stock;

class StockResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        $stock = new Stock;
        return [
            'tags'=> explode(',', $this->tags),
        ];        
    }
}
無制限に質問可能なプログラミングスクール!

万が一転職できない場合は、転職保障全額返金できるコースもあり!!

無制限のメンター質問対応

 

DMMウェブキャンプでプログラミングを学習しませんか?

独学より成長スピードをブーストさせましょう!

 

まずは無料相談から!

おすすめの記事