astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

BootstrapとVueでタブを作ってみる、の巻

2016/09/30に2.0がリリースされてから、現在2017/03/05まででダウンロード数約3倍の増加をみせるVue.js。 f:id:astamuse:20170308132520p:plain

npmtrends(2016/09/25 32,043 → 2017/03/05 109,774)

そろそろ無視出来ない勢いなので、とりあえず触ってみることにしました。 ドキュメントもしっかり日本語化されてるので英検4級の私にはありがたい。←ここ重要

1. 下準備

vue-cliを使ったほうがかなり楽そうなんですが、めんど(ry

ファーストステップなのでVue.jsのみでやってみたいと思います。

またCSS書くのもめんど(ry

久しぶりにbootstrap触りたくなったのでそれ使います。

ということでCodePenだけで準備完了です。すばらしい。pugも使える。CodePenありがとう。

2. とりあえずhtmlを組んでいく

元テンプレートはこちら

div.container
  h1 vue tab sample
  ul(class="nav nav-tabs" role="tablist")
    li(class="nav-item" role="presentation")
      a(href="#home" class="nav-link active" role="tab") ホーム
    li(class="nav-item" role="presentation")
      a(href="#test" class="nav-link" role="tab") テスト
  
  div.tab-content
    div(id="home" role="tabpanel")
      h2 HOME
      p ホームタブコンテンツ
    div(id="test" role="tabpanel")
      h2 TEST
      p テストタブコンテンツ




まず <slot>を使ってhtml側で管理する内容と、vue側で管理する内容を分離していきましょう。ここでは、テキスト内容をhtml、そのほかのものはvue側で管理させるようにしました。 (サンプルではわかりやすいようにnameをつけてますが、なくてokです)

div#v-root.container
  h1 vue tab sample
  tabs
    div(slot="tabContents")
      tab(name="home" text="ホーム" v-bind:selected="true")
        div(slot="tabinner")
          h2 HOME
          p ホームタブコンテンツ
        
      tab(name="test" text="テスト")
        div(slot="tabinner")
          h2 TEST
          p テストタブコンテンツ


Vue.component('tabs', {
  template: `
    <div>
      <ul class="nav nav-tabs" role="tablist">
        <li class="nav-item" role="presentation">
          <a href="#home" class="nav-link" role="tab">ホーム</a>
        </li>
        <li class="nav-item" role="presentation">
          <a href="#test" class="nav-link" role="tab">テスト</a>
        </li>
      </ul>
      <div class="tab-contents">
        <slot name="tabContents"></slot>
      </div>
    </div>
  `
});
Vue.component('tab', {
    template: `
    <div :id="'#' + this.name" role="tabpanel"><slot name="tabinner"></slot></div>
    `,
    props: {
        name: { required: true },
    },
});
new Vue({
    el: '#v-root'
});



See the Pen Vue tab sample 03 by 35n139e (@35n139e) on CodePen.

3. Vue実装開始

tab実装

タブコンテンツを軸にタブリストを生成する方法で実装していきます。 html側でid名やタブのテキストを管理するので、propsにtext(テキスト)とname(id名)、selectedでデフォルトで開くタブを選べるようにしました。

tab(name="home" text="ホーム" v-bind:selected="true")
  div(slot="tabinner")
    h2 HOME
    p ホームタブコンテンツ
  
tab(name="test" text="テスト")
  div(slot="tabinner")
    h2 TEST
    p テストタブコンテンツ


Vue.component('tab', {
  template: `
    <div :id="'#' + this.name" role="tabpanel" v-if="isActive">
      <slot name="tabinner"></slot>
    </div>
  `,
  data() {
    return {
      isActive: false
    };
  },
  props: {
    text: { required: true },
    name: { required: true },
    selected: { default: false }
  },
  mounted() {
    if(location.hash != ""){
      const url = location.hash;
      this.isActive = (url == '#' + this.name);
    }else{
      this.isActive = this.selected;
    }
  }
});

tabs実装

つぎにタブリストの生成です。

Vue.component('tabs', {
  template: `
    <div>
      <ul class="nav nav-tabs" role="tablist">
        <li v-for="tab in tabs" class="nav-item" role="presentation">
          <a :href="'#' + tab.name" @click.prevent="selectTab(tab)" role="tab" class="nav-link" :class="{ 'active': tab.isActive }">{{ tab.text }}</a>
        </li>
      </ul>
      <div class="tab-content">
        <slot name="tabwrap"></slot>
      </div>
    </div>

    `,
    data() {
        return { tabs: [] };
    },
    created() {
        this.tabs = this.$children;
    },
    methods: {
      selectTab(selectedTab) {
        this.tabs.forEach(function(tab){
          tab.isActive = (tab.name == selectedTab.name);
        });
      }
    }
});

v-forでタブコンテンツの中を見に行かせます。

<li v-for="tab in tabs" class="nav-item" role="presentation">

タブリストの中身はそれぞれtabのpropsを参照させます。タブのアクティブ化は'v-bind:class'で行います。

<a :href="'#' + tab.name" @click.prevent="selectTab(tab)" role="tab" class="nav-link" :class="{ 'active': tab.isActive }">{{ tab.text }}</a>


タブリストをクリックしたときの関数をmethodsに記述。 それぞれのtab.nameとクリックしたtab.name(selectedTab.name)にアクティブフラグを立てさせます。

methods: {
  selectTab(selectedTab) {
    this.tabs.forEach(function(tab){
      tab.isActive = (tab.name == selectedTab.name);
    });
  }
}

完成

tabsとtabを合わせるとあら不思議。タブの完成です。

See the Pen Vue tab sample 04 by 35n139e (@35n139e) on CodePen.

4. <transition>で味付け

最後に<transition>を使って味付けしていきます。

animationのcssはAnimate.cssを利用させてもらいます。

下記のようにカスタムすれば<transition>でAnimate.cssを直接使うことができます。

<transition
  name="animateCSS"
  enter-active-class="animated fadeIn"
  leave-active-class="hide"
>
  <div :id="'#' + this.name" role="tabpanel" v-if="isActive">
    <slot name="tabinner"></slot>
  </div>
</transition>

これでアニメーションさせたいコンテンツをラッピングして終了です。簡単!キレイ!

See the Pen Vue tab sample 3 by 35n139e (@35n139e) on CodePen.

5. 最後に

Vue.jsはガイドもしっかりしてるし、学習コストも低めなので「Jqueryしか書いたことないよ」って方もぜひ触ってみていただきたいフレームワークです。

私もお試しで触ってみてハマった口で、今週の月曜日に弊社の採用サイトをVue.jsで作り直しました。ぜひそちらのほうも見ていただければと思います。

参照・参考元

Copyright © astamuse company, ltd. all rights reserved.