大田区から発信するゆるゆる日記

主にITエンジニアに関する備忘録日記。たまに趣味も。何か不備があればコメント頂けると幸いです。Twitterアカウント https://twitter.com/ryuzan03

【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の追加は参考にした動画では追加されていないので忘れがちです。

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」を押します。 Xcode ウィンドウが開くので「Push Notifications」を検索してダブルクリックします。 LocalNotifications これで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時間単位で用意されているので作業の時間経過を把握するのにも便利です。

 

www.youtube.com

www.youtube.com

 

 

第二位 楽器のみ

これは皆さんもよく聴かれているかもしれませんね。

カフェやコワーキングスペースなどでよくかかっているボサノバやジャズヒップホップ、和楽器などです。

 

www.youtube.com

www.youtube.com

www.youtube.com

 

 

第三位 ファンク

海外の昔の曲もオススメです。

英語は理解できないですし(私だけ?)、昔の曲はスロービートが多いです。最近の曲だと思わず体が反応しちゃうこともあると思うので、少し前の時代の曲なんて聴いてみるのはどうですか?

 

www.youtube.com

www.youtube.com

www.youtube.com

 

 

第四位 サカナクション

「歌詞が理解できる曲がダメなら邦楽もダメなんじゃ...」と思われる方もいらっしゃるかもしれませんが、サカナクションの曲は歌詞がよく分かりません(サカナクションファンの方すみません汗)。ちなみに私自身サカナクションはとても好きなのです!

歌詞は理解できないのですが、落ち着いた曲は多いので作業用BGMとして重宝しております。

 

www.youtube.com

www.youtube.com

 

 

番外編 musicforprogramming

エンジニア向けの作業用BGMを探していたらこんなサイトを見つけました。

私は使っていませんが興味のある方はぜひ。

 

musicforprogramming.net

 

 

最後に

結局、俺得の記事になってしまいました。すみません。

 

いつもの曲だと少し集中力が下がってきたなと思われる方は、これらの曲をぜひ試してみてください。

 

皆さまのオススメの曲なんかも教えてもらえると嬉しいです。

【Ionic/Angular】Android Studioの初期設定で少し躓いたところ

※下記の内容に不備がありましたら、コメント頂けると幸いです。また、下記の内容をご使用頂ける場合は自己責任でお願いします。

【目次】

 

概要

【Ionicで作る モバイルアプリ制作入門[Angular版]】を参考にモバイルアプリ開発に手を出しました。Angularでさえまともなレベルじゃないのに手を出して良かったのかな…

 

最初の開発環境のところで少しだけ躓いたところがあったので備忘録として書いていきます。が、アプリの指示通りにしていたらまず大丈夫だと思いますので、この記事を見てIonic難しいとかは思わないでくださいね笑

 

 

Android Studioの初期設定で少し躓いたところ

1 ライセンスが許可されていないため一部のAndroid SDKパッケージのインストールに失敗した

ERROR: Failed to install the following Android SDK packages as some licences have not been accepted.
build-tools;28.0.3 Android SDK Build-Tools 28.0.3
platforms;android-28 Android SDK Platform 28
To build this project, accept the SDK license agreements and install the missing components using the Android Studio SDK Manager.
Alternatively, to transfer the license agreements from one workstation to another, see http://d.android.com/r/studio-ui/export-licenses.html

Using Android SDK: /Users/ryuzan/Library/Android/sdk
Install missing SDK package(s)

 

解決方法 

Install missing SDK package(s)

ここをクリックする。

 

common.jarをダウンロードしていない

ERROR: Unable to resolve dependency for ':capacitor-android@debug/compileClasspath': Could not download common.jar (android.arch.lifecycle:common:1.1.1)
Show Details
Affected Modules: capacitor-android

 

解決方法 

Android Studioを再起動させる。

 

 

 

USBデバッグが無効になっている

今回はMacAndroidをUSBで繋げて、アプリ画面をAndroidに表示させようとした時に躓いたところです。

For ADB to recognize your device as a target for deploying debuggable APKs, you must first enable USB debugging in the on-device developer options.
Depending on the version of Android you're using, proceed as follows:

◆On Android 8.0 and higher, go to Settings > System > About phone and tap Build number seven times.
◆On Android 4.2 through 7.1.2, go to Settings > About phone and tap Build number seven times.

Return to the main Settings menu to find Developer options at the bottom. In the Developer options menu, scroll down and enable USB debugging.

 

解決方法 

Android8.0以上で以下の操作を行う。

Settings > System > About phone and tap Build number seven times.

設定>システム>電話情報にある「ビルド番号」を7回押す。

そうすると、どこかに「開発者向けオプション」が表示されます。私の場合は、設計>システムに「開発者向けオプション」が出現しました。

 

最後に「開発者向けオプション」のUSBデバッグを有効にします。

 

 

今後に向けて

 まずは本の内容を実践していきたいと思ってます。

【Angular/TypeScript】Spread Operator(...)【ドット3つ】

※下記の内容に不備がありましたら、コメント頂けると幸いです。また、下記の内容をご使用頂ける場合は自己責任でお願いします。

【目次】

 

概要

Angular After Tutorialに取り組んでいると、見慣れない「...(ドット3つ)」が出てきたので備忘録として記事にまとめます。

 

 

Spread Operator(...)

意味

早速ですが、参考資料から。

Spread Operator - TypeScript Deep Dive 日本語版

スプレッド構文 - JavaScript | MDN

 

上記のサイトを参考にすると、【...は演算子で、配列またはオブジェクトの要素(中身)を展開する】ことができるみたいです。

 

たぶんイマイチ理解できていないと思います。スプレッド演算子を理解するには、例を見るのが最も分かりやすいとのことなので、次の項目で例を挙げていきます。

 

使い方 

配列の代入(Array Assignment)

function foo(x, y, z) { }
var args = [0, 1, 2];
foo(...args); // foo.apply(null, args);と同じ結果

 applyのthis引数にnullを渡さなくてもすむようになりました。

 

配列の代入(Array Assignment)

var list = [1, 2];
list = [...list, 3, 4];
console.log(list); // [1,2,3,4]

 

オブジェクトの展開(Object Spread) 

const point2D = {x: 1, y: 2};
const point3D = {...point2D, z: 3}; // {x: 1, y: 2, z: 3}
const point2D = {x: 1, y: 2};
const anotherPoint3D = {x: 5, z: 4, ...point2D};
console.log(anotherPoint3D); // {x: 1, y: 2, z: 4}
const yetAnotherPoint3D = {...point2D, x: 5, z: 4}
console.log(yetAnotherPoint3D); // {x: 5, y: 2, z: 4}

Object.assignのような使い方もできるみたいです。これは便利。 

 

 

 

今後に向けて

Angular After Tutorialでは、Storeサービスのupdateメソッドから受け取ったstateオブジェクトを展開して、userListオブジェクトに格納するために使っていましたね。

 

見慣れない構文だったので面倒臭くなりそうだなと思っていましたが、調べてみるととても便利な演算子であることが分かりました。

 

これからはどんどん使っていきたいと思います。

 

 

参考

素晴らしい記事に感謝いたします。

Spread Operator - TypeScript Deep Dive 日本語版

スプレッド構文 - JavaScript | MDN