見出し画像

【Vuetify3】v-date-inputとv-date-pickerを比較してみた

こんにちは!
株式会社SHIFTのsuzueです。

今回は、Vuetify3の日付入力コンポーネントについて、実際に実装し、比較をします。


背景


アカウント登録などで、生年月日を入力するケースはよくあります。
その際、下記のように入力フォームからカレンダーが出現し、日付を選択して入力するタイプのUIを見かけると思います。Vue3 & Vuetify3ではこのような実装する際に、大まかに以下の二つのやり方があります。

  1. v-date-picker、v-menu、v-text-fieldコンポーネントを組み合わせて実装する方法

  2. v-date-inputコンポーネントを使用する方法

前者はVuetify2のときでも活用され、Vuetify3となった今でも同様に実装することができます。多少ソースコードがややこしくなりますが、別々のコンポーネントを使用しているため、カスタマイズ性が高く、自分の意図に沿ったUIの作成がしやすいです。

対してv-date-inputは、現在Vuetify3 labsにある未完成のコンポーネントであり、まだ正式なコンポーネント化はされていません (2024年10月21日)。こちらは日付入力機能に特化したコンポーネントとなっており、コンポーネント内ですでにv-date-picker、v-text-fieldが組み合わされているため、非常に簡単なソースコードで下記のような日付入力が実現できます。

画像より、Select Dateとある上側がv-date-picker + そのほか組み合わせによる実装。 Date Inputとある下側がv-date-inputによる実装となります。

今回の記事は上記で説明した、コンポーネントの組み合わせと、v-date-inputの二つの日付入力の実装方法を、ソースコードと特徴をそれぞれ比較していきます。

想定する読者の技術レベル
Vuetifyのコンポーネントを使用したことがある

使用したサンプルコード


サンプルコード

<template>
	<v-container>
		<v-menu v-model="menu" :close-on-content-click="false">
			<template v-slot:activator="{ props }">
				<v-text-field
					v-model="formattedDate"
					label="Select Date"
					prepend-icon="mdi-calendar"
					v-bind="props"
				></v-text-field>
			</template>
			<v-locale-provider locale="ja">
				<v-date-picker v-model="dateForPicker" color="primary" title="日付選択" header="日付">
					<template v-slot:actions>
						<v-btn @click="menu = false">cancel</v-btn>
						<v-btn @click="saveDate">OK</v-btn>
					</template>
				</v-date-picker>
			</v-locale-provider>
		</v-menu>
	</v-container>
	<v-container>
		<v-locale-provider locale="ja">
			<v-date-input
				label="Date input"
				v-model="dataForInput"
				placeholder=""
				color="primary"
				title="日付選択"
				header="日付"
				:hide-header="false"
			></v-date-input>
		</v-locale-provider>
	</v-container>
</template>

<script>
export default {
	data() {
		return {
			menu: false,
			dateForPicker: null,
			dataForInput: null,
			formattedDate: ''
		};
	},
	methods: {
		saveDate() {
			this.formattedDate = this.formatDate(this.dateForPicker);
			this.menu = false;
		},
		formatDate(dateForPicker) {
			if (!dateForPicker) return '';
			const date = new Date(dateForPicker);
			const year = date.getFullYear();
			const month = String(date.getMonth() + 1).padStart(2, '0');
			const day = String(date.getDate()).padStart(2, '0');
			return `${year}/${month}/${day}`;
		}
	}
};
</script>

コンポーネントの組み合わせによる日付入力と、v-date-inputコンポーネントによる日付入力はサンプルコードのv-containerを境に実装が分けられています。 両者に共通して、v-locale-providerコンポーネントがv-date-picker、v-date-inputそれぞれの手前についています。
こちらは、表示するカレンダーの言語を設定しており、locale="ja"とpropsを指定することで、日本語の表示に変更しています。デフォルトは英語です。

サンプルコードの解説


