今回達成すること
- 検索結果を表示するページファイルと、
- ページタイトルを表示するコンポーネントファイルを作成します。
検索ページを作成する
「pages」ディレクトリに、検索結果を表示する
% touch pages/search.vue
レイアウトファイルの指定とページタイトルを用意します。
pages/search.vue
<template>
<v-container>
<v-card-title
class="text-h5 font-weight-bold"
>
{{ pageTitle }}
</v-card-title>
</v-container>
</template>
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
@Component
export default class SearchPage extends Vue {
layout (): string {
return 'app'
}
pageTitle: string = this.$my.routePageTitle(this.$route.name)
}
</script>
日本語翻訳データを用意する
検索ページのタイトルを
locales/ja.json
{
"pages": {
"privacy": "プライバシーポリシー",
// 追加
"search": "サイト内検索"
}
}
Nuxtサーバーを再起動しましょう。
% yarn dev
検索ページを確認しよう
前回作成した検索フォームに適当な文字列を入力し、エンターキーを押しましょう。
そうすると、/search
ページに遷移します。
URLにクエリ?q=
が付いていることも確認してください。
無事ページタイトルが日本語化されました。
ページタイトルをコンポーネント化する
ページタイトルのスタイリングは
<v-card-title
class="text-h5 font-weight-bold"
>
{{ pageTitle }}
</v-card-title>
そこで、このUIをコンポーネント化します。
UIコンポーネントファイルを作成する
「components/Ui」ディレクトリに
% touch components/Ui/UiPageTitle.vue
Vueのprops
を使用し、title
とsubTitle
を受け取ります。
components/Ui/UiPageTitle.vue
<template>
<v-card
flat
tile
color="transparent"
>
<v-card-title
class="text-h5 font-weight-bold"
>
{{ title }}
</v-card-title>
<v-card-text
v-if="subTitle"
class="font-weight-bold"
>
{{ subTitle }}
</v-card-text>
</v-card>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'nuxt-property-decorator'
@Component
export default class UiPageTitleComponent extends Vue {
@Prop({
type: String,
required: true
})
title!: string
@Prop({
type: String,
default: ''
})
subTitle?: string
}
</script>
<v-card>
flat
... Boxに影をつけない。tile
... 角張ったBoxにする。color="transparent"
...background-color
を透明に。
VueのpropsとTypeScriptの書き方
Vueのprops
はnuxt-property-decorator
を使用すると@Prop({ <オプション> })
に書き変わります。
必須のバインドキーの書き方
コンポーネント必須のバインドキーは、
@Prop
=>required
プロパティをtrue
に、title!
=> 変数にはエクスクラメーションマーク(!
)を付け、null
、undefined
にならないことを宣言します。
props(受け取り側)
@Prop({
type: String,
required: true
})
title!: string
required: true
を宣言すると、コンポーネントにそのバインドキーがない場合にエラーとなります。
v-bind(送信側)
<!-- titleキー必須 -->
<ui-page-title
:title="hoge"
/>
必須ではないバインドキーの書き方
必須ではないバンドキーは、
subTitle?
=> クエスチョンマークを付けて変数を宣言します。@prop
=>default
プロパティは無くても構いません。今回は明示的にtype
と一致させて初期値を代入しています。
props(受け取り側)
@Prop({
type: String,
default: ''
})
subTitle?: string
変数に?
を付与しないと、「コンストラクターで確実に代入されません。」というTypeScriptのエラーが出ます。
/*
TS Error
Property 'subTitle' has no initializer and is not definitely assigned in the constructor.
*/
@Prop({
type: String,
default: ''
})
subTitle: string
また変数に直接初期値を代入すると、今度はバインドされたデータを受け取ることができず、Vueがエラーを吐きます。
/*
Vue Error
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
Instead, use a data or computed property based on the prop's value. Prop being mutated: "subTitle"
*/
@Prop({
type: String
})
subTitle: string = ''
props
の書き方には気をつけてください。
ページタイトルコンポーネントに書き換える
search.vue privacy.vue
上記2つのページファイルのタイトルを<ui-page-title>
コンポーネントに書き換えます。
pages/search.vue
<template>
<v-container>
<ui-page-title
:title="pageTitle"
/>
</v-container>
</template>
プライバシーポリシーページはサブタイトルを変数で用意します。
pages/privacy.vue
<template>
<v-container>
<!-- 追加 -->
<ui-page-title
:title="pageTitle"
:sub-title="pageSubTitle"
/>
<!-- 削除 -->
<!-- <v-card
flat
color="transparent"
>
<v-card-title
class="text-h5 font-weight-bold"
>
{{ pageTitle }}
</v-card-title>
<v-card-text
class="font-weight-bold"
>
{{ $config.appName }}(以下、当サイト)では、個人情報の保護およびその適切な取り扱いについて、以下のとおりプライバシーポリシー(以下、本ポリシー)を定めております。
</v-card-text>
</v-card> -->
...
</template>
<script lang="ts">
...
@Component
export default class PrivacyPage extends Vue {
...
pageTitle: string = this.$my.routePageTitle(this.$route.name)
// 追加
pageSubTitle: string = `${this.$config.appName}(以下、当サイト)では、個人情報の保護およびその適切な取り扱いについて、以下のとおりプライバシーポリシー(以下、本ポリシー)を定めております。`
}
</script>
それぞれのページにアクセスして表示が正しいか確認してください。
今回の作業は以上です。
% git add -A
% git commit -m "Add search page and page title component"
まとめと次回
今回は検索ページとその中で使用するページタイトルコンポーネントを作成しました。
次回はContentfulにAPIリクエストを行うFunctionsプロジェクトを作成し、検索機能を完成させます。