Angularで2度押し禁止ボタンをカスタムディレクティブで作成する

● Angularで2度押し禁止ボタンをカスタムディレクティブで作成する

html

次のコンポーネントの HTML でボタンを次のようにします

<button type="button">送信する</button>

 ↓ ( appOnClickDisable="1" を追加 )

<button
    appOnClickDisable="1"
    type="button" >
    送信する
</button>

カスタムディレクティブを作成します app/directives/app-on-click-disable.directive.ts

import { Directive, HostListener, Input, Renderer2 } from '@angular/core';

 @Directive({
  selector: '[appOnClickDisable]'
})
export class AppOnClickDisableDirective {
  @Input() appOnClickDisable: number;
  constructor(
    private renderer: Renderer2, 
  ) { }
  @HostListener('click', ['$event']) onClick(event) {
    console.log( 'AppOnClickDisableDirective clicked' );
    if ( this.appOnClickDisable == 1 ){
      event.target.disabled = true;
    }
  }
}

app.module.ts で読み込みます app/app.module.ts

import { AppOnClickDisableDirective } from './directives/app-on-click-disable.directive';
@NgModule({
  declarations: [
    AppComponent,
    AppOnClickDisableDirective,  // 追加
  ],

以上で一度クリックするとボタンが disabled になります。 ( disabled解除は app-on-click-disable.directive.ts ソース内に実装する必要があります。)

No.1799
06/23 18:15

edit

Angular で environment が prod の時は console.log を出力しないようにする。

● Angular で environment が prod の時は console.log を出力しないようにする。

main.ts

if (environment.production) {
  enableProdMode();
}

  ↓ このように追加します

if (environment.production) {
  enableProdMode();
  // production環境下では console.log , console.error を何もさせない
  if (window) {
    window.console.log = () => {};
    window.console.error = () => {};
  }
}

引用元 : https://bit.ly/3hTy660

No.1798
06/23 17:56

edit

Angular renderer2 で直接DOM操作を行う。

● Angular renderer2 で直接DOM操作を行う。

https://angular.io/api/core/Renderer2

html

クリックイベントの引数に $event を渡します

<button
	type="button"
    (click)="sendButtonClicked($event)">
	送信する
</button>

ts

event.target が DOMオブジェクトです。

  private sendButtonClicked( event:any ) {
    // css class を追加
    this.renderer.addClass(event.target, 'now-loading');
  }
No.1797
06/23 17:56

edit

Angular で 前のページに戻る ( history.back() )

● Angular で 前のページに戻る ( history.back() )

my.component.ts に 以下の3つを追加

import { Location } from '@angular/common';
  constructor( 
              private location: Location
              ) {
  }
  private backBtnClick() {
    this.location.back();
  }

my.component.html に戻るボタンを設置

<button (click)="backBtnClick()" type="button">戻る</button> 
No.1795
06/22 15:23

edit

「AngularでREST API からデータ取得して一覧表示」の基本

● サービス BoardsService の作成

ng g service services/boards
CREATE src/app/services/boards.service.spec.ts (333 bytes)
CREATE src/app/services/boards.service.ts (135 bytes)
ng g コマンドでサービスを生成した場合、そのサービスは root に所属します。appモジュールではありません。

以下のように @Injectable に providedIn というのが追加になっていて、

@Injectable({
  providedIn: 'root'
})

どのモジュールから追加されるのか指定できるようになってます。
ここが 'root' の場合は app/app.module.ts への登録不要で Dependency Injection できます。
なお root 所属のサービスは全てのモジュールで同じインスタンスが利用されます(つまりシングルトン)。

引用: https://bit.ly/3d1U2bA

● app/app.module.ts に 外部モジュールの登録

import { HttpClientModule } from '@angular/common/http';
  imports: [
    .................................
    HttpClientModule,
  ],

● environments/environment.ts に 開発用 / 本番用 URL をセット

environments/environment.ts

export const environment = {
  production: false ,
  apiUrl: 'http://localhost:5000'
};

environments/environment.prod.ts

export const environment = {
  production: true ,
  apiUrl: 'http://localhost:5000'
};

● サービス「BoardsService」にロジックを記述

app/services/boards.service.ts を以下の内容で保存

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { environment } from './../../environments/environment';

@Injectable({
  providedIn: 'root'
})
export class BoardsService {

  constructor(private httpClient: HttpClient) { }

  getIndex(): Observable<any> {
    return this.httpClient.get(environment.apiUrl + '/boards/');
  }
}

● コンポーネント「BoardsComponent」で一覧を表示

app/boards/boards.component.ts を以下の内容で保存

1. メンバ変数を定義

export class BoardsComponent implements OnInit {
  boards_loop = [];

2. DI に追加

  constructor(
               private boardsService: BoardsService ,
            ) 

3. ngOnInit() のタイミングでデータ取得

  ngOnInit() {
    this.boardsService.getIndex().subscribe(res => {
      this.boards_loop = res;
    });
  }

app/boards/boards.component.html に以下を追加

<ul>
<li *ngFor="let board of boards_loop">
    <span>{{board.id}}</span> : {{board.name}}
</li>
</ul>
No.1791
06/15 17:15

edit

NgModule の providers や imports の使い分け

● NgModule の providers や imports の使い分け

実は簡単な使い分け
declarations : ディレクティブ(含コンポーネント、パイプ)を書きます。 htmlテンプレートに書くもの、ですね。

providers : Serviceなど、DIで解決するものをここに書きます。 Angular 6 以降は各モジュールに
================
@Injectable({
  providedIn: 'root'
})
================
と書きます。

imports : 外部のAngularモジュール。Httpモジュールとか、UIモジュールとか。
これだけです!

引用元 : https://qiita.com/kohashi/items/1415a358901ca438c400

No.1790
06/15 17:11

edit

Angular 一覧表示の基本

● Angular 一覧表示の基本

app/app.component.ts

 ngOnInit() {
    this.boards_loop = [
      { id: 11, name: 'Mr. Nice' },
      { id: 12, name: 'Narco' },
      { id: 13, name: 'Bombasto' },
      { id: 14, name: 'Celeritas' },
      { id: 15, name: 'Magneta' },
      { id: 16, name: 'RubberMan' },
      { id: 17, name: 'Dynama' },
      { id: 18, name: 'Dr IQ' },
      { id: 19, name: 'Magma' },
      { id: 20, name: 'Tornado' }
    ];    
  }

app/app.component.html

<li *ngFor="let board of boards_loop">
    <span class="badge">{{board.id}}</span> : {{board.name}}
</li>

出力結果

<ul _ngcontent-bek-c0=""><!--bindings={
  "ng-reflect-ng-for-of": "[object Object],[object Object"
}--><li _ngcontent-bek-c0=""><span _ngcontent-bek-c0="">11</span> : Mr. Nice
</li><li _ngcontent-bek-c0=""><span _ngcontent-bek-c0="">12</span> : Narco
</li><li _ngcontent-bek-c0=""><span _ngcontent-bek-c0="">13</span> : Bombasto
</li><li _ngcontent-bek-c0=""><span _ngcontent-bek-c0="">14</span> : Celeritas
</li><li _ngcontent-bek-c0=""><span _ngcontent-bek-c0="">15</span> : Magneta
</li><li _ngcontent-bek-c0=""><span _ngcontent-bek-c0="">16</span> : RubberMan
</li><li _ngcontent-bek-c0=""><span _ngcontent-bek-c0="">17</span> : Dynama
</li><li _ngcontent-bek-c0=""><span _ngcontent-bek-c0="">18</span> : Dr IQ
</li><li _ngcontent-bek-c0=""><span _ngcontent-bek-c0="">19</span> : Magma
</li><li _ngcontent-bek-c0=""><span _ngcontent-bek-c0="">20</span> : Tornado
</li></ul>

app/app.component.html ( ng-container を使うやり方 )

    <ng-container *ngFor="let board of boards_loop">
        <li><span>{{board.id}}</span> : {{board.name}}</li>
    </ng-container>      

出力結果 ( ng-container を使うやり方 )

<ul _ngcontent-nve-c1="">
    <!--bindings={
  "ng-reflect-ng-for-of": "[object Object],[object Object"
     }-->
    <!---->
    <li _ngcontent-nve-c1=""><span _ngcontent-nve-c1="">11</span> : Mr. Nice</li>
    <!---->
    <li _ngcontent-nve-c1=""><span _ngcontent-nve-c1="">12</span> : Narco</li>
    <!---->
    <li _ngcontent-nve-c1=""><span _ngcontent-nve-c1="">13</span> : Bombasto</li>
    <!---->
    <li _ngcontent-nve-c1=""><span _ngcontent-nve-c1="">14</span> : Celeritas</li>
    <!---->
    <li _ngcontent-nve-c1=""><span _ngcontent-nve-c1="">15</span> : Magneta</li>
    <!---->
    <li _ngcontent-nve-c1=""><span _ngcontent-nve-c1="">16</span> : RubberMan</li>
    <!---->
    <li _ngcontent-nve-c1=""><span _ngcontent-nve-c1="">17</span> : Dynama</li>
    <!---->
    <li _ngcontent-nve-c1=""><span _ngcontent-nve-c1="">18</span> : Dr IQ</li>
    <!---->
    <li _ngcontent-nve-c1=""><span _ngcontent-nve-c1="">19</span> : Magma</li>
    <!---->
    <li _ngcontent-nve-c1=""><span _ngcontent-nve-c1="">20</span> : Tornado</li>
</ul>

参考 : Angularの便利タグng-container, ng-content, ng-template - Qiita

No.1789
06/15 17:25

edit

Angular router のページ遷移のイベント

● Angular router のページ遷移のイベント

https://angular.io/api/router/Event

NavigationStart,
RouteConfigLoadStart,
RouteConfigLoadEnd,
RoutesRecognized,
GuardsCheckStart,
ChildActivationStart,
ActivationStart,
GuardsCheckEnd,
ResolveStart,
ResolveEnd,
ActivationEnd
ChildActivationEnd
NavigationEnd,
NavigationCancel,
NavigationError
Scroll

解説

種別 発火タイミング
NavigationStart ナビゲーションが開始された時。
ActivationStart ナビゲーション先のコンポーネントが決まった時(GuardやResolveの前)。
ActivationEnd ナビゲーション先のインスタンスが作られた後)。
NavigationEnd ナビゲーションが終了した時(正常に終了した場合)。
NavigationCancel ナビゲーションが終了した時(ナビゲーション処理の途中でキャンセルされた場合)。
NavigationError ナビゲーションが終了した時(ナビゲーション先が存在しないなど、エラーが発生した場合)。

引用: https://bit.ly/37t3uU2

記述方法 イベント(NavigationStart)で操作を行う例」

  constructor( private _router: Router ) {

    _router.events.subscribe(event => {
      if(event instanceof NavigationStart) {
        // ここにページ遷移ごとに実行するメソッド
      }
    });

  }
No.1788
06/15 14:39

edit

No.1773
06/03 08:34

edit

Angular Fullcalendar を使用する

● Angular Fullcalendar を使用する

● パッケージのインストール

npm install --save @fullcalendar/angular @fullcalendar/core @fullcalendar/daygrid @fullcalendar/interaction @fullcalendar/timegrid

styles.scss

// add this
@import '~@fullcalendar/core/main.css';
@import '~@fullcalendar/daygrid/main.css';
@import '~@fullcalendar/timegrid/main.css';

app/app.module.ts

// add
import { FullCalendarModule } from '@fullcalendar/angular';

......

  imports: [
    BrowserModule,
    FullCalendarModule // add
  ],

app/app.component.ts

// calendar
import { FullCalendarComponent } from '@fullcalendar/angular';
import { EventInput } from '@fullcalendar/core';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGrigPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction'; // for dateClick
export class AppComponent {

  @ViewChild('calendar') calendarComponent: FullCalendarComponent; // the #calendar in the template

  calendarVisible = true;
  calendarPlugins = [dayGridPlugin, timeGrigPlugin, interactionPlugin];
  calendarWeekends = true;
  calendarEvents: EventInput[] = [
    { title: 'Event Now', start: new Date() }
  ];

  toggleVisible() {
    this.calendarVisible = !this.calendarVisible;
  }

  toggleWeekends() {
    this.calendarWeekends = !this.calendarWeekends;
  }

  gotoPast() {
    let calendarApi = this.calendarComponent.getApi();
    calendarApi.gotoDate('2000-01-01'); // call a method on the Calendar object
  }

  handleDateClick(arg) {
    if (confirm('Would you like to add an event to ' + arg.dateStr + ' ?')) {
      this.calendarEvents = this.calendarEvents.concat({ // add new event data. must create new array
        title: 'New Event',
        start: arg.date,
        allDay: arg.allDay
      })
    }
  }

}

No.1763
05/25 16:29

edit

Angular アプリをサブディレクトリにビルド(デプロイ)する

● Angular アプリをサブディレクトリにビルド(デプロイ)する

通常ビルドする

ng build

サブディレクトリ /angular/ にビルドする

ng build --base-href=/angular/

(最後のスラッシュは忘れずに!)

No.1761
05/24 22:10

edit

No.1715
03/04 22:05

edit

Angular で datetimepicker を使用する

● Angular で datetimepicker を使用する

またバリデーションにも対応させます。


* jquery , jquery-datetimepicker をインストール

npm install jquery --save
npm install jquery-datetimepicker  --save

* angular.json に 追加

            "styles": [
              "src/styles.scss" ,
              "node_modules/jquery-datetimepicker/build/jquery.datetimepicker.min.css"
            ],
            "scripts": [
              "node_modules/jquery/dist/jquery.min.js" ,
              "node_modules/jquery-datetimepicker/build/jquery.datetimepicker.full.min.js"
            ]


* app/app.component.ts に追加

import { Component } from '@angular/core';

// jquery
declare var $: any;


* 表示するページ page01 を新規作成

ng g component page01

1.(ルーティング) * app/app-routing.module.ts にルーティングを追加

const routes: Routes = [
  { path: 'page01', component: Page01Component }, 
  { path: '**', component: Page01Component }, // 追加
];

2.(html) app/page01/page01.component.html を以下の内容で保存

<input type="text" class="form-control jquery-datetimepicker" name="test-date" formControlName="datepicker" autocomplete="off">
<pre class="debug">
{{ registerForm.value | json }} <br>
Validation: {{ registerForm.get( 'datepicker' ).valid }}
</pre>

3.(TypeScript) app/page01/page01.component.ts を以下の内容で保存

import { Component, OnInit } from '@angular/core';

// jquery
declare var $: any;

@Component({
  selector: 'app-page01',
  templateUrl: './page01.component.html',
  styleUrls: ['./page01.component.scss']
})
export class Page01Component implements OnInit {
  constructor() { }
  ngOnInit() {

    // ===== jquery-datetimepicker =====
    let _this = this;
    $.datetimepicker.setLocale('ja'); // 日本語化
    $('input.jquery-datetimepicker').datetimepicker({
      lang: 'ja',
      timepicker: false,
      format:'Y-m-d',
      onSelectDate:function( date ){
        var year = date.getFullYear();
        var  month = ("0"+(date.getMonth() + 1)).slice(-2);
        var  date =  ("0"+date.getDate()).slice(-2);
        var date_formatted = year + '-' + month + '-' + date;
        _this.registerForm.get('datepicker').setValue( date_formatted );
      }
    });
    // ===== jquery-datetimepicker =====

  }
}

これでコンポーネントは完成です。

jquery-datetimepicker で日付を選択時に、_this.registerForm.get('datepicker') にも値が反映されます。

No.1708
02/21 15:36

edit

Angular コーディングスタイルガイドの日本語訳抜粋

● Angular コーディングスタイルガイドの日本語訳抜粋

https://www.miraclelinux.com/tech-blog/4cd30h

おすすめです。

オブジェクト 記法
インターフェース名 パスカルケース(アッパーキャメル) interface Result {}
クラス名 パスカルケース(アッパーキャメル) export class ExceptionService
メソッド名 キャメルケース getName()
プロパティ名 キャメルケース private toastCount: number;
No.1707
06/15 09:33

edit

Laravel6 に jwt-auth をインストールしAngular の SPAからログインする(フロントエンド Angular編)

● Angularアプリの作成

ng new jwtauth-app
cd jwtauth-app
ng serve --open

● 必要なコンポーネント、サービスの作成

ng g component login
ng g component home
ng g service services/authentication

● 必要なガードの作成

ng g guard guard/auth

( CanActivate を選択して作成する )

app/guard/auth.guard.ts

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthenticationService } from '../services/authentication.service';

@Injectable()
export class AuthGuard implements CanActivate {
	constructor(
		private authService: AuthenticationService,
		private router: Router
	) { }

	canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
		if ( !this.authService.isAuthenticated() ) {
			this.router.navigate(['login']);
			return false;
		}
		return true;
	}
}

