
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、気がつけばこのブログも記事数が350
を超え、過去を思い出せばホントにいろんな機能を紹介してきました。
そして、そう考えると(もちろん有益だと思うので書いているのですが)最近は少し方向性がマンネリ化してしまっている部分もあるんじゃないかという考えが頭をよぎりました。
そのため、たまに「これやってみたら便利なんじゃ?」というようなチャレンジ企画もお届けできたらいいなと考えています。
そして、今回はそんな中、とある「開発者向け」の機能を考えてみることにしました。
それはというと・・・・・・
クイック・ページリンク
です。
実はこの間、ある顧問契約を開始させていただいたのですが、その中でGitHub
のコミットを見ながら確認をするという作業があり、そのとき「うーん、いちいちルートを見て探すのは非効率だよね・・・」と感じたからなんですね。
ということで、今回は「スペースキー」を押したら登録されているルートのリストが一気に表示され、クリックするだけでそのページを開くことができる「クイック・ページリンク」をつくってみたいと思います。
今回は学習のためではなく、読みもの的なカンジで読んでいただけると嬉しいです。
「僕だけがいない街をエンジョイ中。
ドキドキ感、半端ないです」
開発環境: Laravel 8.x(ただし、Laravel 7.xでも動作を確認しています)
やりたいこと
今回実装したい詳しいは内容は以下5つです。
- スペースキーをタイプしたらルートの一覧が表示される(ただし、入力ボックスにフォーカスしている場合は無視)
- 検索ボックスにキーワードを入力したらリアルタイムで絞り込み
- {user} などのパラメータは自分で変更できる
- ただし、GET以外のメソッドはパラメータが何かを用意しにくいので除外
- できるだけ汎用的に使いたいので、1ファイルで完結させ、JS・CSSフレームワークは一切使わない
では、楽しくやっていきましょう
使い方
まずは使い方です。
「やりたいこと」でも書いたとおり汎用的に使いたいので、以下のようにBlade
ファイルを読み込むだけで使えるようにします。(レイアウトファイルにセットすれば一気に全てのページで有効になりますよ)
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<!-- 省略 -->
</head>
<body>
<!-- 省略 -->
</body>
@env('local')
@include('test.route_list')
@endenv
</html>
読み込むファイルの中身
以下が今回作成したソースコードです。
resources/views/test/route_list.blade.php
@php
$grouped_routes = \Illuminate\Support\Facades\Route::getRoutes()->getRoutesByMethod();
@endphp
<div id="dev-route" style="display:none;position:absolute;width:100%;top:0;left:0;color:#333;">
<div style="background-color:#ccc;margin:15px;padding:15px;">
<input id="dev-route-filter" type="text" style="padding:3px;margin-bottom:15px;" placeholder="ルート名で絞り込み">
<br>
@foreach($grouped_routes as $method => $routes)
@foreach($routes as $route)
@if($method === 'GET')
@php
$index = $loop->index;
$content = preg_replace_callback('|\{[^\}]*\}|', function($matches) use($index) {
return '<input '.
'type="text" '.
'style="padding:2px;width:100px;font-size:14px;margin:0 5px;" '.
'class="dev-route-input dev-route-input-'. $index .'" '.
'placeholder="'. $matches[0] .'"'.
'data-pattern="'. $matches[0] .'"'.
'data-index="'. $index .'"
onfocus="this.select()">';
}, $route->uri);
@endphp
<div class="dev-route-item" style="padding-bottom:7px;">
{!! $content !!}
<a
id="dev-route-link-{{ $loop->index }}"
href="{{ $route->uri }}"
style="padding-left:10px;"
data-uri="{{ $route->uri }}"
target="_blank">開く</a>
</div>
@endif
@endforeach
@endforeach
</div>
</div>
<script>
window.onload = () => {
document.querySelector('#dev-route-filter')
.addEventListener('input', e => {
const keyword = e.target.value;
document.querySelectorAll('.dev-route-item').forEach(link => {
const linkText = link.innerText;
if(keyword === '' || linkText.includes(keyword)) {
link.style.display = 'block';
} else {
link.style.display = 'none';
}
});
});
document.querySelectorAll('.dev-route-input').forEach(input => {
input.addEventListener('input', e => {
const target = e.target;
const index = target.getAttribute('data-index');
const link = document.querySelector('#dev-route-link-'+ index);
let url = link.getAttribute('data-uri');
document.querySelectorAll('.dev-route-input-'+ index)
.forEach(input => {
const pattern = input.getAttribute('data-pattern');
console.log(pattern)
url = url.replace(pattern, input.value);
})
link.setAttribute('href', url);
});
});
document.onkeypress = e => {
const tagName = e.target.tagName;
const code = e.code;
if(tagName === 'BODY' && code === 'Space') {
const container = document.querySelector('#dev-route');
if(container.style.display === 'none') {
container.style.display = 'block';
setTimeout(() => {
const filterInput = document.querySelector('#dev-route-filter').focus();
}, 500);
} else {
container.style.display = 'none';
}
}
}
};
</script>
ご注意
「1ファイルでつくる」がコンセプトなので、通常の開発ではやるべきではない書き方がたくさん入っていますので気をつけてください。(会社でやったら怒られるかも・・・のレベルです)
そのため、ソースコードの中身については割愛させていただきます。m(_ _)m
テストしてみる
では、実際にソースコードをセットして試してみましょう
なお、Laravel 8.x
にはレイアウトファイルが2つあるので、今回はログイン不要でアクセスできるファイルを使ってテストします。
resources/views/layouts/guest.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<!-- 省略 -->
@env('local')
@include('test.route_list')
@endenv
</html>
では、「http://****/login」へアクセスします。
すると、ページが表示され自動的に入力ボックスにフォーカスが当たっています。
今回の仕様「スペースキーをタイプしたらルートの一覧が表示される(ただし、入力ボックスにフォーカスしている場合は無視)」を試してみましょう。
すると、仕様どおり何も動きはありませんでした。
では、次にフォーカスを外してスペースキーをタイプしてみます。
すると・・・・・・
ルートの一覧が表示されました。
では、この中から「register」の横にある「開く」リンクをクリックしてみましょう。
はい
うまくユーザー登録ページが表示されました。
では、次は絞り込みをしてみましょう。
絞り込みボックスにitem
と入力します。
はい
こちらもうまく絞り込みできました。
それでは、次は「item/1」にアクセスしたいので以下のように入力してページを開いてみます。
すると・・・・・・
はい
入力した値を含むURLが表示されました。
成功です
おわりに
ということで、今回はエンドユーザーのためではなく開発者のための機能をつくってみました。
冒頭でも書いたとおり、個人的にもこれで開発効率が上がれば嬉しいなと考えています。
なお、今回はLaravel 7.x
でもテストしていますが、こちらはCSSフレームワークにTailwind CSS
ではなくBootstrap
を使っているので若干見え方が違いました。(そういえばBootstrap 5
はいつ出るんでしょうか)
逆に、JavaScript
はネイティブな使い方をしているので特別何も読み込む必要はありません。
・・・そういえば、今回のようなコードを書いたのはホントに久しぶりでした。
やっぱりフレームワークってすごいですね。
「そっか、これもできないのか」と感じる場面がいくつかありました。
あって当然な存在ですが、そのありがたみを感じた瞬間でした。
以上、ぜひ皆さんも「開発者向け」のプログラムを書いてみてくださいね。
ではでは〜
「少しずつピアノで早い曲に慣れてきました♪」