v-date-picker + 他コンポーネントの組み合わせ

v-date-picker、v-menu、v-text-fieldを用いた、日付入力はサンプルコードの内、以下の箇所となります。また、主要となる3つのコンポーネントの働きに関してを表でまとめました

<template>
    <v-container>
		<v-menu v-model="menu" :close-on-content-click="false">
			<template v-slot:activator="{ props }">
				<v-text-field
					v-model="formattedDate"
					label="Select Date"
					prepend-icon="mdi-calendar"
					v-bind="props"
				></v-text-field>
			</template>
			<v-locale-provider locale="ja">
				<v-date-picker v-model="dateForPicker" color="primary" title="日付選択" header="日付">
					<template v-slot:actions>
						<v-btn @click="menu = false">cancel</v-btn>
						<v-btn @click="saveDate">OK</v-btn>
					</template>
				</v-date-picker>
			</v-locale-provider>
		</v-menu>
	</v-container>
</template>

<script>
export default {
	data() {
		return {
			menu: false,
			dateForPicker: null,
			formattedDate: ''
		};
	},
	methods: {
		saveDate() {
			this.formattedDate = this.formatDate(this.dateForPicker);
			this.menu = false;
		},
		formatDate(dateForPicker) {
			if (!dateForPicker) return '';
			const date = new Date(dateForPicker);
			const year = date.getFullYear();
			const month = String(date.getMonth() + 1).padStart(2, '0');
			const day = String(date.getDate()).padStart(2, '0');
			return `${year}/${month}/${day}`;
		}
	}
};
</script>

それぞれのコンポーネントで取得できる値は、v-modelによってバインドされています。

v-date-input

このコンポーネントは、Vuetify3 ドキュメント より、テキストフィールドとdatepickerを組み合わせたものとなっており、このコンポーネント一つで、日付入力フォームを実装することができます。

実装は以下のようになります。

<template>
	<v-container>
		<v-locale-provider locale="ja">
			<v-date-input
				label="Date input"
				v-model="dataForInput"
				placeholder=""
				color="primary"
				title="日付選択"
				header="日付"
				:hide-header="false"
			></v-date-input>
		</v-locale-provider>
	</v-container>
</template>

<script>
export default {
	data() {
		return {
			dataForInput: null
		};
	}
};
</script>

このコンポーネントはpropsとして、v-text-field、v-date-pickerのpropsの多くを使用することができるため、カスタマイズが可能です。

比較


v-date-pickerを初めてする複数のコンポーネントの組み合わせと、v-date-inputコンポーネント特徴を比較したものを下記に示します。

特に以下の3点に注目して比較します

  • ソースコード量

  • 日付フォーマット

  • カスタマイズ性

ソースコード量

<template>
    <v-container>
		<v-menu v-model="menu" :close-on-content-click="false">
			<template v-slot:activator="{ props }">
				<v-text-field
					v-model="formattedDate"
					label="Select Date"
					prepend-icon="mdi-calendar"
					v-bind="props"
				></v-text-field>
			</template>
			<v-locale-provider locale="ja">
				<v-date-picker v-model="dateForPicker" color="primary" title="日付選択" header="日付">
					<template v-slot:actions>
						<v-btn @click="menu = false">cancel</v-btn>
						<v-btn @click="saveDate">OK</v-btn>
					</template>
				</v-date-picker>
			</v-locale-provider>
		</v-menu>
	</v-container>
</template>

<script>
export default {
	data() {
		return {
			menu: false,
			dateForPicker: null,
			formattedDate: ''
		};
	},
	methods: {
		saveDate() {
			this.formattedDate = this.formatDate(this.dateForPicker);
			this.menu = false;
		},
		formatDate(dateForPicker) {
			if (!dateForPicker) return '';
			const date = new Date(dateForPicker);
			const year = date.getFullYear();
			const month = String(date.getMonth() + 1).padStart(2, '0');
			const day = String(date.getDate()).padStart(2, '0');
			return `${year}/${month}/${day}`;
		}
	}
};
</script>
<template>
	<v-container>
		<v-locale-provider locale="ja">
			<v-date-input
				label="Date input"
				v-model="dataForInput"
				placeholder=""
				color="primary"
				title="日付選択"
				header="日付"
				:hide-header="false"
			></v-date-input>
		</v-locale-provider>
	</v-container>