作成したガードを app/app.module.ts へ読み込ませる
( import して @NgModuleのprovidersに追加する )

app/app.module.ts

// guard
import { AuthGuard } from './guard/auth.guard';

@NgModule({
    ........
	providers: [AuthGuard],
})

● 必要なモデルの作成

mkdir src/app/models
vi src/app/models/user.ts

user.ts

export class User {
    id: number;
    username: string;
    password: string;
    firstName: string;
    lastName: string;
    token?: string;
}
app/home/<いくつかのファイル>
app/login/<いくつかのファイル>
app/services/<いくつかのファイル>

が作成されます

● bootstrap4 を Angularアプリにインストール

npm install --save bootstrap jquery popper.js

angular.json に以下を追加

    "styles": [
      "src/styles.scss" ,
      "node_modules/bootstrap/dist/css/bootstrap.min.css"
    ],
    "scripts": [
      "node_modules/jquery/dist/jquery.slim.min.js",
      "node_modules/popper.js/dist/umd/popper.min.js",
      "node_modules/bootstrap/dist/js/bootstrap.min.js"
    ]

● auth0/angular-jwt のインストール

npm install @auth0/angular-jwt

● ReactiveFormsModule の読み込み

app/app.module.ts

import { ReactiveFormsModule } from '@angular/forms';

  imports: [
    ReactiveFormsModule ,    // 追加
  ],
