"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

Nuxtアプリの体裁を整える「Bulma」ベースのUIフレームワーク「Buefy」をざっくり

f:id:arakan_no_boku:20190509225706p:plain

目次

はじめに

リライトして目次を追加しました。

内容は2019年5月当時のままです。

僕がNuxt.jsの各機能を理解するために、ひとつひとつ確認したことをまとめていこうと考えています。

インストール・環境構築は、以下の手順でできている前提です。

arakan-pgm-ai.hatenablog.com

Nuxt.jsを整理する方針

Nuxt.js関連資料やサンプルソースを見ると混乱することが多いのは、Nuxt.jsはVue.jsの機能を包含していて、ひとつのソースの中で、どれがNuxt.js特有の機能で、どれがVue.jsの機能かの区別がつけにくいからだと感じています。

その混在しているイメージを図にしてみると、こんな感じに見えます。

f:id:arakan_no_boku:20190511100242p:plain

なので、僕はアプローチとして、各技術要素にわけて、一回に少しずつ要素を確認していくことにしました。

分け方としては、以下のようになると考えています。

  1. Nuxt.js独自の要素ー基本
  2. Vue.js独自の要素
  3. BulmaのCSS要素(Burfyのclassは、Bulmaを使っている場合が多い)
  4. Buefy独自の要素
  5. Nuxt.js独自の要素ー応用
  6. テストフレームワーク他モジュールの要素

今回は、CSSに絡む部分です。

BulmaのCSS要素/Buefy独自の要素の両方に関連します。

UIフレームワーク「Buefy」

UIフレームワーク「Buefy」を適用して、画面の体裁を整えるサンプルをやります。

Buefyは「Bulma」のCSSをベースに、各パーツなどを「Vue.js」を適用して強化した「Vue.jsのためのUIフレームワーク」です。

ざっくり役割分担を見てみると。

  • レイアウトや基本的なコンテンツ(h1とかpとか)は「Bulma」のclass
  • Formコントロールや拡張ICONとかは「b-input」などのBuefyで拡張した機能

それぞれを利用します。

混在もできますし、Vue.jsとの親和性も問題ありません。

まさに、Buefyは「Lightweight UI components for Vue.js based on Bulma」です。

フレームワークですから使用にあたっての決め事があります。

例えば。

<h1>とか<ul>みたいな一般的なタグにCSSの効果を出すためには、「content」classで囲っておかないといけない・・とかです。

詳細は、BulmaおよびBuefyの各ドキュメントのサンプルを見るのがはやいです。

buefy.org

bulma.io

 

Buefyで体裁を整えるサンプル

Nuxtの場合、共通レイアウトを定義するやり方が一般的なので、共通レイアウトへの適用とそれ以外で分けて考えます。

共通レイアウトへの適用(default.vue)

共通レイアウトで、ヘダーとフッターを設定します。

フッターには当日の日付と曜日を表示するようにしてみました。

真ん中に「<nuxt/>」と書いている部分に、pagesフォルダで定義したイメージが挿入される感じです。

なお、以下は、イメージで「<nuxt/>」と表示してますが、ソースには書いてません。あしからず(笑)。

f:id:arakan_no_boku:20190522204641p:plain

layouts/default.vue

<template>
  <div>
    <section class="hero is-primary">
      <div class="hero-body">
        <div class="container  has-text-centered">
          <h1 class="title">
            BOKUのサンプル画面
          </h1>
          <h2 class="subtitle">
            Nuxt自己流チュートリアル
          </h2>
        </div>
      </div>
    </section>
    <section>
      <nuxt />
    </section>
    <footer class="footer">
      <div class="content has-text-centered">
        <p>
          <strong>今日は{{ today }}です。</strong>
        </p>
      </div>
    </footer>
  </div>
</template>

<script>
import moment from 'moment'
export default {
  computed: {
    today () {
      return moment().format('YYYY/MM/DD dddd')
    }
  }
}
</script>

ほぼ、BulmaのHeroとfooterタグをほぼサンプル通りに使っているだけです。

<template>から</template>の間だけが、今回影響するところです。

 

それ以外の画面への適用1:画像など要素の表示

pagesフォルダにおく「.vue」ファイルはの内容が、全体レイアウトの<nuxt/>の部分に展開されるわけです。

まず、ルート画面にあたる「index.vue」のイメージはこちらです。

f:id:arakan_no_boku:20190522203125p:plain

ソースはこちらです。

<template>
  <div>
    <div class="container content has-text-centered">
      <img src="~assets/00_nuxt.JPG" alt="image01">
    </div>
    <div class="container content has-text-centered">
      <NLink to="/about">
        <h3>{{ linkname1 }}</h3>
      </NLink>
    </div>
    <div class="container content has-text-centered">
      <NLink to="/cityapi">
        <h3>{{ linkname2 }}</h3>
      </NLink>
    </div>
    <div class="container content has-text-centered">
      <a :href="outurl">
        <h3>{{ linkname3 }}</h3>
      </a>
    </div>
  </div>
</template>

補足しておきます。

ごくシンプルに真ん中寄せしています。

<div class="container content has-text-centered">

ほとんど、これでいけます。

ただ、画像の中央寄せには注意が必要です。

Bulmaのサンプルを見ると、イメージファイルは「<figure class="image is-128x128">」のように囲ってますが、そうすると「has-text_centered」では中央寄せができなくなり、別のやり方を画像だけ適用しないといけなくなるわけです。

 

あと、前にも書いた「contentです」。

