頑張らないために頑張る

ゆるく頑張ります

Vue cliとBuefyでシンプルに始めるVue.js - UIのコンポーネントを作成する

Posted at — Aug 27, 2019

はじめに

前回は、Vue CLIで作った環境の内容を確認しつつ、どのファイルがどのように参照されているか、どのファイルをどう変更してアプリケーションを開発するかを確認しました。

今回はやっとこさ、コーディングします。もう内容は見切った!もう完璧だ!(死亡フラグ)

取っ掛かりにUI部分のコーディングをします。何はともあれ、まずは見た目です。

と、その前に

「そもそもどんなアプリケーション作るのよ?」ってところです。ここではダミーテキストを生成する機能を実装します。単純に、ちょっと今そういう機能が欲しいのです。

ちなみに、Vue.jsに限らないと思いますが、この手のチュートリアルはToDoリストが鉄板だと思います。

コンポーネントの構成

必要な項目

ダミーテキストジェネレータを実装するに当たって、ざっくりとどんなコンポーネント構成で実装するかを考えます。そこで、テキスト生成に必要なものはどんな機能か、ちょっと考えてみます。

こんなところでしょうか。ダミーテキストは青空文庫から入手するものとします。あと英文のダミーテキストが必要なら、王道のアレですかねぇ。

クリップボードへのコピーは正確にはテキストの内容に影響はしませんが、単純に機能として存在するとうれしいかな、と思って実装します。だって、文字列を生成するだけなのに、その都度ボタンを押して生成されたテキストをマウスなんかで選択してコピーして・・・ってしたくありませんし。それと同じ発想で、「このボタンを押せば、条件はすべてお任せでテキスト生成できるよ」という機能も実装したいなー。

ちなみに、意味のないただランダムに文字を並べただけの文字列が必要なシチュエーションを想定できなかったため、一応「読める文章」を生成するつもりです。なので、基本的には指定された文字数分、青空文庫の文章を切り出す感じに実装しようかと思います。適当に切り出したら文章の途中で終わっちゃうかもしれないから、その辺のケアは必要かもしれません。まぁ追々考えます。

コンポーネントの分割

必要があればコンポーネントを分割して疎結合にし、再利用が容易になるよう実装すべきでしょう。となると、分割できそうな、あるいは分割しても問題なさそうな部分はどのあたりかちょっと考えてみます。

とは言ったものの、先述した機能は画面に表示されるオブジェクトとして1つのコンポーネントにまとめちゃってもいいかもしれません。もちろん、それぞれを別コンポーネントにしっかり分割する、という方法もあります。が、今回のような小規模なアプリケーションにおいては、あんまり細分化するのもイマイチだなぁと思っています。たった数行のHTMLやJavaScriptのためにいくつもコンポーネントを分割するのは、保守性の面から考えれば本末転倒です。

というわけで、今回については1つのコンポーネントに全部乗せすることとします!(白目)あーでも、テキストの生成部分は別コンポーネントに分割するかもしれないなー・・・。

重ねて強調しますが、規模によってコンポーネントを分割する方が絶対保守性高いから!じゃないと後で泣きを見るから!!_(:3」∠)_(経験則)

というわけで、UIの検討を

UIコンポーネントはBuefyを利用するつもりだったので、まずはオフィシャルのドキュメントを参考にどんなインターフェイスを実装するか検討します。

ダミーテキストの生成元の選択

これはもうRadio一択でしょう。いくつかある選択肢のうち、1つだけ選ぶとなればRadioで決まりです。

生成する文字数

デフォルトを100文字として、フォームに任意の数値を入力してもらう方法にしようかな・・・と思っていたらNumberinputという機能を発見。おお、これいいじゃん。ただ、デフォルトでは「+」をクリックしたときに1しか増加しないので、「200文字出力したいから、スタートが100だとすると100回クリックすることに・・・?」ってなるからステップの調整は必須ですね。

あるいはSliderでもいいかもしれません。この辺は実装してみてから使用感を比べて、どっちがいいか考えます。場合によっては両刀使いもいいかもしれません。

その他オプション

各文章において改行の要否

これは単純にYes/NoなのでSwitchでいいと思います。