No.1704
02/17 16:46

edit

「ERROR in node_modules/rxjs/internal/types.d.ts(81,44): error TS1005: ';' expected.」エラーの対処法

● 「ERROR in node_modules/rxjs/internal/types.d.ts(81,44): error TS1005: ';' expected.」エラーの対処法

rxjs": "^6.0.0",

 ↓

rxjs": "6.0.0",
No.1706
02/17 10:44

edit

No.1701
02/13 15:38

edit

angular/router ルーターのルート一覧を表示させる

● Augury を使用する

https://chrome.google.com/webstore/detail/augury/elgalmkoelokbchhkhacckoklkejnhcd?hl=ja

● 手動でルート一覧を表示させる

http://bit.ly/2UKeBDD

import { Router, Route } from "@angular/router";

constructor(private router: Router) { }

ngOnInit() {
  this.printpath('', this.router.config);
}

printpath(parent: String, config: Route[]) {
  for (let i = 0; i < config.length; i++) {
    const route = config[i];
    console.log(parent + '/' + route.path);
    if (route.children) {
      const currentPath = route.path ? parent + '/' + route.path : parent;
      this.printpath(currentPath, route.children);
    }
  }
}
No.1699
02/13 14:26

edit

Angular do not found

Finally, I am able to solve that issue.
do() is replaced by tap().
for reference https://www.academind.com/learn/javascript/rxjs-6-what-changed/
and tap() should be inside .pipe().
like this, .pipe(tap())
for more reference, you can refer this link,
https://alligator.io/angular/angular-6/
and
https://www.learnrxjs.io/operators/utility/do.html

