KurocoとNuxt.jsで、フォーム画面を構築する
Kurocoを利用したNuxt.jsプロジェクトで、フォームの利用方法を紹介します。
このページは、KurocoとNuxt.jsでのプロジェクトが構築済みであることを前提としています。 まだ構築していない場合は、こちらを参照してください。
Kurocoにおけるフォームとは
Kurocoにおけるフォームとは、HTMLフォームの作成支援のためのデータの定義、自動返信の設定、ユーザーから送信されたフォーム用データの閲覧などができる機能です。
フォームの設定は、管理画面の[キャンペーン]の[フォーム]から管理/設定ができます。
詳細は下記を参照してください。
フォームは初期状態では動作せず、各種設定とフロントエンド側のコーディングが必要となります。 この記事では、KurocoとNuxt.jsでのプロジェクトの内容を変更してフォームを有効化するための手順を紹介します。
フォームのデータを作成する
フォームを作成する
まず、フォーム定義を作成します。 フォーム一覧画面より、[フォーム追加]をクリックします。
新規フォーム作成画面が表示されるので、フォーム基本設定を参考に、動作確認用のフォーム定義を入力します。今回は下記のように設定しました。
|項目 |値 |
| :--- | :--- |
|タイトル|テストフォーム|
|説明|説明です。
説明です。
説明です。|
|サンクス文言|サンクス文言です。
サンクス文言です。
サンクス文言です。|
最下部の[追加する]をクリックします。
自動でフォーム一覧画面に遷移し、入力したフォームが新規追加されていることが確認できます。
この画面に表示されているID
が、これから作成していく対象のフォームですので、メモをしておいてください。
ここではID: 3
となっていますが、この値は自動採番のため、値は環境によって異なります。この後の操作は、実際に画面に表示されたID
を使用してください。
ここまででフォーム定義の作成は完了しました。
フォームの設定を確認する
次に、先ほど作成したフォームの設定やデータを確認します。
「項目設定」の確認
フォーム一覧画面より、作成した[テストフォーム]をクリックします。
フォーム基本設定画面が表示されるので、[項目設定]タブをクリックします。
フォーム項目設定画面が表示されます。
この画面は、HTMLフォームの項目を定義するものです。
「name」「email」「message」は、Kurocoで必須の項目のためあらかじめ設定されています。
項目を追加したい場合は、ご自身で項目の定義を追加できます。
[message]以下に空白の項目が見えていますが、必要に応じて、ここにカスタム定義を追加していきます。
今回はデフォルトの項目のみでフォームを作成しますので、項目の設定はこのままにしておいてください。
「回答」の確認
続いて[回答]タブをクリックすると、ユーザーから送信されたフォームの回答データの一覧が表示されます。
ここにはHTMLフォームから送信された実際の回答が一覧で表示されます。
(現在はまだHTMLフォームが作成されておらず、データが送信されていないため、0件で表示されています)
回答内容などの詳細はこの画面や、右隣に隣接している[レポート]タブから確認できます。
フォーム用のAPIを作成する
次に、作成したフォーム定義に関するAPIを作成し、フロントエンドからフォーム機能を使用できるように設定をします。
API基本設定を行う
Kurocoの管理画面から[API]->[Default]をクリックします。
[追加]をクリックします。
タイトル、版、ディスクリプションを入力して[追加する]をクリックします。
追加したAPIに遷移しますので、続いて、セキュリティの設定をします。
[セキュリティ]をクリックします。
[Cookie]を選択して[保存する]をクリックします。
注意)
Cookieをセキュリティ用のトークンとして利用する場合、APIドメインとフロントエンドのドメインが違うとサードパティクッキーの問題があり、Safari等で認証が効きません。
フロントエンドとAPIドメインをサブドメイン違いで設定をする必要があるので、独自ドメイン/TLS証明書でAPIドメインを設定し、アカウント設定からAPIドメインを変更ください。
(Chromeでは正常に動作しますので、開発やテストの段階ではまずChromeで構築していただくことをお勧めします。)
CORS設定を行う
[CORSを設定する] をクリックします。
CORS_ALLOW_ORIGINSの [Add Origin] をクリックし、下記を追加します。
http://localhost:3000/
- フロントエンドドメイン
CORS_ALLOW_METHODSの [Add Method] をクリックし、下記を追加します。
- GET
- POST
- OPTIONS
CORS_ALLOW_CREDENTIALSの[Allow Credentials]にチェックが入っていることを確認します。
問題なければ [保存する] をクリックします。
エンドポイントを設定する
今回は下記2つのエンドポイントを作成します。
- フォーム定義を取得するエンドポイント:
(GET) form
- データを送信するエンドポイント:
(POST) form
フォーム定義を取得するエンドポイントを作成する
対象のAPI画面より [新しいエンドポイントの追加] をクリックます。
今回は下記設定にてエンドポイントを作成します。
ここでは簡易化のため、APIリクエスト制限はNoneを指定しています。
項目 | 設定内容 |
---|---|
パス | form |
カテゴリー | フォーム |
モデル | InquiryForm |
オペレーション | details |
APIリクエスト制限 | None |
[追加する]をクリックしてエンドポイントを作成します。
データを送信するエンドポイントを作成する
次に、HTMLから対象のフォームにデータを送信するためのエンドポイントを作成します。
対象のAPI画面より [新しいエンドポイントの追加] をクリックます。
今回は下記設定にてエンドポイントを作成します。
ここでも同じく、APIリクエスト制限はNoneを指定してください。
項目 | 設定内容 |
---|---|
パス | form |
カテゴリー | フォーム |
モデル | InquiryForm |
オペレーション | send |
APIリクエスト制限 | None |
[追加する]をクリックしてエンドポイントを作成します。
SwaggerUIで確認する
SwaggerUIにて、作成したフォーム定義を取得できるか確認します。 API画面より、[Swagger UI]をクリックします。
Swagger UI画面が表示されるので、先ほど作成したフォーム定義を取得するエンドポイントをクリックします。
[Try it out]をクリックします。
入力項目[inquiry_id]に、先ほど作成したフォームのIDを入力し(ここでは3
を入力しています)、[Execute]をクリックします。
作成したフォーム定義がJSON化されて表示されることが確認できます。
以上でフォーム用のエンドポイントが完成です。
ここまでで以下の2種類のエンドポイントができました。
- フォーム定義を取得するエンドポイント:
(GET) form
- フォームを送信するエンドポイント:
(POST) form
では実際にこれらのエンドポイントを使用して、HTML上からフォームの作成と送信ができるようにしていきます。
フロントエンドで、HTMLフォームを作成する
Nuxtインストールディレクトリに、下記構造のファイル追加作成します。
今回はpagesディレクトリ内にformディレクトリを作成し、formディレクトリ内に「index.vue」を作成します。
pages
- form
- index.vue
追加したファイルpages/form/index.vue
に下記を記載します。
<template>
<div>
<h1>FORMページ</h1>
<form v-if="!submitted" ref="form">
<div v-if="error" class="error">
<p v-for="(err, idx) in error" :key="idx">
{{ err }}
</p>
</div>
<div class="row--status">
<h2>フォーム名</h2>
<div>{{ name }}</div>
</div>
<div class="row--status">
<h2>説明</h2>
<div>
<p v-for="(line, idx) in textLines2texts(info)" :key="idx">
{{ line }}
</p>
</div>
</div>
<div class="row--status">
<h2>サンクス文言</h2>
<div>
<p v-for="(line, idx) in textLines2texts(thanksText)" :key="idx">
{{ line }}
</p>
</div>
</div>
<div class="row--status">
<h2>フォーム項目</h2>
<div class="row--internal">
<div v-for="col in cols" :key="col.key">
<p>[{{ col.title }}]</p>
<pre>{{ col }}</pre>
</div>
</div>
</div>
<div v-for="col in cols" :key="col.objKey" class="row--form">
<h2>[{{ col.title }}]</h2>
<input :name="col.objKey" type="text" />
</div>
<div class="row--bottom-next">
<button @click="handleOnSubmit">submit</button>
</div>
</form>
<form v-else>
<div class="row--status">
<h2>問い合わせID</h2>
<div>
{{ submittedId }}
</div>
</div>
<div class="row--status">
<h2>サンクス文言</h2>
<div>
<p v-for="(line, idx) in textLines2texts(thanksText)" :key="idx">
{{ line }}
</p>
</div>
</div>
<div class="row--bottom-back">
<button @click="handleOnBack">back</button>
</div>
</form>
</div>
</template>
<script>
const FORM_ID = 3; // 作成したフォーム定義のID
export default {
async asyncData({ $axios }) {
const response = await $axios.$get(`/rcms-api/8/form/${FORM_ID}`);
return {
name: response.details.inquiry_name,
info: response.details.inquiry_info,
thanksText: response.details.thanks_text,
cols: Object.entries(response.details.cols).map(([k, v]) => ({
objKey: k,
...v,
})),
};
},
data: () => {
return {
submitted: false,
submittedId: null,
error: null,
};
},
methods: {
textLines2texts(textLines = '') {
return textLines.split('\r\n');
},
async handleOnSubmit(e) {
e.preventDefault();
// collect input elements
const formInputElements = Array.from(this.$refs.form.elements).filter(
(elm) => elm.tagName.toLowerCase() === 'input'
);
// transform key:value inputs to an object
const body = formInputElements
.map((elm) => ({ [elm.name]: elm.value }))
.filter((inputObj) => Object.values(inputObj).every((v) => v !== ''))
.reduce((prev, cur) => ({ ...prev, ...cur }), {});
try {
// post data
const { id } = await this.$axios.$post(
`/rcms-api/8/form?id=${FORM_ID}`,
body
);
this.error = null;
this.submittedId = id;
this.submitted = true;
} catch (e) {
this.error = [`${e}`, ...e.response.data.errors];
}
},
handleOnBack(e) {
e.preventDefault();
this.submitted = false;
},
},
};
</script>
<style>
body,
h2 {
margin: 0;
}
input {
width: 100%;
border: none;
}
.error {
color: red;
}
.error > *:first-child {
font-weight: bold;
}
.row--status {
display: flex;
border-top: 1px solid black;
}
.row--status > *:first-child {
background-color: yellow;
min-width: 15rem;
max-width: 15rem;
border-right: 1px solid black;
}
.row--form {
display: flex;
border-top: 1px solid black;
}
.row--form > *:first-child {
background-color: aquamarine;
min-width: 15rem;
max-width: 15rem;
border-right: 1px solid black;
}
.row--bottom-next {
padding: 8px 16px;
display: flex;
justify-content: flex-end;
}
.row--bottom-back {
padding: 8px 16px;
display: flex;
justify-content: flex-start;
}
.row--internal {
display: flex;
}
form > *:nth-last-child(2) {
border-bottom: 1px solid black;
}
</style>
const FORM_ID = 3
の箇所は、ご自身の作成したフォーム定義のIDをご記入ください。
/rcms-api/8/form/${FORM_ID}
、/rcms-api/8/form?id=${FORM_ID}
の箇所は、Kuroco管理画面に記載のパスをご記入ください。
ブラウザで確認する
次に、先ほど作成したファイルをブラウザで確認します。
ローカルサーバーが停止している場合はnpm run dev
を実行し、http://localhost:3000/form
にアクセスします。
すると、下記画面が表示されます。
ここで表示されている表のうち、黄色の行については、取得したフォーム定義より抜粋したデータを表示しています。
データ内容 | アクセス方法 |
---|---|
フォーム名 | (response).details.inquiry_name |
説明 | (response).details.inquiry_info |
サンクス文言 | (response).details.thanks_text |
フォーム項目 | (response).details.cols |
ソースコード内「asyncData()
」にあるように、上記の対応表通りにフォーム定義を取得できていることが確認できます。
フォーム項目は、[カテゴリ]以外のデフォルト項目がオブジェクト形式で返却されています。
より詳細な情報を閲覧したい場合には、Chromeの開発者コンソールから、[Network]タブで実際にKurocoと通信している内容をご確認ください。
データを送信する
次に、取得したデータ定義を元に実際にHTMLフォームからデータを送信します。
画面に表示している表の緑色の行は入力項目(HTMLフォーム)になっています。
各項目に下記のように値を入力します。
項目名 | 入力値 |
---|---|
[name] | テスト名 |
[email] | test@example.com |
[message] | テストメッセージ |
[submit]ボタンをクリックすると、データが送信され次の画面に遷移します。
ソースコード内「handleOnSubmit()
」にあるように、この操作によって、[フォーム項目]のデータをKurocoに送信しました。
データをKurocoで確認する。
次に、送信したデータをKurocoで確認します。
フォーム一覧画面より、作成した[テストフォーム]をクリックします。
[回答]タブをクリックして、回答の一覧を表示すると、データが1件存在することを確認できます。
[No.]をクリック(ここでは14
)すると、回答内容の詳細を確認できます。
回答の詳細に、HTMLフォームから送信された値が含まれていることが確認できます。
フォームのバリデーションを確認する
最後に、フォームのバリデーションを確認します。
先ほど作成したフロントの画面を再度表示し、[email]の項目に無効なメールアドレスを入力して、エラーが返却されることを確認します。
表の緑色の各行に以下を入力します。
([email]の値は無効なメールアドレスですので、エラーとなることが望ましいです)
|項目名|入力値|
| :--- | :--- |
|[name]|バリデーションエラー名|
|[email]|mail|
|[message]|バリデーションエラーメッセージ|
[submit]をクリックすると、画面上部にエラーメッセージが表示されます。
このように、Kurocoにはサーバー側のバリデーションがあることを確認できます。
エラー時はHTTPのステータスコードでのエラーと共に、レスポンスボディ内にエラー内容が返却されてきます。
ソースコード内にある通り、エラー時のハンドリングを実装したい場合には、フロント側でその実装をする必要があります。
動的なバリデーションが必要な場合は、Kurocoから取得してきた値を元に実装が必要になります。
カスタム項目を追加する
デフォルトのフォーム項目以外に、自由にカスタム項目の定義/作成が可能です。
カスタム項目を作成する場合の手順を確認します。
カスタム項目追加手順
フォーム一覧画面より、作成した[テストフォーム]をクリックします。
[項目設定]タブをクリックして、フォーム項目設定画面を表示します。
改行なしの短文(テキストボックス)を追加する。
改行なしの短文(テキストボックス)の追加方法を記載します。
項目名 | 入力値 |
---|---|
タイトル | item |
必須属性 | 任意 |
回答形態/入力制限 | 改行なしの短文(テキストボックス) |
選択項目の設定 | (なし) |
上記表にあるように、[必須属性]、[選択項目の設定]にてバリデーション用の属性を設定し、フォーム項目の属性としてその入力制限の内容を返却させるように設定も可能です。
設定したら、画面下部の[更新する]をクリックしてください。
先ほど作成したフロントの画面を再度表示し、Kurocoから取得するフォーム定義が一部変更になっていることを確認します。 [フォーム項目]に[item]が追加となっていることが確認できます。
では実際にフォームデータを送信し、新しい[item]項目がKurocoに送信されることを確認します。 フロント画面の表の緑色の各行に以下を入力します。
項目名 | 入力値 |
---|---|
[name] | 追加項目名 |
[item] | 追加項目アイテム |
[email] | test@example.com |
[message] | 追加項目メッセージ |
[submit]をクリックしてデータを送信します。
管理画面の[フォーム]から[回答]タブをクリックして回答の一覧を表示し、
データが一件追加されていることを確認します。
[No.]をクリック(ここでは15
)して詳細を表示すると、新規追加した項目[item]と、その項目に入力した値が適用されていることが確認できます。
日付の入力項目を追加する
カスタム項目のうち、日付の入力項目を追加してフォームを確認します。
日付の入力項目をフォーム定義に追加する
既存で作成したフォーム定義の[項目設定]タブをクリックして、フォーム項目設定画面を表示します。
ここに日付のinput項目を追加定義します。
項目名 | 入力値 |
---|---|
タイトル | date |
必須属性 | 任意 |
回答形態/入力制限 | 日付フォーマット |
選択項目の設定 | (なし) |
上記表にあるように、[必須属性]、[選択項目の設定]にてバリデーション用の属性を設定し、フォーム項目の属性としてその入力制限の内容を返却させるような設定も可能です。
設定したら、画面下部の[更新する]をクリックしてください。
フロントの画面を表示し、Kurocoから取得するフォーム定義が変更されていることを確認します。
では実際にフォームデータを送信し、新しい[date]項目がKurocoに送信されることを確認します。 フロント画面の表の緑色の各行に以下を入力します。
項目名 | 入力値 |
---|---|
[name] | 日付項目名 |
[item] | 空欄 |
[date] | 1900/01/01 |
[email] | test@example.com |
[message] | 日付項目メッセージ |
[submit]をクリックしてデータを送信します。
管理画面の[フォーム]から[回答]タブをクリックして回答の一覧を表示し、
追加されたデータが送信した内容と一致していることを確認します。
日付の入力項目にバリデーションを設定する
日付にはKuroco側でのバリデーションを設定できます。
今回は一例として、今現在から10年前までを許容範囲とするバリデーションを設定します。
作成したフォーム定義の[項目設定]から、フォーム項目設定画面を表示します。
[設定]をクリックします。
設定画面にて、開始までの期間
に-10 years
を入力して設定、適用します。
項目設定については、フォーム定義で利用できるフォーム項目一覧 -> 日付フォーマットを参照してください。
これでバリデーションが設定されましたので、10年前より以前の日付はエラーとなります。
フロントエンドのフォーム画面から先ほどと同様に1900/01/01
をリクエストして、失敗することを確認します。
先ほどのフォーム画面にて、下記を送信します。
項目名 | 入力値 |
---|---|
[name] | 日付項目名 |
[item] | 空欄 |
[date] | 1900/01/01 |
[email] | test@example.com |
[message] | 日付項目メッセージ |
エラーがレスポンスされることを画面上部のエラーメッセージから確認します。
(エラーとなった場合は画面上部にエラー内容を表示するようにしてありますので、表示範囲外の場合は、上部までスクロールしてください)
では、10年前以降の日付を送信して、データが正常に処理されることを確認します。
[date]の内容を10年前以降となるとうな日付を入力し直して再度フォームを送信します。
(今回は2022/04/01
としています)
データが送信され、完了画面に画面遷移することを確認します。
ファイルの入力項目を追加する
カスタム項目のうち、ファイルの入力項目を追加してフォームを確認します。
ファイルの入力項目をフォーム定義に追加する
既存で作成したフォーム定義の[項目設定]タブをクリックして、フォーム項目設定画面を表示します。
ここにファイルのinput項目を追加定義します。
項目名 | 入力値 |
---|---|
タイトル | file |
必須属性 | 任意 |
回答形態/入力制限 | ファイル(KurocoFiles) |
拡張子の設定 | (なし) |
[必須属性]、[拡張子の設定]にてバリデーション用の属性を設定し、フォーム項目の属性としてその入力制限の内容を返却させるような設定も可能です。
設定したら、画面下部の[更新する]をクリックしてください。
フロントの画面を表示し、Kurocoから取得するフォーム定義が変更されていることを確認します。
アップロード用のエンドポイントを作成する
ファイルアップロードの実装は下記の流れとなります。
- Kurocoへファイルをアップロードする
- アップロードしたファイルIDを送信する
そのため、まずはアップロードの受け口となるエンドポイントを追加します。
[API]より対象のAPIを選択し、API管理画面より [新しいエンドポイントの追加] をクリックます。
今回は下記設定にてエンドポイントを作成します。
項目 | 設定内容 |
---|---|
パス | file |
カテゴリー | ファイル |
モデル | Files |
オペレーション | upload |
APIリクエスト制限 | None |
簡易化のため、APIリクエスト制限はNoneを指定しています。
SwaggerUIで確認する
SwaggerUIにて、作成したフォーム定義を取得できるか確認します。 API画面より、[Swagger UI]をクリックします。
Swagger UI画面が表示されるので、先ほど作成したエンドポイントをクリックします。
[Try it out]をクリックします。
入力項目[file]に、サンプルのファイルを選択し、[Execute]をクリックします。ここではkuroco.png
というロゴ画像を選択しました。
ファイルがアップロードされ、一時領域にファイルが格納されます。
file_id
には、ファイルの格納先が表示されます。
なお、file_id
にブラウザでアクセスすると、アップロードした画像が表示され、ファイルが格納されていることが確認できます。
確認先のURLは、管理画面のホスト名 + file_id
です。
以上でフォーム用のエンドポイントが完成しました。
フォームを修正してファイルを送信する。
次に、pages/form/index.vue
を変更します。
ファイルの拡張項目に対応する入力項目には、ファイル入力を受付可能とさせるように変更します。
<div v-for="col in cols" :key="col.objKey" class="row--form">
<h2>[{{ col.title }}]</h2>
- <input :name="col.objKey" type="text" />
+ <input
+ v-if="col.title === 'file'"
+ :name="col.objKey"
+ type="file"
+ @change="uploadFile"
+ />
+ <input v-else :name="col.objKey" type="text" />
</div>
<div class="row--bottom-next">
ファイルを入力すると同時にアップロード用エンドポイントにファイルを送信し、レスポンスのfile_id
を保存するようにします。
data: () => {
return {
submitted: false,
submittedId: null,
error: null,
+ file_id: null,
};
},
methods: {
+ async uploadFile(e) {
+ const fm = new FormData();
+ fm.append('file', e.target.files[0]);
+
+ const { file_id } = await this.$axios.$post(`/rcms-api/8/file`, fm, {
+ headers: {
+ 'Content-Type': 'multipart/form-data', // required to post file as a binary
+ },
+ });
+ this.file_id = file_id;
+ },
textLines2texts(textLines = '') {
return textLines.split('\r\n');
},
送信するデータのうち、ファイルの拡張項目のデータをfile_id
に手動設定します。
.filter((inputObj) => Object.values(inputObj).every((v) => v !== ''))
.reduce((prev, cur) => ({ ...prev, ...cur }), {});
+ // apply file_id instead of the actual file input value
+ if (body.ext_03) {
+ body.ext_03 = {
+ file_id: this.file_id,
+ };
+ }
+
try {
// post data
では実際に画像のアップロードとフォームデータ送信し、新しい[file]項目がKurocoに送信されることを確認します。 フロント画面の表の緑色の各行に以下を入力します。
項目名 | 入力値 |
---|---|
[name] | ファイル項目名 |
[item] | 空欄 |
[date] | 空欄 |
[file] | 画像ファイル |
[email] | test@example.com |
[message] | ファイル項目メッセージ |
[submit]をクリックしてデータを送信します。
管理画面の[フォーム]から[回答]タブをクリックして回答の一覧を表示し、データが一件追加されていることを確認します。
[No.]をクリック(ここでは18
)して詳細を表示すると、新規追加した項目[file]と、その項目に入力した値が適用されていることが確認できます。
フロントエンドにおける、拡張項目のバリデーションについて
Kuroco側のバリデーションはあくまでバックエンド側のバリデーションです。
フロントエンドでのバリデーションをさらに実施されたい場合には、ご自身で実装していただく必要があります。
例として、日付の拡張項目を設定した場合における、フロントエンドでの簡単なバリデーションを追加実装します。
Kurocoは設定したバリデーションの内容を、GETリクエストでのフォーム内容取得時に返します。
フロントエンドのフォーム画面を確認すると、取得したGETレスポンス内に、[options]という設定したバリデーションが返却されていることを確認します。
この値を、HTMLのinputにmin
として設定します。
まず、バリデータの値はPHPのstrtotime()
互換のものであるため、javascriptにてこれを動作させるライブラリをインストールします。
まずはターミナルから下記を実行してください。
npm install locutus
次に、pages/form/index.vue
を変更します。
ライブラリをコンポーネント内にインポート(使用可能に)します。
</template>
<script>
+import strtotime from 'locutus/php/datetime/strtotime';
+
const FORM_ID = 3; // 作成したフォーム定義のID
export default {
strtotime()
の配置と、これを使ったパーサをメソッドへ実装します。
},
methods: {
+ strtotime,
+ getMin(startPeriodStr) {
+ const minDateNum = strtotime(startPeriodStr) * 1000; // to millisec
+ return new Date(minDateNum).toJSON().split('T')[0]; // to YYYY-MM-DD
+ },
async uploadFile(e) {
const fm = new FormData();
fm.append('file', e.target.files[0]);
テンプレート(HTML)の内容を変更します。
type="file"
@change="uploadFile"
/>
+ <input
+ v-else-if="col.title === 'date'"
+ :name="col.objKey"
+ type="date"
+ :min="
+ getMin(col.options.find(({ key }) => key === 'minPeriod').value)
+ "
+ />
<input v-else :name="col.objKey" type="text" />
</div>
実装後画面を再表示し、該当の入力箇所を選択して、指定された日付以前の値が指定できなくなっていることを確認します。
サポート
お探しのページは見つかりましたか?解決しない場合は、問い合わせフォームからお問い合わせいただくか、Slackコミュニティにご参加ください。