【Ionic/Angular】Tabsテンプレートで作ったIonicアプリにSideBarを追加する(別解編)
※下記の内容に不備がありましたら、コメント頂けると幸いです。また、下記の内容をご使用頂ける場合は自己責任でお願いします。
【目次】
概要
この記事は以前書いた記事【Ionic/Angular】Tabsテンプレートで作ったIonicアプリにSideBarを追加する - 大田区から発信するゆるゆる日記の別解となります。
以前書いた記事だと後々面倒なことになりそうだったので、色々と考えてたら下記の別解に辿り着きました。なかなか開発が前に進まないTT
Tabsテンプレートで作ったIonicアプリにSideBarを追加する(別解編)
前回と今回の比較
前回はtabs.pageをcomponent化し、各ページにそのcomponentを設置することで、SideBarメインのページに仕上げました。つまりionic startでsidebarを選んでアプリを作成し、そこにtabs.componentをねじ込んだイメージです。
今回はtabs.pageの中にSidebar.pageを作ります。Sidebar.pageの中で各ページを遷移させるイメージです。
別解に至った経緯
前回ではion-tab-buttonの性質上、属性tabを指定しているとページ遷移時にtabの属性値を現時点のページのURLに『追加』することになり、ルーティングがとてもややこしくなりました。ion-tab-buttonにはhref属性もあるのですが、ion-tabsのページ遷移の恩恵を受けることができない(ページを再読み込みしてしまう)ので却下としました。
ネットで諸先輩方のアプリを漁っているとtabs.pageの中にsidebar.pageを入れていたので、真似てみたら幸せになれました。
MenuPageを追加する
Ionicアプリを作成する。
$ ionic start project tabs --type=angular
MenuPageを追加する。
このMenuPageがサイドバーになります。
project $ ionic g page menu
app-routing.module.ts(menuを削除)
... const routes: Routes = [ { path: '', loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule) } ]; ...
tabs-routing.module.ts
... const routes: Routes = [ { path: 'tabs', component: TabsPage, children: [ { path: '', loadChildren: () => import('../menu/menu.module').then(m => m.MenuPageModule) }, { path: '', redirectTo: '/tabs/tab1', pathMatch: 'full' } ] }, { path: '', redirectTo: '/tabs/tab1', pathMatch: 'full' } ...
manu-routing.module.ts
... const routes: Routes = [ { path: '', component: MenuPage, children: [ { path: 'tab1', loadChildren: () => import('../tab1/tab1.module').then(m => m.Tab1PageModule), }, { path: 'tab2', loadChildren: () => import('../tab2/tab2.module').then(m => m.Tab2PageModule), }, { path: 'tab3', loadChildren: () => import('../tab3/tab3.module').then(m => m.Tab3PageModule) }, { path: '', redirectTo: '/tabs/tab1', pathMatch: 'full' } ] }, { path: '', redirectTo: '/tabs/tab1', pathMatch: 'full' } ]; ...
menu.page.html
<ion-split-pane contentId="content"> <ion-menu contentId="content"> <ion-header> <ion-toolbar color="primary"> <ion-title>MENU</ion-title> </ion-toolbar> </ion-header> <ion-content> <ion-list> <ion-menu-toggle auto-hide="false" *ngFor="let p of pages"> <ion-item [routerLink]="p.url" routerDirection="root" [class.active-item]="selectedPath.startsWith(p.url)"> <ion-label> {{p.title}} </ion-label> </ion-item> </ion-menu-toggle> </ion-list> </ion-content> </ion-menu> <ion-router-outlet id="content"></ion-router-outlet> </ion-split-pane>
menu.page.ts
... export class MenuPage { pages = [ { title: 'tab1', url: '/tabs/tab1' }, { title: 'tab2', url: '/tabs/tab2' }, { title: 'tab3', url: '/tabs/tab3' }, ]; selectedPath = ''; constructor(private router: Router) { this.router.events.subscribe((event: RouterEvent) => { if (event && event.url) { this.selectedPath = event.url; } }); } }
タブの表示/非表示を実装する
TabsPageの表示/非表示を実装します。
tabs.component.scss
@media screen and (min-width: 992px) { ion-tab-bar { display: none; } }
今後に向けて
ion-tab-buttonの属性について理解していなかったため、ややこしい実装をしてしまいました。 参考書とか見ずに新しい機能を実装する時は、公式リファレンスを見ておいた方が良さそうですね。個人開発だとなかなか難しそうですが。。。
参考資料
素晴らしい記事に感謝致します。
ion-tab-button - Ionic Documentation
【Ionic/Angular】Tabsテンプレートで作ったIonicアプリにSideBarを追加する(エラー対応編)
※下記の内容に不備がありましたら、コメント頂けると幸いです。また、下記の内容をご使用頂ける場合は自己責任でお願いします。
【目次】
概要
前回のブログ【Ionic/Angular】Tabsテンプレートで作ったIonicアプリにSideBarを追加する - 大田区から発信するゆるゆる日記でエラーが発生しました。
本来であれば前回の記事を修正するのがいいと思うのですが、前回の内容が多かったので新しく記事を書くことにしました。
エラー対応
エラー内容
各ページに設置した要素に何かしらのアクション(クリックなど)をしても反応しない。
原因
これはIonicの構造的な問題です。
HTMLの構造で考えるとすぐに分かりそうなことですが、「ion-content」と「ls-tabs」が干渉しあって「ls-tabs」がオーバーラッピング(overlapping)しています。
前回ブログのtab1/tab2/tab3.page.html
<ion-header [translucent]="true"> <ion-toolbar> <ion-title>その他</ion-title> </ion-toolbar> </ion-header> <ion-content [fullscreen]="true"> <ion-button (click)="test()"></ion-button> </ion-content> <ls-tabs></ls-tabs>
解決
こちらの公式ガイドStructure - Ionic Documentationを見て解決しました。
以下のようにtab1/tab2/tab3.page.htmlとscssを変更します。
tab1/tab2/tab3.page.heml
... </ion-content> <ion-footer> <ion-toolbar> <ls-tabs></ls-tabs> </ion-toolbar> </ion-footer> ...
tab1/tab2/tab3.page.scss
... @media screen and (min-width: 992px) { ion-footer { display: none; } } ...
今後に向けて
「分かった!」と思ってしまうと一気に開発を進めてしまうので、少しずつ開発を進めるようにしたいです。
それ以前にテスト書いとけって話かもしれませんが...
参考資料
素晴らしい記事に感謝致します。
Structure - Ionic Documentation
angular - ion-button Click not firing up function in ionic 4 - Stack Overflow
【Ionic/Angular】Tabsテンプレートで作ったIonicアプリにSideBarを追加する
※下記の内容に不備がありましたら、コメント頂けると幸いです。また、下記の内容をご使用頂ける場合は自己責任でお願いします。
【目次】
【2020/5/7 追記】
今回の記事の別解を書きました。別解の方がスマートなので、個人的には別解で実装することをオススメします。
【Ionic/Angular】Tabsテンプレートで作ったIonicアプリにSideBarを追加する(別解編) - 大田区から発信するゆるゆる日記
【2020/4/28 追記】
下記の内容では遷移後のページでアクション(イベント)を起こしても反応しないことが分かりました。この記事の内容が多いため、エラー解決方法については別の記事に書かせていただきます。詳しくはこちら【Ionic/Angular】Tabsテンプレートで作ったIonicアプリにSideBarを追加する(エラー対応編) - 大田区から発信するゆるゆる日記をご確認ください。
概要
Tabsテンプレートで作ったIonicアプリにSidebarを追加してみました。意外に躓いたので備忘録にします。
あと恐らくこれはベストプラクティスではないと思います。「こうした方がいいよ」とかあれば、コメントいただければとても嬉しいです。
完成イメージと参考動画
完成イメージはこちらです。
デスクトップではslidebar、スマホではtabsが表示されるようにします。
Ionic Frameworkを用いたデスクトップレイアウトを考える - Qiita
slidebarを追加する際に参考にした動画です。
How to Combine Ionic 4 Tabs and Side Menu Navigation - YouTube
Tabsテンプレートで作ったIonicアプリにSideBarを追加する
ポイント
ポイントはMenuPageのion-split-paneにcontentIdを追加することとmenu-routingのややこしいルーティングです。
contentIdの追加は参考にした動画では追加されていないので忘れがちです。
MenuPageを追加する
Ionicアプリを作成する。
$ ionic start project tabs --type=angular
MenuPageを追加する。
このMenuPageがサイドバーになります。
project $ ionic g page menu
app-routing.module.ts
... const routes: Routes = [ { path: '', loadChildren: () => import('./menu/menu.module').then( m => m.MenuPageModule) } ]; ...
menu.page.html
<ion-split-pane contentId="content"> <ion-menu contentId="content"> <ion-header> <ion-toolbar color="primary"> <ion-title>MENU</ion-title> </ion-toolbar> </ion-header> <ion-content> <ion-list> <ion-menu-toggle auto-hide="false" *ngFor="let p of pages"> <ion-item [routerLink]="p.url" routerDirection="root" [class.active-item]="selectedPath.startsWith(p.url)"> <ion-label> {{p.title}} </ion-label> </ion-item> </ion-menu-toggle> </ion-list> </ion-content> </ion-menu> <ion-router-outlet id="content"></ion-router-outlet> </ion-split-pane>
menu.page.ts
... export class MenuPage { pages = [ { title: 'tab1', url: '/menu/tab1' }, { title: 'tab2', url: '/menu/tab2' }, { title: 'tab3', url: '/menu/tab3' }, ]; selectedPath = ''; constructor(private router: Router) { this.router.events.subscribe((event: RouterEvent) => { if (event && event.url) { this.selectedPath = event.url; } }); } }
menu-routing.module.ts
... const routes: Routes = [ { path: 'menu', component: MenuPage, children: [ { path: 'tab1', children: [ { path: '', loadChildren: () => import('../tab1/tab1.module').then(m => m.Tab1PageModule), }, { path: 'tab1', redirectTo: '', pathMatch: 'full' }, ] }, { path: 'tab2', children: [ { path: '', loadChildren: () => import('../tab2/tab2.module').then(m => m.Tab2PageModule), }, { path: 'tab2', redirectTo: '', pathMatch: 'full' } ] }, { path: 'tab3', children: [ { path: '', loadChildren: () => import('../tab3/tab3.module').then(m => m.Tab3PageModule) }, { path: 'tab3', redirectTo: '', pathMatch: 'full' } ] }, { path: '', redirectTo: '/menu/tab1', pathMatch: 'full' } ] }, ...
タブの表示/非表示を実装する
TabsPageを利用してタブの表示/非表示を実装します。
今回はTabsPageをコンポーネント化してSharedModuleに登録し、各tabに追加していきます。
SharedModuleを作成する
project $ ionic g module shared
tabs.component.ts
... @Component({ selector: 'ls-tabs', templateUrl: 'tabs.component.html', styleUrls: ['tabs.component.scss'] }) export class TabsComponent { constructor() {} } ...
tabs.component.scss
@media screen and (min-width: 992px) { ion-tab-bar { display: none; } }
shared.module.ts
... @NgModule({ imports: [ CommonModule, IonicModule ], declarations: [TabsComponent], exports: [TabsComponent], }) ...
tab1/tab2/tab3.page.html
... </ion-content> <ls-tabs></ls-tabs>
tab1/tab2/tab3.module.ts
imports: [ ... SharedModule, ... ],
タブのルーティングを紐付ける
menu-routing.module.ts
... { path: 'tab3', children: [ { path: '', loadChildren: () => import('../tab3/tab3.module').then(m => m.Tab3PageModule) }, { path: 'tab3', redirectTo: '', pathMatch: 'full' } ] }, { path: 'tab2/tab1', redirectTo: 'tab1', pathMatch: 'full' }, { path: 'tab3/tab1', redirectTo: 'tab1', pathMatch: 'full' }, { path: 'tab1/tab2', redirectTo: 'tab2', pathMatch: 'full' }, { path: 'tab3/tab2', redirectTo: 'tab2', pathMatch: 'full' }, { path: 'tab1/tab3', redirectTo: 'tab3', pathMatch: 'full' }, { path: 'tab2/tab3', redirectTo: 'tab3', pathMatch: 'full' }, { path: '', redirectTo: '/menu/tab1', pathMatch: 'full' } ] }, { path: '', redirectTo: '/menu/tab1', pathMatch: 'full' } ]; ...
今後に向けて
今回はTabsテンプレートを尊重するあまりルーティングがややこしくなってしまいました。
各タブにURLを割り当てていたらもっと簡単になってたのかな...
もっとスマートな方法を知っている方がいましたらコメントお願いします!
参考資料
素晴らしい記事に感謝致します。
ion-split-pane - Ionic Framework 日本語ドキュメンテーション
Angular Lazy LoadingでつまずいちゃうNgModuleのコンポーネント登録 - Qiita
Ionic Frameworkを用いたデスクトップレイアウトを考える - Qiita
How to Combine Ionic 4 Tabs and Side Menu Navigation - YouTube
【Firebase/Angular】AngularFireAuthのauthプロパティ削除について
※下記の内容に不備がありましたら、コメント頂けると幸いです。また、下記の内容をご使用頂ける場合は自己責任でお願いします。
【目次】
概要
AngularFire6.0からAngularFireAuthのauthプロパティが削除されましたので、その内容について備忘録を書いていきます。
AngularFire authプロパティの変更
AngularFire6.0での変更点
今回はAngularFireAuthの変更にフォーカスを当てて解説と使い方を見ていきます。
- AngularFireAuth has dropped the auth property and instead Promise Proxies the underlying Firebase auth.Auth instance; allowing your development experience to more closely mirror the JS SDK. Similar changes have been made to AngularFireFunctions, AngularFireMessaging, and AngularFirePerformance.
【和訳】
AngularFireAuthはauthプロパティを削除し、代わりにFirebaseのauth.Authインスタンスをプロキシするようになりました。同様の変更がAngularFireFunctions、AngularFireMessaging、AngularFirePerformanceにも行われました。
変更点の全文はこちらから。
要は「authプロパティは削除したから、これからはFirebaseのauth.Authインスタンスを代わりに使ってね」ということですね。
使い方
今回は現在ログインしているユーザーのuidを取得する方法を例に使い方を見ていこうと思います。
Firebase のユーザーを管理する
Angularで使うFirebaseの認証系API一覧 - Qiita
Firebase
component.ts
import * as firebase from 'firebase'; ... getUserId(): string { return firebase.auth().currentUser.uid; }
AngularFireAuth(おまけ)
import { AngularFireAuth } from '@angular/fire/auth'; ... constructor( public afAuth: AngularFireAuth ) {} ... getUserId(): string { return this.afAuth.auth.currentUser.uid; }
参考資料
素晴らしい記事に感謝致します。
angularfire/version-6-upgrade.md at master · angular/angularfire · GitHub
Firebase のユーザーを管理する
Angularで使うFirebaseの認証系API一覧 - Qiita
【Ionic/Angular】iOSアプリ開発におけるLocalNotificationsの挙動
※下記の内容に不備がありましたら、コメント頂けると幸いです。また、下記の内容をご使用頂ける場合は自己責任でお願いします。
【目次】
概要
本格的にIonicでアプリ開発を始めました。
参考書は「Ionicで作るモバイルアプリ制作入門[Angular版]」です。
初心者の方でも本の通りに開発を進めればちゃんとアプリが作れる内容になっていると思います。
今回はLocalNotificationsの実装で詰まったところがあったので備忘録として書いていきます。
iOSアプリ開発におけるLocalNotificationsの挙動
問題発生
参考書に則ってLocalNotificationsを実装していたのですが、iOSで挙動を確認すると動作はしているけど通知が表示されない。Androidでは通知は来るしWebでも動作は確認できる。
確認環境
・Ionic 5.0.0
・Angular 8.3.23
・Xcode
・iPhone11ProMax
対策①
IonicのドキュメントからLocalNotifications(capacitor)をインストールします。
Local Notifications - Ionic Framework 日本語ドキュメンテーション
app.module.tsでLocalNotificationsをインポートする必要があります。
app.module.ts
import { LocalNotifications } from '@ionic-native/local-notifications/ngx'; ... providers: [ LocalNotifications ] ...
これでiOSで動くようになりましたが、Androidの方で挙動がおかしくなりました。
対策②
Xcodeの設定を変更します。
Push Notifications - Capacitor
Signing&CapabilitiesにPush Notificationsを追加します。
Signing&Capabilities画面の左上にある「+Capability」を押します。
ウィンドウが開くので「Push Notifications」を検索してダブルクリックします。
これでiOSでも通知が来るようになります。
結論
「Ionicで作るモバイルアプリ制作入門[Angular版]」で開発を進めるのであればXcodeの設定を変えるのがいいと思います。
参考
素晴らしい記事に感謝致します。
Push Notifications - Capacitor
Local Notifications - Ionic Framework 日本語ドキュメンテーション
【Angular】ngTemplateOutletディレクティブの使い方
※下記の内容に不備がありましたら、コメント頂けると幸いです。また、下記の内容をご使用頂ける場合は自己責任でお願いします。
【目次】
概要
ngTemplateOutletの使い方について基本を学んだので備忘録として書きます。少し応用(?)も入れました。
あと私事ですが、今回からMarkdownで記事を書いてます。コードが綺麗に書けて感動!
ngTemplateOutletの使い方
ngTemplateOutletとは?
あらかじめ用意したテンプレート(ng-template)を任意の場所に挿入することができるディレクティブです。
構文
<ng-container *ngTemplateOutlet="参照元変数; context: テンプレートに反映させるオブジェクト"> </ng-container>
ng-template側では以下のようにして変数を受け取ります。
<ng-template #参照元変数 let-変数名="テンプレートに反映させるオブジェクトのプロパティ名"> // テンプレート化したいコード </ng-template>
例
ngTemplateOutletディレクティブで呼び出すテンプレートを指定し、contextに指定したオブジェクトはテンプレートに引き渡すことができます。
component.html
<ng-container *ngTemplateOutlet"person; context: persons[0]"> </ng-container> <ng-template #person let-name="name" let-gender="gender" let-hobby="hobby"> <ul> <li>名前: {{name}}</li> <li>性別: {{gender}}</li> <li>趣味: {{hobby}}</li> </ul> </ng-template>
component.ts
persons = [ { name: 'Yamada', gender: 'man', hobby: 'cooking' }, ... ];
応用編
$implicit
$implicitは指定した値を、明示的に値を指定していない"let-変数名"属性に割り当てられます。 具体的に見ていきましょう。
component.html
<ng-container *ngTemplateOutlet"person; context: persons[0]"> </ng-container> <ng-template #person let-message let-name="name" let-gender="gender" let-hobby="hobby"> <ul> <li>名前: {{name}}</li> <li>性別: {{gender}}</li> <li>趣味: {{hobby}}</li> <p>{{message}}<p> </ul> </ng-template>
component.ts
persons = [ { name: 'Yamada', gender: 'man', hobby: 'cooking' $implicit: '今日は良い天気ですね' }, ... ];
$implicitの値"今日は良い天気ですね"はlet-message属性に割り当てられ、template内でローカル変数"message"として使うことができます。
ngTemplateOutletで複数のオブジェクトをテンプレートに渡したい
あまりないケースだと思いますが、複数のデータをngTemplateOutlet内で指定したい場合です。
component.html
<ng-container *ngTemplateOutlet"person; context: {$implicit: '今日は良い天気ですね', name:'Yamada', gender: 'man', hobby: 'cooking'}"> </ng-container> <ng-template #person let-message let-name="name" let-gender="gender" let-hobby="hobby"> <ul> <li>名前: {{name}}</li> <li>性別: {{gender}}</li> <li>趣味: {{hobby}}</li> <p>{{message}}<p> </ul> </ng-template>
これも先程の$implicitを使った例と同じ結果になります。注意点はcontextにはオブジェクト形式で値を渡さないといけないので、{}(波括弧)を使用する必要があります。
今後に向けて
ほぼ参考書に乗っていた内容です。
半年ほどAngularの勉強していますが参考書にこんな内容が載ってるとは知りませんでした。
参考書は熟読しないといけませんね。
Markdown記法いいですね。
もっと早く始めてたら良かった。
参考
素晴らしい記事に感謝致します。
FOX HOUND TECH
はてなで使えるMarkdown記法 - hero-rinのブログ
エンジニア向け作業用BGM集
最近はコロナウイルスの影響でリモートワークになったエンジニアの方も増えたのではないでしょうか?
在宅だと周りの目が気にならないのでのんびり作業をしがちですよね。オンラインミーティングの向こう側では実は...なんてこともありそうですね。
仕事に対するモチベーションは人それぞれだと思いますが、できれば在宅でも集中して仕事をしたいところです。
そこで今回は在宅でも集中して仕事をするための作業用BGMについて考えてみました。
【目次】
そもそも集中して仕事をするには?
はっきり言って人それぞれだと思います←
私の場合は
- 適度な雑音
- 適度な満腹
- 十分な睡眠
- 仕事着
- ディスプレイがある
- 仕事に詰まると何か食べ続ける
です。私は適度な緊張感がある方が集中して仕事ができます。
もう少しざっくりと説明すると、三大欲求が満たされている状態でストレスを五感で調整することで集中力を高めるといった感じでしょうか。
しかし全ての人がこれに該当するとは思えません。環境や時期によっても集中力の上げ方は変わってくると思います。なので集中して仕事をする方法は自分自身で見つけていく必要があります。
適度な雑音=作業用BGM
私は適度な雑音がある方が集中できます。その適度な雑音の調整を行ってくれるのが作業用BGMです。
では適度な雑音とはどのような感じでしょうか。
- うるさすぎない
- 周りの雑音は消してくれる
- テンションが上がらない(曲にのれない)
- 意味(歌詞)が理解できない(英語も含む)
今回はこれらを基準にYouTubeから作業用BGMを選定しました。
第一位 焚き火
焚き火は圧倒的にオススメです!
音量の調節がしやすいですし、動画時間が1時間単位で用意されているので作業の時間経過を把握するのにも便利です。
第二位 楽器のみ
これは皆さんもよく聴かれているかもしれませんね。
カフェやコワーキングスペースなどでよくかかっているボサノバやジャズヒップホップ、和楽器などです。
第三位 ファンク
海外の昔の曲もオススメです。
英語は理解できないですし(私だけ?)、昔の曲はスロービートが多いです。最近の曲だと思わず体が反応しちゃうこともあると思うので、少し前の時代の曲なんて聴いてみるのはどうですか?
第四位 サカナクション系
「歌詞が理解できる曲がダメなら邦楽もダメなんじゃ...」と思われる方もいらっしゃるかもしれませんが、サカナクションの曲は歌詞がよく分かりません(サカナクションファンの方すみません汗)。ちなみに私自身サカナクションはとても好きなのです!
歌詞は理解できないのですが、落ち着いた曲は多いので作業用BGMとして重宝しております。
番外編 musicforprogramming
エンジニア向けの作業用BGMを探していたらこんなサイトを見つけました。
私は使っていませんが興味のある方はぜひ。
最後に
結局、俺得の記事になってしまいました。すみません。
いつもの曲だと少し集中力が下がってきたなと思われる方は、これらの曲をぜひ試してみてください。
皆さまのオススメの曲なんかも教えてもらえると嬉しいです。