http://bit.ly/31LeglK

import { of } from 'rxjs';
import { tap, map } from 'rxjs/operators';
No.1698
02/12 16:01

edit

ionic で 横スクロールするナビゲーション

● ionic で 横スクロールするナビゲーション

<ion-content overflow-scroll="true">
......
</ion-content>

http://bit.ly/2H6NKK1
http://bit.ly/2H5gB1h

No.1695
02/08 22:37

edit

angular/router で 前のページURLを取得する

import { Router, NavigationEnd } from '@angular/router';
	constructor(
		private router: Router
	) {
		// 前のページURLを取得する
		this.currentUrl = this.router.url;
		router.events.subscribe(event => {
			if (event instanceof NavigationEnd) {
				this.previousUrl = this.currentUrl;
				this.currentUrl = event.url;
			}
		});
	}

これで this.previousUrl に前ページURLが入ります。

引用: http://bit.ly/31DcnHM

No.1694
02/08 20:54

edit

Ionic で Firebase ( Cloud Firestore ) を使用する

● angularfire2 のインストール

npm install angularfire2 firebase

● FirebaseのWEBコンソール画面からアプリを登録して設定情報をコピーしておく

var config = {
  apiKey: 'Your credentials here',
  authDomain: 'Your credentials here',
  databaseURL: 'Your credentials here',
  projectId: 'Your credentials here',
  storageBucket: 'Your credentials here',
  messagingSenderId: 'Your credentials here'
};

