Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What is going on with this article?
@uegaki-masaaki

Material Design for Bootstrap 4 (Vue version)を導入してみる

More than 3 years have passed since last update.

Angular5で使ってたMDBootstrapのVueバージョンがあったので入れてみた。
ナビゲーションがMDBootstrapそのままだとvue-routerのrouter-linkが使えなくて、
コンポーネントを少しローカルで変更した。

完成した画面

  • vue-cliの初期画面に上にナビゲーションくっ付けた

Screen Shot 2018-05-03 at 20.39.00.png

必要なパッケージのインストール

yarn add mdbootstrap bootstrap-vue classnames
yarn add @types/classnames --dev

テンプレートのコンポーネントをgit cloneで落としてくる

  • Angular、Reactはパッケージ追加でいけるけど、Vueは何故かテンプレートをgit cloneしてプロジェクト作成するか、vue-cliでテンプレートを使って新規作成する事になってて、既存プロジェクトへの導入が不便だった
git clone https://github.com/mdbootstrap/Vue-Bootstrap-with-Material-Design.git mdb

テンプレートからvue-cliで作った既存プロジェクトにソースをコピー

  • コンポーネント
mkdir ~/works/todo/src/conponents/mdbootstrap
cp ~/works/mdb/src/components/* ~/works/todo/src/mdbootstrap
  • mixins
mkdir ~/works/todo/src/mixins
cp ~/works/mdb/src/mixins/* ~/works/todo/src/mixins

main.tsにリップル効果のCSSをimport追加

src/main.ts
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import './registerServiceWorker';

import 'bootstrap/dist/css/bootstrap.css';
import 'mdbootstrap/css/mdb.css';
+ import './components/mdbootstrap/Waves.css';

Vue.config.productionTip = false;

new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount('#app');

App.vueのナビゲーションをMDBootstrapのナビゲーションに変更

src/App.vue
<template>
  <div id="app">
    <!--Navbar-->
    <navbar position="top" class="indigo navbar-dark" name="ToDo" to="/" scrolling>
      <navbar-collapse>
        <navbar-nav>
          <navbar-item to="/" active exact waves-fixed>Home</navbar-item>
          <navbar-item to="/about" waves-fixed>About</navbar-item>
          <navbar-item to="/tasks" waves-fixed>Tasks</navbar-item>
          <!-- Dropdown -->
          <dropdown tag="li" class="nav-item">
            <dropdown-toggle tag="a" navLink color="indigo" waves-fixed>Dropdown</dropdown-toggle>
            <dropdown-menu>
              <dropdown-item>Action</dropdown-item>
              <dropdown-item>Another action</dropdown-item>
              <dropdown-item>Something else here</dropdown-item>
            </dropdown-menu>
          </dropdown>
        </navbar-nav>
          <!-- Search form -->
        <form class="form-inline">
          <mdinput type="text" placeholder="Search" aria-label="Search" label navInput waves waves-fixed/>
        </form>
      </navbar-collapse>
    </navbar>

    <div class="container">
      <router-view/>
    </div>
  </div>
</template>

<script>
import Navbar from "@/components/mdbootstrap/Navbar.vue";
import NavbarItem from "@/components/mdbootstrap/NavbarItem.vue";
import NavbarNav from "@/components/mdbootstrap/NavbarNav.vue";
import NavbarCollapse from "@/components/mdbootstrap/NavbarCollapse.vue";
import Container from "@/components/mdbootstrap/Container.vue";
import Dropdown from "@/components/mdbootstrap/Dropdown.vue";
import DropdownItem from "@/components/mdbootstrap/DropdownItem.vue";
import DropdownMenu from "@/components/mdbootstrap/DropdownMenu.vue";
import DropdownToggle from "@/components/mdbootstrap/DropdownToggle.vue";
import drop from "@/mixins/drop";
import Mdinput from "@/components/mdbootstrap/MdInput.vue";

export default {
  components: {
    Navbar,
    NavbarItem,
    NavbarNav,
    NavbarCollapse,
    Container,
    Dropdown,
    DropdownItem,
    DropdownMenu,
    DropdownToggle,
    Mdinput
  },
  mixins: [drop]
};
</script>

<style lang="scss">
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

.navbar .dropdown-menu a:hover {
  color: inherit !important;
}
.fixed-top {
  position: sticky;
}
</style>

Navbarのコンポーネントをrouter-linkで遷移させるように変更

  • activeのメニューの背景色を変える部分も動かなかったので変更した。App.vue側の/のルーティングにはexact属性を付けてある。
src/components/mdbootstrap/Navbar.vue
<template>
  <nav :class="className" :is="tag">
-    <a :href="href" class="navbar-brand">{{name}}
+    <router-link :to="to" class="navbar-brand">{{name}}
      <img v-if="src" :src="src" :alt="alt"/>
-    </a>
+    </router-link>
    <button class="navbar-toggler" type="button" data-toggle="collapse" :data-target="target" aria-controls="navbarSupportedContent"
        aria-expanded="false" aria-label="Toggle navigation" v-on:click="toggle">
      <span class="navbar-toggler-icon"></span>
    </button>
    <slot></slot>
  </nav>
</template>


<script>
//
//
//
//
//
//
//
//
//
//
//
//
//
//

import classNames from "classnames";

export default {
  props: {
    tag: {
      type: String,
      default: "nav"
    },
    expand: {
      type: String,
      default: "large"
    },
    position: {
      type: String
    },
-    href: {
+    to: {
      type: String
    },
    src: {
      type: String
    },
    alt: {
      type: String
    },
    name: {
      type: String
    },
    target: {
      type: String,
      default: "#navbarSupportedContent"
    },
    scrolling: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      className: classNames(
        "navbar",
        "navbar-dark",
        this.expand === "small"
          ? "navbar-expand-sm"
          : this.expand === "medium"
            ? "navbar-expand-md"
            : this.expand === "large" ? "navbar-expand-lg" : "navbar-expand-lx",
        this.position === "top"
          ? "fixed-top"
          : this.position === "bottom" ? "fixed-bottom" : "",
        this.scrolling ? "scrolling-navbar" : ""
      ),
      scrolled: false,
      toggleClicked: true
    };
  },
  methods: {
    toggle(e) {
      if (this.toggleClicked) {
        this.collapse.classList.toggle("show-navbar");
        this.collapse.classList.remove("hide-navbar");
        this.collapse.classList.toggle("collapse");
        this.collapse.style.overflow = "hidden";
        this.collapseOverflow = setTimeout(() => {
          this.collapse.style.overflow = "initial";
        }, 300);
        this.toggleClicked = false;
      } else {
        this.collapse.classList.add("hide-navbar");
        this.collapse.classList.toggle("show-navbar");
        this.collapse.style.overflow = "hidden";
        this.collapseOverflow = setTimeout(() => {
          this.collapse.classList.toggle("collapse");
          this.collapse.style.overflow = "initial";
        }, 300);
        this.toggleClicked = true;
      }
    },
    handleScroll() {
      if (window.scrollY > 100 && this.scrolled === false) {
        this.$el.style.paddingTop = 5 + "px";
        this.$el.style.paddingBottom = 5 + "px";
        this.scrolled = true;
      } else if (window.scrollY < 100 && this.scrolled === true) {
        this.$el.style.paddingTop = 12 + "px";
        this.$el.style.paddingBottom = 12 + "px";
        this.scrolled = false;
      }
    }
  },
  mounted() {
    this.collapse = this.$el.children.navbarSupportedContent;
    this.collapse.classList.add("collapse");
  },
  created() {
    window.addEventListener("scroll", this.handleScroll);
  },
  destroyed() {
    window.removeEventListener("scroll", this.handleScroll);
  }
};
</script>

<style scoped>
.scrolling-navbar {
  transition: padding 0.5s;
}
.nav-item {
  position: relative;
}
</style>

NavbarItemのコンポーネントをrouter-linkで遷移させるように変更

src/components/mdbootstrap/NavbarItem.vue
<template>
  <li :is="tag" :class="[className, {'ripple-parent': waves}]" @click="wave">
-    <a :href="href" class="nav-link"><slot></slot></a>
+    <router-link :to="to" class="nav-link" :exact="exact"><slot></slot></router-link>
  </li>
</template>

<script>
//
//
//
//
//
//

import classNames from "classnames";
import waves from "../../mixins/waves";

export default {
  props: {
    tag: {
      type: String,
      default: "li"
    },
    active: {
      type: Boolean,
      default: false
    },
-    href: {
+    to: {
      type: String,
      default: "#"
    },
    waves: {
      type: Boolean,
      default: true
    },
    wavesFixed: {
      type: Boolean,
      default: false
    },
+    exact: {
+      type: Boolean,
+      default: false
+    }
  },
  data() {
    return {
      className: classNames("nav-item", this.active ? "active" : "")
    };
  },
  mixins: [waves]
};
</script>

<style scoped>
+ .navbar.navbar-dark .breadcrumb .nav-item > .nav-link.router-link-active,
+ .navbar.navbar-dark .navbar-nav .nav-item > .nav-link.router-link-active {
+   background-color: rgba(255, 255, 255, 0.1);
}
</style>

これで完成。

感想

  • Angular5でMDBootstrap使った時はパッケージ追加してソース修正でやれたけど、Vue.jsでは面倒だった。
  • App.vueをTypeScriptにしたら、エラーで動かなかった。型定義は用意されてないみたいなのでJsでやるしかない。

ERROR in /Users/uwettie/works/todo-client/src/App.vue
43:18 Could not find a declaration file for module '@/mixins/drop'. '/Users/uwettie/works/todo-client/src/mixins/drop.js' implicitly has an 'any' type.
    41 | import DropdownMenu from "@/components/mdbootstrap/DropdownMenu.vue";
    42 | import DropdownToggle from "@/components/mdbootstrap/DropdownToggle.vue";
  > 43 | import drop from "@/mixins/drop";
       |                  ^
    44 | import Mdinput from "@/components/mdbootstrap/MdInput.vue";
    45 |
    46 | export default {
Version: typescript 2.8.3, tslint 5.9.1

Vuetify

Vue.jsのマテリアルデザインのコンポーネントフレームワークは、Vuetifyというのが有名らしい。
そちらでやる方がいいんだろうか。
Vue.js専用っぽいから廃れちゃったら嫌だなと思っちゃうけど大丈夫なんだろうか。

作ったソース

8
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
8
Help us understand the problem. What is going on with this article?