webアプリを開発していくと、新たな機能を後になってから付加したいことがたくさんあります。
現在DevOpsで運用しているタスク管理アプリでは、タスクの詳細画面をモーダル(ダイアログ)で開く設計にしています。
そのため、タスク詳細を開いてもURLは変化せず、固有のパスを持っていない状態でした。
最近になって、タスクの変更をメールで通知する機能を付与したのですが、そのメール文にタスクのURLを記載して、直接タスク詳細を開けないものかと悩みがありました。
これをクエリパラメータを使用することで、URLから直接タスク詳細を開くところまで実装しましたので、紹介していきます。
開発環境
フロント | Vue.js(Vue3)、Vue-Router |
バック | AWS、ServelessFramework |
※ 今回の解説ではサンプルを作成して紹介するので、メール送信やDBは省略します。
※ バック側もほぼ使用することはないか、他のものに代替します。
タスク詳細をメール記載のURLから直接開きたい
冒頭でも記載してますが、タスク管理のアプリを作成して、タスク一覧がtopページに表示しています。
タスクカードをクリックすると、さらにそのタスクの詳細がモーダル・ダイアログとして表示されます。
バックログやTrelloといったメジャーなタスク管理アプリも同様の形式でしたのでこれに倣ったUIとしていました(もちろん機能面では圧倒的に劣りますが…)。
設計当初は、これで満足だったのですが、その後の開発でタスクの変更通知をメールで案内する機能を追加しました。
しかし、タスクの詳細が個別のURLを持っていないモーダル機能を使っていますので、メール本文にタスク詳細を直接開くようなURLを記載することができませんでした。
タスク一覧のtopページのURLを記載しても、そこから該当のタスクを探さないといけないので、あまり意味がありません。
個別のURLを付与していないタスク詳細モーダルをURLから直接開きたいが今回の課題です。
タスクIDをクエリパラメータに付与することで対応
今回のアプリは、タスク自体はRDBで管理しており、固有のIDを持っています。
この固有IDをクエリ文字列としてURLに付与し、それをメール文に添付することで対応していきます。
クエリパラメータ
クエリパラメータは様々なwebサイトのURLを見ても見つけることができます。
基本的はURLは、https://tech.affordigitalife.com/
(このブログのURL)のような形です。 クエリ文字列は、末尾に?を付け、それ以降に変数名と値を記載することで情報を付加することできます。
たとえば、このような感じhttps://tech.affordigitalife.com?id=123456
このブログのTOPページのURLの後ろに?を付けてidというKeyを記載しました。
実際にこのURLをクリックすると、画面はこのブログのTOPページにアクセスされます。 URLのアクセス先としては、クエリパラメータは無関係ですので、アクセス先は?より前のURLとなります。
ただ、フロント側では、このクエリ文字列をしっかりと受け取ることができるのでこれを活用していきます。
クエリ文字列からURLを開くまで
ここからは、クエリ文字列を利用したURLの生成と、そのURLにアクセスして、タスク詳細を開くまでの手順を解説していきます。
本記事では、サンプルなのでtaskIdを受け取り、タスクを開く挙動までを説明していきます。
実装する場合は、このURLを叩いたユーザーがだれか、本当にタスク詳細を開く権利があるユーザーかなど、ログインや認証の判定が必要になってきます。
AWS Cognitを使用して、認証していますが、今回の説明では、話がそれてしまいますので、割愛します。
1. メール文にクエリ文字列を付与したURLを記載する
メールを送信する機能は、AWSのLambdaとSNSを利用していますが、今回はそこの説明は割愛します。
メール文に含むURLは以下のように開きたいtaskIdを食える文字列として作成してます。 https://example.com?taskId=123456789 (※架空のURL)
2. Vue-Routerでドメインからクエリを取得する
次に利用者がこのURLからアクセスしたときの処理です。
今回のアプリはvue3を利用していますので、vue-routerのindex.jsからbeforeEachを活用していきます。
これは、どのページに遷移するときも、ページ遷移より前に実行される部分で、現在のパスと遷移先のパスを取得することが出来、この関数の中特定の処理を追加することが可能です。
今回の場合は直接URLからアクセスしてくるので、fromがundifinedでtoにタスク詳細IDのクエリパラメータを含んだURLが取得できます。
ここで、if文を使用して、まずはパスが’/’トップページの場合に、クエリパラメータとしてtaskIdがあるかで判定をします。
router.beforeEach(async (to, from, next) => {
if (to.path === '/' && to.query.taskId) {
console.log(to.query.taskId)
}
...{{そのほかの処理}}
next()
})
3. セッショントークンにIDを保存
次に、クエリ文字列から取得したタスクIDを、トップページのコンポーネントで使用するための準備をします。
タスクIDをコンポーネントで利用できるようにこのタスクIDを保存しておく必要があります。
- セッショントークンを利用して、保存する
- 共通パラメータとしてストアに保存する
- 次のURLにそのまま渡す
これらの方法が考えられるでしょう。
今回は、画面が閉じられるまで情報を記憶できるセッショントークンを利用して、タスクIDをトップページのコンポーネントからも取得できるようにします。
vue-routertのindex.js
で取得した、クエリ文字列(パスパラメーター)をセッショントークンに保存します。
router.beforeEach(async (to, from, next) => {
if (to.path === '/' && to.query.taskId) {
console.log(to.query.taskId)
sessionStorage.setItem('taskId', to.query.taskId);
}
...{{そのほかの処理}}
next()
})
4. タスク一覧画面からタスク詳細を開く
最後に、トップページの画面からタスク詳細のモーダルを開く処理を追加していきます。
と言っても、すでにアプリとしては公開しており、タスク一覧からタスク詳細を開く、メソッドはすでに作成済みです。clickTaskメソッドでタスクカードをクリックした後、タスク詳細コンポーネントが開いています。
<template>
<div>
<TaskCard
:task="task"
@click="clickTask(task)"
/>
</div>
</template>
そこで、 トップページに遷移したときに、すでにパスパラメータからセッショントークンに受け取ったtaskIdがある場合は、clickTaskメソッドを実行してあげることで、タスクが開きます。
vue2とvue3で書き方が少し違うのですが、今回のアプリはvue2とvue3移行の狭間でのリリースであったため、コード記法が混在しています。ご了承ください。
タスク詳細を開くかどうかの判定は、画面描画の前ですので、createdかmountedのライフサイクルにてセッショントークンの判定を追加します。
まずは、taskIdのセッショントークンがある場合、すでに作成済みのタスク詳細を開くclickTaskメソッドを実行しています。 ちなみに、このclickTaskメソッドでは、taskIdをキーにGETメソッドのAPIを叩き、結果が得られたらモダールを表示するロジックを組んできます。
async mounted(){
if (sessionStorage.taskId && sessionStorage.taskId !== 'undefined') {
this.clickTask(res.task);
}
},
まだ、これで終わりではありません。
最後に、タスクを開いた後はセッショントークンを削除します。セッショントークンが残っているとトップページに遷移するたびにタスクを開いてしまうため削除しておきます。
さらに今回はアプリの要件でURLにタスクIDを残しておきたくなかったので、見かけ上のURLからパスパラメーター(クエリ文字列)の範囲を削除して挙動完了です。
async mounted(){
if (sessionStorage.taskId && sessionStorage.taskId !== 'undefined') {
sessionStorage.removeItem('taskId');
this.clickTask(res.task);
}
let url = location.href.split('?')[0]; //?でURLを区切りパスパラを除外
window.history.pushState(null, null, url); //urlを置き換える
},
おわりに
アプリ開発した当初は考慮していなかったURLから直接タスク詳細のモーダル展開を実装する流れでした。
実装では、タスクを開こうとしているユーザーは誰なのか、そもそもそのユーザーはタスクの閲覧権限があるのかなどチェックを入れています。
DevOps開発していくと、あとから要件が増えていくことも多く、既存のアプリ利用に影響を与えないことはもちろんですが、コードの改修もどこまでリファクタリング必要なのかなど考えることが多くありました。
今後も機能追加のたびに、根本的に見直すか、ひとまず機能実装を目指すのかなど悩んでいくんだろうなとも感じました。
それでは、引き続きアプリ開発していきます!!
コメント