● モデル(インターフェース)の定義

app/models/song.interface.ts

export interface Song {
    id: string;
    albumName: string;
    artistName: string;
    songDescription: string;
    sonName: string;
}

● モデル(サービス)の定義

Cloud Firestore のコレクション songList を操作するサービスを記述します。

ionic generate service services/data/firestore

自動生成された app/services/data/firestore.service.ts に以下を追記

import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';
import { Song } from '../../models/song.interface';
export class FirestoreService {
	constructor(public firestore: AngularFirestore) { }
	createSong(
		albumName: string,
		artistName: string,
		songDescription: string,
		songName: string
	): Promise<void> {
		const id = this.firestore.createId();
		return this.firestore.doc(`songList/${id}`).set({
			id,
			albumName,
			artistName,
			songDescription,
			songName,
		});
	}
	getSongList(): AngularFirestoreCollection<Song> {
		return this.firestore.collection(`songList`);
	}
	getSongDetail(songId: string): AngularFirestoreDocument<Song> {
		return this.firestore.collection('songList').doc(songId);
	}
	deleteSong(songId: string): Promise<void> {
		return this.firestore.doc(`songList/${songId}`).delete();
	}
}

● /home 画面にデータ一覧を表示

app/home/home.page.ts に下記を追加

import { FirestoreService } from '../services/data/firestore.service';
import { Router } from '@angular/router';
export class HomePage {
	public songList;  // add this
	constructor(
		private firestoreService: FirestoreService,
		private router: Router
	) { }
	ngOnInit() {
		this.songList = this.firestoreService.getSongList().valueChanges();  // add this
	}
}

app/home/home.page.html に下記を追加

<ion-content class="ion-padding">
	<ion-card *ngFor="let song of songList | async" routerLink="/detail/{{song.id}}">
		<ion-card-header>
			{{ song.songName }}
		</ion-card-header>
		<ion-card-content>
			Artist Name: {{ song.artistName }}
		</ion-card-content>
	</ion-card>
</ion-content>

● ルール設定(セキュリティ設定)を行う