Bulmaではクラス「content」を適用したもので囲っていないと、h1やp,ulなどのタグのCSS定義が適用されないようになってます。

なので。

   <div class="container content has-text-centered">
      <a :href="outurl">
        <h3>{{ linkname3 }}</h3>
      </a>
    </div> 

このように、すべての要素をcontentを適用したDIVタグでくくっています。

 

それ以外の画面への適用2:ボタンなど

表示したイメージはこんな感じで。

f:id:arakan_no_boku:20190522215528p:plain

ソースコードはこうです。

<template>
  <div>
    <div class="container content has-text-centered">
      <NLink to="/">
        <p>ルートのページへ戻ります</p>
      </NLink>
    </div>
    <div class="container content has-text-centered">
      <img src="/post.JPG" alt="image02">
    </div>
    <div class="columns">
      <div class="column" />
      <div class="column">
        <div class="column">
          <b-field label="郵便番号">
            <b-input v-model="zip" placeholder="郵便番号を入力する" />
          </b-field>
        </div>
      </div>
      <div class="column" />
    </div>
    <div class="container  has-text-centered">
      <b-button type="is-primary" @click="getAddr({zip})">
        住所情報取得
      </b-button>
    </div>
    <hr>
    <div v-if="datas != null" class="container content has-text-centered">
      <div v-if="datas.status == 200">
        <h4>郵便番号:{{ datas.results[0].zipcode }}</h4>
        <h4>都道府県名:{{ datas.results[0].address1 }}</h4>
        <h4>市区町村名:{{ datas.results[0].address2 }}</h4>
        <h4>町域名:{{ datas.results[0].address3 }}</h4>
      </div>
      <div v-else>
        <h4>指定の郵便番号では住所情報の取得ができませんでした。</h4>
      </div>
    </div>
    <div v-else class="container content has-text-centered">
      <br>
      <p>郵便番号を入力してボタン「住所情報取得を押します。</p>
    </div>
  </div>
</template>

ポイントだけ補足します。

入力欄とボタンについては、<b-xxxx>のBuefyのタグを使ってます。

入力欄は。

<b-field label="郵便番号">
    <b-input v-model="zip" placeholder="郵便番号を入力する"></b-input>
</b-field> 

のように、<b-field>で囲います。

そうするとラベルをつけるのが楽です。

ただ、気をつけないといけないのが、位置合わせの方法です。

ボタンは「<b-button>」に変更しても、has-text-centeredでセンタリングできます。

でも、<b-input>はききません・・というか、めいっぱい横に広がってしまいます。

そのため「 <div class="columns">」のなかに「<div class="column">」を3つ配置して、真ん中に「<b-input>」を配置することで、位置合わせしてます。

 

それ以外の画面への適用2:Selectなど

こんなイメージです。

f:id:arakan_no_boku:20190522225447p:plain


ソースコードはこうです。

<template>
  <div>
    <div class="container content has-text-centered">
      <NLink to="/">
        <p>ルートのページへ戻ります</p>
      </NLink>
    </div>
    <div class="container content has-text-centered">
      <img src="/jamap.JPG" alt="image03">
    </div>
    <div v-if=" prefecturesJson.message == null" class="columns">
      <div class="column" />
      <div class="column is-half">
        <b-field horizontal label="都道府県" type="is-primary">
          <b-select v-model="prefecturesSelected" expanded @input="updateMunicipalitiesJson({prefecturesSelected})">
            <option v-for="pOption in prefecturesJson.result" :key="pOption.id" :value="pOption.prefCode">
              {{ pOption.prefName }}
            </option>
          </b-select>
        </b-field>
      </div>
      <div class="column" />
    </div>
    <div v-if="municipalitiesJson != null" class="columns">
      <div class="column" />
      <div class="column is-half">
        <b-field horizontal label="市区町村" type="is-primary" :v-if="municipalitiesJson.message == null">
          <b-select v-model="municipalitiesSelected" expanded>
            <option v-for="mOption in municipalitiesJson.result" :key="mOption.id" :value="mOption.cityCode">
              {{ mOption.cityName }}
            </option>
          </b-select>
        </b-field>
      </div>
      <div class="column" />
    </div>
    <div v-else class="columns">
      <div class="column" />
      <div class="column is-half">
        <b-field horizontal label="市区町村">
          <b-select v-model="municipalitiesSelected" expanded>
            <option value="" />
          </b-select>
        </b-field>
      </div>
      <div class="column" />
    </div>
  </div>
</template>

ポイントを補足します。 

Selectを使ってます。

Selectも「has-text-centered」はきかないので、<column>でレイアウトしてます。

Selectは「<b-select v-model="municipalitiesSelected" expanded>」のようにBuefyの「b-select」を使ってます。 

Bulmaの「<select>」から、「<b-select>」に変更した時に、気をつけないといけないことがあります。

Bulmaの「<select>」の場合は「@change」で選択されたイベントをひろって、処理をキックすることができます。

でも、「<b-select>」に変更すると「@change」では発火しません。

イベントのトリガを「@input」に変更する必要があります。

 

まとめです

BlumaとBeufyのスタイルを適用して、体裁を整えるのをやってみました。

多様な機能のごく一部しか使ってないですが、それでも、ヘダーとフッターをつけて、体裁を整えるだけで、かなり見た目が変わるので、BuefyのようなUIフレームワークは、センスの良い人が考えたベストプラクティスを簡単にとりいれることができて、センスのない自分みたいな人間にはありがたいかな・・とは思います。

さて、今回はこんなところで・・。 

ではでは。