はじめに
Vue3系を勉強し始めてcomponentを使う練習をしたかったので
モーダルを作ってみた時の忘備録です。
また、初心者のため
Emit(子→親)についても整理しながらの記事になります。
もうVueを使い倒している方には向かない内容につき
時間の無駄になってしまうので読まないでくださいね!!!
私は、今までVue2系の"vue-property-decorator"
を使用していたので
Vue3系の勉強をしながら作成したものになります。
不備等もあると思いますがご了承ください。
*関連ワード
Vue.js 3系 / TypeScript / SCSS / component / Mac OS / Modal / emit
完成形
ディレクトリ構成
ディレクトリ内
※ storeやrouterなど使用していないものは省略しています
src/
┣ components/
│ ├ Header.vue
│ ├ Modal.vue
│
┣ views/
│ ├ Home.vue
│
┣ App.vue
###モーダルウィンドの構成
① Header.vue に作成したハンバーガーボタンをクリックする(親コンポーネント)
↓
② Modal.vue がモーダルで表示される(子コンポーネント)
↓
③ Modal.vue の背景をクリックするとモーダルが閉じる(子→親)
というシンプルな構成です。
*前提として
App.vue でHeaderコンポーネントを表示
海の背景は Home.vue としてviewsに格納してあり、
Topページとして表示させてある状態です。
<template>
<Header></Header>
<router-view />
</template>
<script lang="ts">
import { defineComponent } from "vue";
import Header from "./components/Header.vue";
export default defineComponent({
components: { Header },
});
</script>
// style 省略
<template>
<div class="container">
<div class="top-img">
<img src="/img/sea.jpg" alt="人魚のシルエット" />
</div>
</div>
</template>
// script 省略
<style lang="scss">
.container {
width: 100%;
}
.top-img {
position: relative;
width: 100%;
height: 100vh;
img {
width: 100%;
height: 100vh;
}
}
</style>
<子コンポーネント> Modal.vueにて
***モーダルウィンドの構成(再)**
`① Header.vue に作成したハンバーガーボタンをクリックする(親コンポーネント)` ↓ `② Modal.vue がモーダルで表示される(子コンポーネント)` ↓ `③ Modal.vue の背景をクリックするとモーダルが閉じる(子→親)`まず最初に、
モーダルウィンドの構成②:モーダルウィンドが表示された時の画面(子コンポーネント)
モーダルウィンドの構成③:モーダルウィンドが背景をクリックするとモーダルが閉じる(子→親)
について触れていきます。
*templateにて <モーダルウィンドの構成②>
<div id="overlay" v-on:click="closeModal()">
の
divタグで表示させたいところを囲む。
※ v-on:click="closeModal( )"については、後ほど説明
<template>
<div id="overlay" v-on:click="closeModal()">
<div id="content">
<img src="img/ariel.png" alt="人魚のシルエット" />
<span>モーダルウィンドが<br />表示されました</span>
</div>
</div>
</template>
*scriptにて <モーダルウィンドの構成③>
Vue3系より使用できるようになったsetup()関数
でコンポーネントを、
setup()
の第1引数にprops、第2引数にcontextを持たせる。
これで 親子間のデータの引き渡しが可能となる。
モーダルが表示された後、
モーダルの背景をクリックするとモーダルウィンドが閉じるようにしたいので
このウィンド自体である<div id="overlay"> </div>
に
v-on:clickで背景がクリックされた時にモーダルが閉じるメソッドを持たせる
v-on:click="closeModal()
<script lang="ts">
import { defineComponent } from "@vue/runtime-core";
export default defineComponent({
setup(props, context) {
/**
* モーダルウィンドを閉じる.
*/
let closeModal = () => {
context.emit("close");
};
return {
closeModal,
};
},
});
</script>
closeModal()
メソッドの処理内容に
context.emit("close");
と書く。
この処理を記載することで
子コンポーネント内でcloseModal()
が呼ばれた時に
親コンポーネントのtemlateタグに記載している、
<Header v-on:close="親のメソッド名"> </Header>
↑ここの親メソッド名
の処理が呼ばれることになる。
***Emit 子→親にデータを渡す処理**
今回はデータを渡すというよりは、“クリックされたよ〜!” ってemitを通じて親に伝えているみたいな?ニュアンス?? `context.emit("close")` ↑この1文で親の`close`を呼び出すようなイメージ。 子コンポーネントで`"close"`で呼ばれたらこの処理をするというメソッドを 親コンポーネントに書いておくことで 親子関係が成り立っている。次に親での処理をみていきます
<親コンポーネント> Header.vueにて
*templateにて <モーダルウィンドの構成①>
今回クリックするボタンは、fontAwesomeのbarsを使用。
<i class="fas fa-bars"></i>
もし、分かりにくと感じた方は<div class="btn"> </div>
自体が
<button type="button">ボタン</button>
であると考えてもらって大丈夫です!
<template>
<header>
<div class="btn" v-on:click="openModal()">
<i class="fas fa-bars"></i>
</div>
<transition name="fade">
<Modal v-if="showContent" v-on:close="closeModal()"></Modal>
</transition>
</header>
</template>
v-on:clickやv-ifのところは次に詳細記載します。
####*scriptにて <モーダルウィンド完成>
setup()
内にてモーダルの表示の有無を宣言する
let showContent = ref(false);
trueならモーダル表示
/ falseならモーダル非表示
そしてこの表示の切り替えをするメソッドをそれぞれ作成する。
openModal()
/ closeModal()
<script lang="ts">
import { defineComponent, ref } from "@vue/runtime-core";
import Modal from "./HeaderModal.vue";
export default defineComponent({
components: {
Modal,
},
setup() {
//モーダルクリックチェック
let showContent = ref(false);
/**
* モーダルウィンドウを表示する.
*/
let openModal = () => {
showContent.value = true;
};
/**
* モーダルウィンドを閉じる.
*/
let closeModal = () => {
showContent.value = false;
};
return { showContent, openModal, closeModal };
},
});
</script>
***Vue3系でのコンポーネントの書き方**
・親コンポーネントのscriptタグにて子コンポーネントをインポートする `import 子コンポ名A from "子コンポまでのパス"` ・`defineComponent({})`内で`setup()`の前に使用するコンポーネントを書く `export default defineComponent({` `components: { 子コンポ名A },` この子`コンポ名A`でtemplateタグ内で表示させることができる*templateにて
<template>
<header>
<div class="btn" v-on:click="openModal()">
<i class="fas fa-bars"></i>
</div>
<transition name="fade">
<Modal v-if="showContent" v-on:close="closeModal()"></Modal>
</transition>
</header>
</template>
<div class="btn">
にv-on:click="openModal()"
で
scriptタグに用意したopenModal()
メソッドを呼び
ボタンをクリックされたらモーダルが開く処理。
子コンポーネントであるModalには、v-if
を使って
trueなら表示、falseなら非表示とさせる。
<Modal v-if="showContent"></Modal>
最後に
v-on:close="closeModal()"
で、
先ほど、子コンポーネントでemitで呼び出す処理にて
"close"
が呼ばれたら、親コンポーネントのscriptダグに宣言した
closeModal()
メソッドが呼ばれるようにしておく。
これで、全ての処理が終わり!
##まとめ
最後にstyleタグも含めて掲載。
汚いコードであるのも承知してます・・・
<template>
<header>
<div class="btn" v-on:click="openModal()">
<i class="fas fa-bars"></i>
</div>
<transition name="fade">
<Modal v-if="showContent" v-on:close="closeModal()"></Modal>
</transition>
</header>
</template>
<script lang="ts">
import { defineComponent, ref } from "@vue/runtime-core";
import Modal from "./HeaderModal.vue";
export default defineComponent({
components: {
Modal,
},
setup() {
//モーダルクリックチェック
let showContent = ref(false);
/**
* モーダルウィンドウを表示する.
*/
let openModal = () => {
showContent.value = true;
};
/**
* モーダルウィンドを閉じる.
*/
let closeModal = () => {
showContent.value = false;
};
return { showContent, openModal, closeModal };
},
});
</script>
<style lang="scss">
header {
font-size: 2rem;
padding: 0.5rem 1rem;
width: 100%;
background-color: rgba(0, 0, 0, 0);
position: fixed;
box-sizing: border-box;
text-align: end;
z-index: 10;
}
// モーダルの出現スピード htmlの<transition > にて
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter-from, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
</style>
<template>
<div id="overlay" v-on:click="closeModal()">
<div id="content">
<img src="img/ariel.png" alt="アリエルの画像" />
<span>モーダルウィンドが<br />表示されました</span>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "@vue/runtime-core";
export default defineComponent({
setup(props, context) {
/**
* モーダルウィンドを閉じる.
*/
let closeModal = () => {
context.emit("close");
};
return {
closeModal,
};
},
});
</script>
<style lang="scss" scoped>
.sp-header-container {
width: 100%;
height: 100vh;
}
#overlay {
z-index: 1;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background-color: rgba(156, 174, 183, 0.3);
display: flex;
align-items: center;
justify-content: center;
transition-duration: 0.6s;
}
#content {
z-index: 2;
width: 55%;
padding: 2em;
margin: 0 auto;
background-color: rgba(0, 0, 0, 0);
text-align: center;
span {
display: block;
width: 100%;
font-weight: 900;
font-size: 3rem;
color: black;
}
img {
width: 20rem;
height: auto;
}
}
</style>
##最後に
こんなに最後まで読んでいただきありがとうございます。
Vue3系でのモーダルの記事があまりなかったので
忘備録して掲載してみました。
プログラミングの勉強始めたばかりで、
今回初めて掲載したため、説明の仕方、コード、やり方など
未熟なところばかりですが、これから勉強していきます。