セキュリティ設定をしていない場合は、全てのアクセスが却下となります。 また一番ゆるく(全てを許可)すると、誰でもアクセスできるようになります。

*1. 誰でもアクセスできる設定

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

この設定

export var firebaseConfig = {
    projectId: "my-project-name",
    storageBucket: "my-project-name.appspot.com",
};

だけで、誰でもアクセスできてしまいます。

*2. コレクション「songList」内なら誰でもアクセスできる設定

service cloud.firestore {
  match /databases/{database}/documents {
    match /songList/{songId} {
      allow read, write: if true;
    }
  }
}
なお {songId} は ワイルドカードです。
* と記述したいところですが、{songId}と書きます。
{songListId}でもいいみたいです。(文字列はなんでも良いらしい)

引用: http://bit.ly/2Om37Cz
http://bit.ly/385JJBi

No.1687
02/03 21:47

edit

Angular CLI からアプリの作成

● Angular CLI のインストール

npm install -g @angular/cli

● Angular CLI からアプリの作成

ng new myapp

● アプリの実行

ng serve --open
No.1683
05/21 22:57

edit

ionic で フォームバリデーションを作成する

● フォーム画面 /form/ に フォームバリデーションを作成する

* 1. app/form/form.page.ts へ記述

(モジュールの読み込み)
app/form/form.page.ts

// add
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';

(エラーメッセージの定義)
app/form/form.page.ts

	validations = {
		'username': [
			{ type: 'required', message: 'お名前は入力必須です' },
		],
		'email': [
			{ type: 'required', message: 'メールアドレスは入力必須です' },
		],
		'content': [
			{ type: 'required', message: 'お問い合わせ内容は入力必須です' },
		],
	};

(バリデーションフォームの定義)
app/form/form.page.ts

	public loginForm: FormGroup;

	constructor(
		public router: Router, 
		public formBuilder: FormBuilder) {

		this.loginForm = new FormGroup({
			username: new FormControl('', Validators.compose([
				Validators.required
			])),
			email: new FormControl('', Validators.compose([
				Validators.required
			])),
			content: new FormControl('', Validators.compose([
				Validators.required
			])),
		});
	}

* 2. app/form/form.page.html へ記述

(お名前フォームのみ)複数入力フォームがある場合は適宜追加すること

<form [formGroup]="loginForm">
    <ion-item>
        <ion-label position="stacked">お名前 <ion-text color="danger">*</ion-text></ion-label>
        <ion-input type="text" formControlName="username"></ion-input>

        <!-- error message -->
        <div class="error-container">
            <ng-container *ngFor="let validation of validations.username">
                <div class="error-message"
                    *ngIf="loginForm.get('username').hasError(validation.type) && (loginForm.get('username').dirty || loginForm.get('username').touched)">
                    <ion-icon name="information-circle-outline" color="danger"></ion-icon>
                    <ion-text color="danger">{{ validation.message }}</ion-text>
                </div>
            </ng-container>
        </div>
        <!-- /error message -->

    </ion-item>

	<div class="ion-padding">
		<ion-button [disabled]="!loginForm.valid" (click)="onClickSubmit()" expand="block" type="submit"
			class="ion-no-margin">送信する</ion-button>
	</div>

</form>

参考 : http://bit.ly/2RkXiXS

No.1680
01/24 19:17

edit

ionic で 次のページへリンクする / 遷移したページに戻るボタンをつける

https://localhost/posts/https://localhost/posts-show/123 への画面遷移を考えてみます。

* 1. posts-show ページの作成

ionic generate page posts-show

* 2. ルーティング posts-show/123 を受けれるようにする

app/posts-show/posts-show-routing.module.ts

const routes: Routes = [
  {
    path: ':postId',  // ● 引数を受ける変数を追加
    component: PostsShowPage
  }
];

* 3. posts.page.html からリンクを貼る。 app/posts/posts.page.ts に登録する。

app/posts/posts.page.html

	<ion-button expand="block" routerLink="/posts-show/" routerDirection="forward">詳細ページ1</ion-button>

* 4. posts-show ページに戻るボタンをつける

app/posts-show/posts-show.page.html

<ion-header>
	<ion-toolbar>
		// ● 追加 ↓
		<ion-buttons slot="start">
		    	<ion-back-button defaultHref="/posts"></ion-back-button>
		</ion-buttons>
		// ● 追加 ↑
		<ion-title>posts-show</ion-title>
	</ion-toolbar>
</ion-header>

デフォルトのリンク先(/posts)をつけておきます。
https://ionicframework.com/jp/docs/api/back-button

No.1679
01/24 13:29

edit

ionic の 表示イベント(ライフサイクル)

● Ionicの Page Life Cycle