英数字出力時の全角・半角を選択

これも全角または半角のいずれかなのでSwitchでいいと思います。

必ず句点で終わる

これも単純にYes/NoのいずれかなのでSwitchでいいと思います。

この機能ですが、任意の文字数を入力させた場合、文章を最後まで生成できず途中で終わってしまう可能性があります。そこで、「句点まではテキストを生成する」条件を設定しておきます。この条件を指定すると、入力された文字数プラスαのテキストが生成されることになります。なお、英文の場合はピリオドです。実装はJavaScript側のハナシなので、今回はあくまでも見た目です。

クリップボードへのコピー

これはもうボタンかな。単純に「クリップボードへコピーする」みたいな文字列だけでもいいかもしれません。が、ボタンならより見やすく「これ押せば何かしら処理するだろう」感を演出するためにも、目立つオブジェクトはあった方がいいかと。

そんなわけでコーディング

任意の名前で.vueファイルを作成します。作成したvueファイルについてコーディングするとVisual Studio Code(以下vscode)が下画像のように補完してくれます。すごい助かる(小並感)

ちなみに下画像にもあるように、script部分の記述にはTypeScriptを利用できます(上から2番目にtypescriptの文字が確認できますね)。ただ、今回は純粋にJavaScriptを利用しています。これは単純にいろいろ手を出しすぎると自分の頭では収集が付かなくなるからで、TypeScriptが書ける人はわざわざJavaScriptを記述する必要はないと思います。

pic

実際のコード

そこそこ長くなったので、折りたたみます。

HTMLソース

<template>
    <div id="generator">
        <div class="tile is-ancestor">
            <div class="tile is-parent is-6">
                <div class="tile is-child box container">
                    <div class="has-text-centered">
                        <b-tooltip class="title is-4" label="生成するテキストの引用元を選択してください。" position="is-bottom" dashed multilined>引用元</b-tooltip>
                    </div>
                    <p class="blank1em"></p>
                    <div class="field">
                        <b-radio v-model="textName"
                            name="name"
                            native-value="Lorem Ipsum">
                            Lorem Ipsum
                        </b-radio>
                    </div>
                    <div class="field">
                        <b-radio v-model="textName"
                            name="name"
                            native-value="Silver">
                            Silver
                        </b-radio>
                    </div>
                    <div class="field">
                        <b-radio v-model="textName"
                            name="name"
                            native-value="Jack">
                            Jack
                        </b-radio>
                    </div>
                    <div class="field">
                        <b-radio v-model="textName"
                            name="name"
                            native-value="Vane">
                            Vane
                        </b-radio>
                    </div>
                    <p class="radioValue">
                        <b>選択した元ネタ:</b>
                        {{ textName }}
                    </p>
                </div>
            </div>
            <div class="tile is-parent is-6">
                <div class="tile is-child box">
                    <div class="has-text-centered">
                        <b-tooltip class="title is-4" label="生成したい文字数を入力、またはスライドバーで選択してください。" position="is-bottom" dashed multilined>出力する文字数</b-tooltip>
                    </div>
                    <p class="blank2em"></p>
                    <b-field>
                        <b-numberinput v-model="textNumber" rounded controls-rounded>
                        </b-numberinput>
                    </b-field>

                    <b-field>
                        <b-slider v-model="textNumber" max="300"></b-slider>
                    </b-field>
                    <p class="numberValue">
                        <b>文字数:</b>
                        {{ textNumber }}
                    </p>
                </div>
            </div>
        </div>
        <div class="tile is-ancestor">
            <div class="tile is-parent is-6">
                <div class="tile is-child box">
                    <div class="has-text-centered">
                        <b-tooltip class="title is-4" label="出力する際の条件を設定してください。" position="is-bottom" dashed multilined>出力オプション</b-tooltip>
                    </div>
                    <p class="blank1em"></p>
                    <div class="field">
                        <b-checkbox v-model="selecedOptions"
                            native-value="Silverhoge">
                            Silverhoge
                        </b-checkbox>
                    </div>
                    <div class="field">
                        <b-checkbox v-model="selecedOptions"
                            native-value="Flinthoge">
                            Flinthoge
                        </b-checkbox>
                    </div>
                    <div class="field">
                        <b-checkbox v-model="selecedOptions"
                            native-value="Vanehoge">
                            Vanehoge
                        </b-checkbox>
                    </div>
                    <div class="field">
                        <b-checkbox v-model="selecedOptions"
                            native-value="Billyhoge">
                            Billyhoge
                        </b-checkbox>
                    </div>
                    <p class="content">
                        <b>Selection:</b>
                        {{ selecedOptions }}
                    </p>
                </div>
            </div>
            <div class="tile is-parent is-6">
                <div class="tile is-child box">
                    <div class="has-text-centered">
                        <b-tooltip class="title is-4" label="テキストを生成してクリップボードへコピーします。" position="is-bottom" dashed multilined>テキスト生成</b-tooltip>
                    </div>
                    <p class="blank5em"></p>
                    <div class="has-text-centered">
                        <b-button size="is-medium" icon-left="clipboard-text" @click="alert">テキスト生成する</b-button>
                    </div>
                    <h1>{{ msg }}</h1>
                </div>
            </div>
        </div>
        <p class="blank1em"></p>
    </div>