</template>

<script>
export default {
	data() {
		return {
			dataForInput: null
		};
	}
};
</script>

両者を比較すると、v-date-inputは非常にコード量が少なく、シンプルに実装できることがわかります。 可読性も高く、propsのみでカスタマイズしているため、どのプロパティをどう設定しているかがわかりやすくなっています。

日付フォーマット

v-date-picker + 組み合わせの実装においては、下記のように関数でフォーマットを指定しており、自由度高めに変更ができます

ormatDate(dateForPicker) {
			if (!dateForPicker) return '';
			const date = new Date(dateForPicker);
			const year = date.getFullYear();
			const month = String(date.getMonth() + 1).padStart(2, '0');
			const day = String(date.getDate()).padStart(2, '0');
			return `${year}/${month}/${day}`;
}
formatDate(dateForPicker) {
			if (!dateForPicker) return '';
			const date = new Date(dateForPicker);
			const year = date.getFullYear();
			const month = String(date.getMonth() + 1).padStart(2, '0');
			const day = String(date.getDate()).padStart(2, '0');
			return `${year}-${month}-${day}`;
}

カスタマイズ性

v-date-inputコンポーネントはv-text-field、v-date-pickerのpropsが活用できるため、ある程度カスタマイズすることができます。ただし、現状はv-text-fieldから入出力することを前提に設計されているため、v-date-pickerとその他コンポーネントの組み合わせによって日付入力するやり方に比べてカスタマイズ性は劣ります。

例えば、組み合わせによる日付入力の場合、下記のようにアイコンのみから日付が選択できるようにすることもできます。このように、v-date-inputでは表現できないUIであっても、組み合わせによっては実装できます。

おわりに


本記事では、日付入力の実装に関して、Vuetify3で新たに実装が検討されているv-date-inputコンポーネントと、これまでのv-date-pickerとv-text-fieldなどの組み合わせによる実装方法の違いと筆者視点の比較を行いました。 個人的にはv-date-inputが非常に使いやすいなと感じたため、個人開発をするさいは、使用していきたいなと思いました。

また、v-date-inputコンポーネントは現在labsにある未完成のコンポーネントのため、機能リクエスト であったり、バグレポート があり、日々更新されています。今できないことも今後できるようになる可能性があるため、使用する場合は情報を日々追うことを推奨いたします。

参考サイト


Vuetify3 v-date-picker 公式ドキュメント:https://vuetifyjs.com/ja/components/date-pickers/#section-4f7f304465b9
Vuetify3 v-menu 公式ドキュメント:https://vuetifyjs.com/ja/components/menus/
Vuetify3 v-text-field 公式ドキュメント:https://vuetifyjs.com/ja/components/text-fields/
Vuetify3 v-date-input 公式ドキュメント:https://vuetifyjs.com/ja/components/date-inputs/
GitHub v-date-input 機能リクエスト:https://github.com/vuetifyjs/vuetify/issues/19996
GitHub v-date-input バグレポート:https://github.com/vuetifyjs/vuetify/issues/19803


執筆者プロフィール:suzue
株式会社 SHIFT アジャイルサービス部所属。現在はサービス改革部でTech Boostプロジェクトに参画中。

お問合せはお気軽に

SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/

SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/

SHIFTの導入事例
https://service.shiftinc.jp/case/

お役立ち資料はこちら
https://service.shiftinc.jp/resources/

SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/

PHOTO:UnsplashGoran Ivos