https://ionicframework.com/jp/docs/angular/lifecycle

* 1. AngularのLife Cycle Events

Event Name Description
ngOnInit コンポーネントの初期化中に発生します。このイベントを使用して、ローカルメンバーを初期化し、一度だけ実行する必要があるServiceを呼び出すことができます。
ngOnDestroy Angularがビューを破棄する直前に発生します。 observables の unsubscribe などのクリーンアップに役立ちます。

* 2. IonicのPage Events

Event Name Description
ionViewWillEnter コンポーネントが表示されるアニメーションがはじまる時に発火します。
ionViewDidEnter コンポーネントが表示されるアニメーションが終了した時に発火します。
ionViewWillLeave コンポーネントを離脱するアニメーションがはじまる時に発火します。
ionViewDidLeave コンポーネントを離脱するアニメーションが終了した時に発火します。
No.1677
01/23 18:31

edit

ionic で now loading のインジケーターを表示させる

● ion-loading

https://ionicframework.com/jp/docs/api/loading

app/posts/posts.page.ts

import { LoadingController } from '@ionic/angular';

constructor に 以下のloadingController を追加

	constructor(
		private http: HttpClient, 
		public loadingController: LoadingController  // この行を追加
	) { }

以下を追加


	async ionViewDidEnter(){
		// define loading
		const loading = await this.loadingController.create({
			spinner: 'circular',
			message: 'loading ...',
			translucent: true,
		});
		// loading 表示
		await loading.present();

		// Make the HTTP request:
		this.http.get('https://YOUR-SERVER.TLD/api/posts').subscribe(data => {
			console.log(data);
			loading.dismiss();  // これを追加(jsonデータ完了時に loading を非表示とする)
		});
	}

No.1676
01/23 18:32

edit

No.1674
01/22 17:30

edit

ionic の 問い合わせフォームを作成する

● まずは一番シンプルなフォームを作成する

app/form/form.page.html

	<form #form="ngForm" (ngSubmit)="postForm(form.value)">
		<ion-item>
			<ion-label position="stacked">お名前 <ion-text color="danger">*</ion-text></ion-label>
			<ion-input type="text" [(ngModel)]="contact.username" name="contact.username"></ion-input>
		</ion-item>

		<ion-item>
			<ion-label position="stacked">メールアドレス <ion-text color="danger">*</ion-text></ion-label>
			<ion-input required email type="email" [(ngModel)]="contact.email" name="contact.email"></ion-input>
		</ion-item>

		<ion-item>
			<ion-label position="stacked">お問い合わせ内容 <ion-text color="danger">*</ion-text></ion-label>
			<ion-textarea [(ngModel)]="contact.content" name="contact.content"></ion-textarea>
		</ion-item>

		<div class="ion-padding">
			<ion-button expand="block" type="submit" class="ion-no-margin">送信する</ion-button>
		</div>
	</form>

app/form/form.page.ts

export class FormPage implements OnInit {

	// フォームパラメーターモデルの定義
	contact = {
		username: '' ,
		email: '' ,
		content: '',
	};

	constructor() {}
	ngOnInit() {}

	postForm(formValue){
		console.log('postForm()');
		console.log( formValue );
	}

}
No.1673
01/22 17:01

edit

No.1672
01/22 14:41

edit

ionic の html で for ループで回す

● ionic の html で for ループで回す

* .ts ファイルで プロパティを宣言

app/posts/posts.page.ts

export class PostsPage implements OnInit {
	posts_loop : {};

	ngOnInit(): void {
                posts_loop = {};
        }

* .html ファイルで ループを回す

app/posts/posts.page.html

	<ion-item *ngFor="let v of posts_loop;">
			<img src="{{v.file_url_array['0']}}" />
	</ion-item>
No.1670
01/21 21:33

edit

ionic の HttpClientModule で サーバの json を読み込む

● 1. モジュール HttpClientModule の登録

app/app.module.ts

// ● Add this
import { HttpClientModule } from '@angular/common/http';