</template>

UIの実装について

配置

それぞれの配置はTilesで調整することにしました。これならPCで見てもモバイルで見ても問題ありません。

pic

Gridでもいいんじゃね?ってところなんですが、Tilesだと各要素の縦横サイズを自由に変更できます。この自由度の高さとタグの記述量でTilesを今回は採用しました。まぁ、結果から言えば縦横のサイズを変更する必要がなくなっちゃったんですけどね・・・_(:3」∠)_

pic

ちなみにTilesは入れ子構造にできるのですが、単純に子タイルを並べるとタイルの間に隙間がありません。これで問題がないならいいのですが、今回はレイアウト的に隙間が欲しかったので、1つの親タイルに対し子タイルを1つのみ指定するようにしました。このように記述することで隙間の空いた配置を実装できます。

実際にコードのコードは下記を参照してみてください。

See the Pen Buefy-Tiles by ysko909 (@ysko909) on CodePen.

引用元

予定通りRadioで作成しました。

文字数

NumberinputSliderの併用で実装しました。細かい調整はNumberinputのボタンで、ざっくりとした調整はSliderでできます。もちろん、スマホなどでも問題ありません。それぞれのオブジェクトで同じデータをバインドしているので、片方で値を調整するともう片方も自動的に反映されます。

ちなみにステップ数を設定すると、Numberinputのプラスあるいはマイナスをクリックした際に、指定したステップ数の分加減算してくれます。ただし、設定されたステップ数で割り切れない値はエラーになります。たとえばステップ数を10に設定した場合、「23」みたいな10で割り切れない値はエラーになってしまいます。しかも、ご丁寧に「有効な値」を提案までした上で。

pic

入力値を制限したいのではなく、単純に入力の手間をを省きたかっただけなのでステップ数の指定は止めました。

出力オプション

Switchで実装する予定だったんですが、そもそもそれぞれの条件は独立していて相関性がないことを考慮すると、わざわざSwitchで実装する意味はあまりないな、と考えなおしました。最終的にはCheckBoxで実装してます。質実剛健。

生成と出力

Buttonです。これも予定通りです。

その他

Tooltipを実装しました。機能の説明はしておきたいけど、あんまり画面がごちゃごちゃするのはイマイチという場合に使い勝手の良い機能です。ただ、複数行の表示は明示的に指定する必要があって、これを忘れると画面からメッセージが飛び出ます。さすがに、勝手に出現位置を調整する、とかはしてくれませんでした。仕方ないね_(:3」∠)_

pic

おわりに

とりあえずこれでUIの実装は完了です。基本的には、Buefyのドキュメントページを参照しつつ、サンプルのHTMLソースをコピーして調整すれば実装が可能です。この手間のかからない感じがいいですね。しかもnpm run serveを実行してブラウザで参照していると、ソースの変更を察知して自動的に反映してくれるので「ソース修正→保存→ブラウザで即確認」がスムーズにできます。とてもいい感じです。

というわけで、次回はJavaScriptでテキスト生成機能を実装したいと思います。

comments powered by Disqus