Vue.jsで、親コンポーネントに埋め込むために作った子コンポーネントを、親コンポーネントの外側で使いたいことがあります。HTML上で離れたところに置く必要があるときです。
子コンポーネントの例:エラーメッセージ
例として、フォームの送信時に使うようなエラーメッセージのコンポーネントを作りました。
props に title と messages を指定せずに、オブジェクト errors を使っているのは、外側から操作しやすいようにするためです。
<template>
<div>
<div class="alert alert-danger" role="alert" v-if="title || messages.length">
<div v-if="title">{{title}}</div>
<ul v-if="messages.length" class="mb-0">
<li v-for="(message, idx) in messages" :key="idx">{{message}}</li>
</ul>
</div>
</div>
</template>
<script>
export default {
props: { errors: Object },
computed: {
title() {
return this.errors.title;
},
messages() {
return this.errors.messages || [];
}
}
}
</script>
普通の使い方
まず、普通に親コンポーネントの中に子コンポーネント(エラーメッセージ)を置く使い方です。
<div id="sample1"></div>
import Vue from 'vue';
import Sample1 from './sample1.vue';
document.addEventListener('DOMContentLoaded', () => {
new Vue(Sample1).$mount('#sample1');
});
<template>
<div>
<alert :errors="errors"></alert>
<p><input type="email" v-model="email" class="form-control" /></p>
<p><input type="button" value="送信" @click="submit" class="btn btn-primary" /></p>
</div>
</template>
<script>
import Alert from './alert.vue';
export default {
components: { 'alert': Alert },
data() {
return {
email: '',
errors: {
title: null,
messages: []
}
};
},
methods: {
submit() {
this.errors.title = '保存に失敗しました。';
this.errors.messages = ['メールアドレスが不正です。'];
}
}
}
</script>
外に置くときの使い方
次は、子コンポーネント(エラーメッセージ)を親コンポーネントの外に置くときの使い方です。props 用のオブジェクト errors を作って2つのコンポーネントで共有します。
キモは、Vue.observable を使ってオブジェクトをリアクティブにすることです。observable
を使わないと title や messages の変化がエラーメッセージに伝わりません。
<div id="alert"></div>
<div id="sample2"></div>
import Vue from 'vue';
import Alert from './alert.vue';
import Sample2 from './sample2.vue';
document.addEventListener('DOMContentLoaded', () => {
const errors = Vue.observable({ title: '', messages: [] });
new Vue({
render: h => h(Alert, { props: { errors: errors } })
}).$mount('#alert');
new Vue({
render: h => h(Sample2, { props: { errors: errors } })
}).$mount('#sample2');
});
<template>
<div>
<p><input type="email" v-model="email" class="form-control" /></p>
<p><input type="button" value="送信" @click="submit" class="btn btn-primary" /></p>
</div>
</template>
<script>
export default {
props: { errors: Object },
data() {
return {
email: ''
};
},
methods: {
submit() {
this.errors.title = '保存に失敗しました。';
this.errors.messages = ['メールアドレスが不正です。'];
}
}
}
</script>
なお、Vueのバージョン3では、observable
の代わりに reactive
を使います。
import { createApp, reactive, h } from 'vue';
import Alert from './alert.vue';
import Sample2 from './sample2.vue';
document.addEventListener('DOMContentLoaded', () => {
const errors = reactive({ title: '', messages: [] });
createApp({
render() { return h(Alert, { errors: errors }); }
}).mount('#alert');
createApp({
render() { return h(Sample2, { errors: errors }); }
}).mount('#sample2');
});
親コンポーネントなしで使う
observable
または reactive
を使うと、親コンポーネントなしでエラーメッセージを使うこともできます。次は、jQueryを使った例です。
<div id="alert"></div>
<div>
<p><input type="email" v-model="email" class="form-control" /></p>
<p><input type="button" value="送信" id="submit" class="btn btn-primary" /></p>
</div>
import Vue from 'vue';
import $ from 'jquery';
import Alert from './alert.vue';
document.addEventListener('DOMContentLoaded', () => {
const errors = Vue.observable({ title: '', messages: [] });
new Vue({
render: h => h(Alert, { props: { errors: errors } })
}).$mount('#alert');
$('#submit').on('click', (e) => {
errors.title = '保存に失敗しました。';
errors.messages = ['メールアドレスが不正です。'];
});
});