はいさい!オースティンやいびーん!
概要
Angular Routerを使って、モーダル、ダイアログ、サイドバーメニューの画面遷移をパスに含める方法を紹介します!
この機能をAuxillary Routesといいます。日本語だと 「副次パス」 と訳すのでしょうか?
背景
モーダル、ダイアログはUIとして頻繁に使う便利な手法です。
しかし、モーダル内の遷移があると一気に複雑になりがち。
しかも、ページを更新したりすると、その遷移の情報が消えてしまい、とても不便です。
そこでAngularが解決してくれるのです。それがAuxillary Routesです。
Angularはいつも、何か手法を持っているので、Angularを信じましょう。
関連記事
実装説明
筆者が作っている記録アプリのモーダルを副次的パスに移動させたいと思います。そうすると、書きかけの情報があったら閉じるときに確認モーダルを出せるようにしたいんです。
一緒に実装しながら解説していきます!行くどー!
現状のRouting
このアプリケーションのapp-routing.module.tsは以下のようです。
const routes: Routes = [
{ path: 'auth', component: AuthComponent },
{ path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
{
path: 'farms/:farmId',
component: FarmComponent,
canActivate: [AuthGuard, MemberGuard],
children: [
{ path: 'manage', component: ManageComponent, data: { title: '農園管理' } },
{ path: 'areas', component: AreasComponent, data: { title: '区域一覧' } },
{
path: 'areas/:areaId',
component: AreaComponent,
children: [
{
path: 'trees',
loadChildren: () => import('./page-modules/tree-pages/tree-pages.module').then((m) => m.TreePagesModule),
},
{
path: 'strawberries',
loadChildren: () =>
import('./page-modules/strawberry-pages/strawberry-pages.module').then((m) => m.StrawberryPagesModule),
canActivate: [CanLoadStrawberryGuard],
},
{ path: 'fertilizer', component: FertilizerComponent },
{ path: 'cropdust', component: CropdustComponent },
{ path: '', component: AreaIndexComponent, pathMatch: 'full' },
],
},
{ path: 'environment', component: EnvironmentComponent },
{ path: '', redirectTo: 'areas', pathMatch: 'full' },
],
},
{ path: '', redirectTo: 'auth', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent },
];
本記事では、記録追加、植物追加のモーダルをAuxillary Routeに落としていきたいと思います。
モーダルのルートを追加する
上記のルートのareas
の中に、children
を追加してモーダルのパスを入れましょう。
Auxillary Routesにするためには、outlet
のプロパティを指定します。
/***/
path: 'farms/:farmId',
component: FarmComponent,
canActivate: [AuthGuard, MemberGuard],
children: [
{ path: 'manage', component: ManageComponent, data: { title: '農園管理' } },
{
path: 'areas',
component: AreasComponent,
data: { title: '区域一覧' },
// ここ!
children: [{ path: 'new', outlet: 'modals', component: NewAreaModalComponent }],
},
/***/
普通にパスを指定するのとほぼ同じですが、outlet
をmodals
に指定しています。
これはAuxillary Routeをどこの<router-outlet>
に出すかを設定する効果があるのです。
どこの
<router-outlet>
ここで顔をしかめた方は続きを読んでいただければと思いますが、そう、複数のを
`を作れるのです。
Auxillary Route用の<router-outlet>
を追加する
AreasComponent
のchildren
にAuxillary Routeとして新規作成モーダルを追加したのですが、その<router-outlet>
も追加しないといけます。
name="modals"
という属性を付与していることにご注目を!
...
<router-outlet name="modals"></router-outlet>
...
これでapp-routing-moduleで指定したoutlet: 'modals'
がDOMにレンダーされるようになります!
Auxillary Routeに遷移するためのリンクを追加する
さらに、そこに遷移できるようにrouterLink
も追加しましょう。
Auxillary Routeの場合、パスの指定がちょっと独特!
<a class="card round" [routerLink]="[{ outlets: { modals: ['new'] } }]">
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" viewBox="0,0,48,48">
<path d="M22.5 38V25.5H10v-3h12.5V10h3v12.5H38v3H25.5V38Z" />
</svg>
</a>
細かくみていきます!
[ // 通常のrouter.navigate([])で使う配列
{ // 文字列ではなくオブジェクト!
outlets: { // <router-outlet name='...'>の子たち
modals: // 'modals'と指定した副次的パスを指定
['new'] // 'modals'の副次的パスのどのパスに行くかを指定
}
}
]
本流のパスとはかけ離れた遷移ロジックを持つAuxillary Routeなので、リンク指定が若干ややこしい!
クリックしてみると無事にモーダルは表示されます!
Auxillary Routeを閉じるための遷移ロジックを追加する
モーダルは開けたのですが、どうやって閉じましょうか?
ちょっとした技が必要です。 NewAreaModalComponent
で見てみましょう!
@Component(/**/)
export class NewAreaModalComponent {
private router = inject(Router);
private route = inject(ActivatedRoute);
handleModalClose() {
this.router.navigate([
{ outlets: { modals: null } } // modalsをnullにする
],
{ relativeTo: this.route.parent } // ここが重要!
);
}
}
最後のrelativeTo
プロパティが非常に重要で、これがないと親部品のRouterの中のmodals
を指定できなくなります。
先ほども言いましたが、この部品は、独自の遷移を持ったので、ActivatedRouteも親から枝分かれした子パスになります。
ちなみに同じことをrouterLink
で実現するためには../
を使うといいですよ!
<base-modal [show]="true" i18n-modal-title modal-title="Add a New Area" (modal-closed)="this.handleModalClose()">
<p [routerLink]="['../', '../', { outlets: { modals: null } }]">CLOSE</p>
<app-new-area-form></app-new-area-form>
</base-modal>
試してみるとどれもうまくいきます!(ここまでの長い試行錯誤を省いていますが)
まとめ
いかがでしょうか?筆者と同様にワクワクしましたか?
現状、仕事で負債回収していて、こういうダイアログを全て独自実装して、閉じる時に「書きかけの情報があれば遷移を止める」のようなロジックがたくさんあるのですが、独自実装なので可読性がゼロに近く、バグが大量に起きていて、それをどうにか直せないかと調べたところ、このAuxillary Routesの機能に出会ったのです。
Angularは色々と嫌われていますが、i18n(多言語機能)といい、モジュールといい、Angular Routerといい、他のフレームワークにないありがたい機能が山盛りなので、過小評価されているのは残念です。筆者がAngularに同情してしまいます。
しかし、そのAngularでも最近は人気を取り戻すために、始めるための学習ハードルを下げようといくつか改善もしています。
最近、Reactive Primitive(React.jsならuseState)をAngularに入れて、パフォーマンスの悪いZone.jsを廃止していくという方針が決まりそうです。
それができれば、RxJSとの相性も良くなるし、パフォーマンスも良くなるので待ち遠しいです!