  imports: [
    BrowserModule,
    HttpClientModule, // ● Add this
    ..... ,
    ..... ,

● 2. 各ページで使用する

例 (app/posts/posts.page.ts)

app/posts/posts.page.ts


// ● Add this
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';

....
....
        // ● Add this
	constructor(private http: HttpClient) { }

        // ● Add this
	ngOnInit(): void {
		// Make the HTTP request:
		this.http.get('https://YOUR-SERVER.TLD/api/posts').subscribe(data => {
			console.log(data);
			console.log(data[1]['name']);
		});
	}

No.1668
01/21 21:33

edit

ionic で画面を追加する

● ionic generate

ionic generate <type> <name> [options]

コマンド例

ionic generate
ionic generate page
ionic generate page contact
ionic generate component contact/form
ionic generate component login-form --change-detection=OnPush
ionic generate directive ripple --skip-import
ionic generate service api/user

実際の例

(posts ページを追加してみます。)(フォルダも作成できます。その場合は posts/index のように記述します。)

ionic generate page posts

こちらのファイルが追加されます

CREATE src/app/posts/posts-routing.module.ts (343 bytes)
CREATE src/app/posts/posts.module.ts (465 bytes)
CREATE src/app/posts/posts.page.scss (0 bytes)
CREATE src/app/posts/posts.page.html (124 bytes)
CREATE src/app/posts/posts.page.spec.ts (640 bytes)
CREATE src/app/posts/posts.page.ts (252 bytes)
UPDATE src/app/app-routing.module.ts (712 bytes)

1. サイドメニューにページを追加する

app/app.component.ts

  public appPages = [
    {
      title: 'Home',
      url: '/home',
      icon: 'home'
    },
    {
      title: 'List',
      url: '/list',
      icon: 'list'
    },
// 追加
    {
      title: 'Posts',
      url: '/posts',
      icon: 'clipboard'
    },
// 追加
  ];

icon はこちらから調べます
https://ionicons.com/

これで、サイドメニューに追加されて画面遷移が確認できます。

追加されたページに sidemenu を追加する

app/posts/posts.page.html

<ion-header>
	<ion-toolbar>
		<ion-title>posts</ion-title>
	</ion-toolbar>
</ion-header>

<ion-header>
	<ion-toolbar>
		<ion-buttons slot="start">
			<ion-menu-button></ion-menu-button>
		</ion-buttons>
		<ion-title>
			posts
		</ion-title>
	</ion-toolbar>
</ion-header>
No.1665
01/23 09:57

edit

No.1664
01/21 14:39

edit

ionicのはじめ方

● ionic の インストール

npm i -g ionic cordova

● ionic アプリの新規作成

Angular 4+ w/ Angular CLI

ionic start myapp

または
Angular 2/3 w/ @ionic/app-scripts

ionic start myapp --type=ionic-angular

ionic-angular のところは プロジェクトのタイプ を選択します。

Project Type Description
angular Ionic Angular 4+ w/ Angular CLI for Tooling
ionic-angular Ionic Angular 2/3 w/ @ionic/app-scripts for Tooling
ionic1 Ionic 1 w/ AngularJS

テンプレートの選択

 Starter template: (Use arrow keys)
❯ tabs     | A starting project with a simple tabbed interface 
  sidemenu | A starting project with a side menu with navigation in the content area 
  blank    | A blank starter project 
  super    | A starting project complete with pre-built pages, providers and best practices for Ionic development. 
  tutorial | A tutorial based project that goes along with the Ionic documentation 
  aws      | AWS Mobile Hub Starter 

● ionic アプリの起動

cd myapp
ionic serve --devapp

● 実機(iPhone , Android , iPad)で確認

ionic devapp をインストールします
https://ionicframework.com/docs/appflow/devapp

● チュートリアル

https://ionicframework.com/jp/docs/angular/your-first-app

● ionicをビルドしてWEBサーバへデプロイする

ionic build

www フォルダをアップロードすれば普通に動きます。

● ionic のバージョン確認

ionic info

--type=ionic-angular で作成したアプリの場合

Ionic:
   Ionic CLI          : 5.4.13 (/Users/kato/.anyenv/envs/nodenv/versions/12.14.0/lib/node_modules/ionic)
   Ionic Framework    : ionic-angular 3.9.9
   @ionic/app-scripts : 3.2.4

Utility:
   cordova-res : 0.8.1
   native-run  : not installed

System:
   NodeJS : v12.14.0 (/Users/kato/.anyenv/envs/nodenv/versions/12.14.0/bin/node)
   npm    : 6.13.4
   OS     : macOS Catalina

ionic4アプリの場合

Ionic:
   Ionic CLI                     : 5.4.13 (/Users/kato/.anyenv/envs/nodenv/versions/12.14.0/lib/node_modules/ionic)
   Ionic Framework               : @ionic/angular 4.11.7
   @angular-devkit/build-angular : 0.801.3
   @angular-devkit/schematics    : 8.1.3
   @angular/cli                  : 8.1.3
   @ionic/angular-toolkit        : 2.1.1

Cordova:
   Cordova CLI       : 9.0.0 (cordova-lib@9.0.1)
   Cordova Platforms : not available
   Cordova Plugins   : not available

Utility:
   cordova-res : 0.8.1
   native-run  : not installed

System:
   NodeJS : v12.14.0 (/Users/kato/.anyenv/envs/nodenv/versions/12.14.0/bin/node)
   npm    : 6.13.4
   OS     : macOS Catalina
No.1650
01/08 19:42

edit