| ページ一覧 | ブログ | twitter |  書式 | 書式(表) |

MyMemoWiki

Angular

提供: MyMemoWiki
2022年1月30日 (日) 14:49時点におけるPiroto (トーク | 投稿記録)による版 (→‎DI)
ナビゲーションに移動 検索に移動

| AngularJS | TypeScript | Google Cloud Platform | Bootstrap | Flask | ブログカテゴリ(Angular) |

目次

Angular

Angular CLI

準備

Quickスタート

  • ダウンロード
> git clone https://github.com/angular/quickstart.git quickstart
  • パッケージのインストール
> cd quickstart
> npm install
  • 実行
> npm start

Angular quickstart.png

要素

ルートモジュール
要素名 説明
コンポーネント UI部品
サービス ビジネスロジック
パイプ 表示値の加工、演算
ディレクティブ 文書ツリーの操作
  • モジュール
    • Angularにおけるモジュールの実態は、TypeScriptのクラス
    • 定義しただけではモジュールとみなされず、@NgModuleデコレータで宣言が必要

設定ファイル

設定ファイル 概要
package.json 利用するライブラリ情報
tsconfig.json TypeScriptコンパイラーの動作を
systemjs.config.js モジュールローダー(SystemJS)の設定

インストール

>npm install -g @angular/cli

アプリケーションの生成

> ng new myapp

アプリケーションの実行

> ng serve

Angular CLI の主なコマンド

概要 コマンド
appアプリを生成 ng new app
ひな形の生成 ng generate ...
ビルドして起動 ng serve
ビルド ng build
ユニットテスト ng test
E2Eテスト mg e2e
i118nメッセージを抽出 ng xi18n
指定されたキーワードで検索 ng doc keyword
TSLintによるコードチェック ng lint
現在の設定を取得 ng get key
指定されたキー/値を設定 ng set key=value
Angular CLIのバージョン ng version
ng generate サブコマンド
要素 コマンド
モジュール ng g moduole hoge
コンポーネント ng g component hoge
ディレクティブ ng g directive hoge
パイプ ng g pipe hoge
サービス ng g service hoge
ガード ng g guard hoge
クラス ng g class hoge
インターフェース ng g interface hoge
列挙 ng g enum hoge

ngx-bootstrap

Angular-CLIから利用

インストール
npm install ngx-bootstrap bootstrap --save
src/app/app.module.ts の編集
import { AlertModule } from 'ngx-bootstrap';
     :
@NgModule({
     :
   imports: [AlertModule.forRoot(), ... ],
     :
})
.angular-cli.json に以下を追加
"styles": [
  "../node_modules/bootstrap/dist/css/bootstrap.min.css",
  "styles.css"
],
src/app/app.component.html に以下を追加
<alert type="success">hello</alert>

0167 bootstrap alert.jpg

Angular Material

非同期通信

app.module.ts
import { HttpModule } from '@angular/http';
    :
@NgModule({
    :
  imports: [
     :
    HttpModule,
  ],
     :
})

データバインディング

構文

データ方向 種類 記法
コンポーネント -> ビュー 補間 {{...}}
コンポーネント -> ビュー プロパティ/属性バインディング [property]='value'
ビュー -> コンポーネント イベントバインディング (event)='handler'
コンポーネント <-> ビュー 双方向バインディング [(target)]='value'
  • input/textarea/selectなどフォーム要素をバインドするには、ngModel を利用する
<textarea [(ngModel)]="memo"></textarea>

テンプレート参照変数

    1. 変数名
  • (change)="0"は、イベントトリガーで値を更新するため必要
 <input #txtHoge type="text" (change)="0"/>
 <div>{{txtHoge.value}}</div>

双方向バインディング

  • import と @NgModule の imports に FormModuleを追加
ルートモジュール(app.modules.ts)
import { FormsModule } from '@angular/forms';
   :
@NgModule({
   :
  imports: [
   BrowserModule,
   FormsModule,
     :
ビュー
  • input/textarea/selectなどフォーム要素をバインドするには、ngModel を利用する
  • このためには、name属性の指定が必須
  • ngModelを[(ngModel)]とする
 <select name="selAcion" [(ngModel)]="selectedAction">
     <option *ngFor="let item of testActions" value="{{item}}" >{{item}}</option>
 </select>
コンポーネント
export class AccountComponent implements OnInit {
  testActions: string[] = [,'login','logout','check'];
  selectedAction: string = ;

入力値の加工

  • 上記は、プロパティバインディング、イベントバインディングを組み合わせて、双方向を実現している。
  • データバインディング時に値を加工する場合、[(ngModel)] を [ngModel]と(ngModelChagen) に分解
  • $eventは入力値そのものを表す
<input name="hoge" type="text"
  [ngModel] = "hogeName"
  (ngModelChange) = "hogeName=$event.toUpperCase()" />

イベントバインディング

  • app.component.ts
import { Component } from '@angular/core';
import { componentFactoryName } from '@angular/compiler';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.sass']
})
export class AppComponent {
  title = 'Hot Dog Status';
  status = ;
  save(e: any) {
    this.status = "Save";
  }
  load(e: any) {
    this.status = "Load";
  }
}

.

  • appcomponent.html
 <h1 id="hotDogStatus">{{title}}:{{status}}</h1>
 <input type="textField" id="latestHotDogStatus" />
 <button (click)="save($event)">Save</button>
 <button (click)="load($event)">Load</button>
 
 <router-outlet></router-outlet>

Angular event.png

Form実装例

ラジオボタン

Angular form radio.png

  • テンプレート
 <form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">
 	<ng-container *ngFor="let itm of radioSample; index as i">
 		<label>
 			<input type="radio" name="radioSample"
 				[(ngModel)]="selected"
 				[value]="item.value" [checked]="selected == itm.value"
 				(change)="onRadioChange(i)">{{ itm.label }}
 		</label>
 	</ng-container>
 	<button>submit</button>
 </form>
  • コンポーネント
 selected = "3";
 radioSample = [
   {label:'One', value:'1'},
   {label:'Twe', value:'2'},
   {label:'Three', value:'3'},
   {label:'Four', value:'4'},
   {label:'Five', value:'5'}
 ];
 onRadioChange(index:number) {
   console.log(index);
 }
 onSubmit(form: NgForm) {
   console.log(form.value);
 }

ディレクティブ

NgIf

表示非表示切り替え

<mat-toolbar  *ngIf="isToolbar">...</mat-toolbar>
  • component
export class AppComponent {
  isToolbar = true;
    :

ルーティング

適用

プロジェクト作成

> ng new app --routing
--routing オプションを付与せずにプロジェクトを作成した場合
  • /src/app/app-routing.module.ts を追加
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [];

@NgModule({
  imports: [
    RouterModule.forRoot(routes),
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }
  • /src/app/app.module.ts
import { AppRoutingModule } from './app-routing.module';
   :
@NgModule({
  imports: [
    BrowserModule,
    AppRoutingModule, 
  ],
  providers: [],
  • /src/app/app.component.spec.ts
import { RouterTestingModule } from '@angular/router/testing';
   :
describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule
      ],
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

コンポーネント作成

> ng g component account --routing

ルーティング定義

  • app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AccountComponent } from './account/account.component'

const routes: Routes = [
  {
    path: 'account', component: AccountComponent
  }
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes),
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

利用

<a routerLink="/account">Account</a>
<router-outlet></router-outlet>

クエリパラメータ

import { ActivatedRoute } from '@angular/router';
     :
export class AppComponent implements OnInit {
  isToolbar: boolean = true;

  ngOnInit(): void {
    let outer = this;
    this.route.queryParamMap.subscribe(
      paramsMap => {
        const fullscreen = Boolean(paramsMap.get("fullscreen"));
        outer.isToolbar = !fullscreen;
      });
  }

コンポーネント

 import { Component } from '@angular/core';
 
 @Component({
   selector: 'my-app',
   template: `<h1>Hello {{name}}&kt;/h1>`,
 })
 export class AppComponent  { name = 'Angular'; }
パラメータ名 概要
selector コンポーネントを適用すべき要素を表す
template コンポーネントに適用するビュー

ライフサイクル

ライフサイクル 内容
コンポーネント生成
コンストラクター
ngOnChanges @Input経由で入力値が設定/再設定された
ngOnInit 入力値(@Inputp)が処理された後、コンポーネントの初期化時(最初のngOnChangesメソッドの後で一度だけ)
ngDoCheck 状態の変更を検出したとき
ngAfterContentInit 外部コンテンツを初期化した時(最初のngDoCheckメソッドの後で一度だけ)
ngAfterContentChecked 外部コンテンツの変更をチェックした時
ngAfterViewInit 現在のコンポーネントと子コンポーネントのビューを生成した時(最初のngAfterContentCheckedメソッドの後で一度だけ)
ngAfterViewChecked 現在のコンポーネントと子コンポーネントのビューが変更された時
ngOnDestroyed コンポーネントが破棄される時
コンポーネント破棄

@ViewChildrenデコレーター : 子コンポーネントを取得

  • 現在のビューに配置された子コンポーネントを取得する
  • child: 取得する子コンポーネント、prop:プロパティ名
@ViewChildren(child) prop

@ViewChildデコレーター : 単一の子コンポーネントを取得

  • 取得すべきコンポーネントが単一であることが分かっている場合は、配列を返す@ViewChildrenではなく、@ViewChildを利用できる
  • child: 取得する子コンポーネント、prop:プロパティ名
@ViewChild(child) prop

コンポーネントの連携

  • 複数のコンポーネントが互いに連携して1つの機能を構成
  • @Input() と @Output() を使うことで、 親のコンテキストと子のディレクティブやコンポーネントとの間でデータをシェアすることができます。
  • @Input() プロパティは書き込み可能である一方、@Output() プロパティは観測可能です。

@Input デコレーター : コンポーネントを入れ子に配置

  • 親側から入れ子のコンポーネント属性に値を設定
@Component({
  selector: 'parent',
  template: `<div><app-feed-item [feed]="feed"></app-feed-item></div>`
})
export class Parent implements OnInit {
    feed: Feed;
    ngOnInit(): void {
        this.feeds = new Feeds();
    }
}
  • 入れ子になったコンポーネントのプロパティに、@Inputを付与することで、親側でセットした値を受け取ることができる
@Component({
  selector: 'app-feed-item',
  template: `<div>{{feed.title}}</div>`
})
export class FeedItemComponent {
  @Input() feed: Feed;
}

@Outputデコレーター : 子コンポーネントからイベントを受け取る

サービス

  • サービスクラスであることの条件は、@Injectable デコレータを付与することのみ。
  • @Injectable デコレータは、コンポーネントに対してサービスを引き渡せることを意味する。

登録

  • モジュールにサービスを登録する。
  • コンポーネントにも登録できる(@Componentデコレーターのprovidersパラメータ)。この場合コンポーネントと子コンポーネントのみで利用できる。
import { HogeService } from './hoge.service';

@NgModule({
   :
  providers: [HogeService],
   :
})

依存性注入

  • 方法を宣言するのは、@NgModule/@Component デコレータの providersパラメータ
  • サービスを提供するためのProviderオブジェクトを登録する

使用する

  • 使用するためには、コンストラクタに対応する引数を追加するだけ
  • 以下の例の場合、this.hogeService.メソッド で利用可能
export class FooComponent {
  constructor(
    private hogeService: HogeService
  ){}
}
Providerであることの条件は以下のプロパティを持つこと
プロパティ 内容
provide サービスを注入する際に利用するDIトークン
useXxxxx サービスの生成方法 例 userClass: XXXX と指定するとクラス XXXX を常にnew でインスタンス化する
multi 同一のDIトークンに対して複数のProviderを追加するか

useXxxx

プロパティ 内容
useClass 指定されたクラスを注入のたびにインスタンス化
useValue 指定されたオブジェクトを常に引き渡す(同じ値になる)
useExisting 指定されたトークンのエイリアスを生成
useFactory 指定されたファクトリー関数で注入の際にオブジェクトを生成
useClass
  • 常に新たなインスタンスを生成する
providers: [
  { provide: HogeService, useClass: HogeService } 
]
useValue
  • 常に同じオブジェクトを注入する
  • クラスのインスタンスを渡す
providers: [
  { provide: HogeService, useVlaue: new HogeService() } 
]
useExisting
  • トークンのエイリアスを生成
providers: [
  { provide: HogeService, useClass: HogeService }、
  { provide: HogeAliasService, useExisting: HogeService }、
]

<blockquote>互換性維持など、別のトークンから同一インスタンスを取得したい場合などに利用</blockquote>

useFactory
  • ファクトリー関数経由でインスタンスを生成
providers: [
  { provide: HogeService, useFactory: () => {
      let service = new HogeService();
      service.foo = "bar";
      return service;
    } 
   } 
]

パイプ

  • テンプレート上に埋め込まれたデータを加工・整形する

呼び出し構文

 {{exp | pipe [:param1 [:param2 ..]]}}

標準パイプ

パイプ 内容
lowercase 小文字変換
uppercase 大文字変換
titlecase 単語の先頭を大文字変換
slice 文字列から部分文字列を切り出し
date 日付・時刻整形
number 数値を桁区切り
percent パーセント形式
currency 通貨形式
json オブジェクトをJSON変換
i18nPlural 数値によって表示文字列をへんか
i18nSelect 文字列に応じて出力を切替
async Observable/Promiseによる非同期処理の結果を取得

作成

  • 小文字に変換してブランクを指定文字に置き換える
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'globCategory'
})
export class GlobCategoryPipe implements PipeTransform {

  transform(value: string, ...args: string[]): string {
    if (value != null) {
      const replacement = ((args != null && args[0] != null)?args[0]:'-');
      value = (value.toLowerCase()).replace(/ /g, replacement);
    }
    return value;
  }
}
  • 使用
<a *ngFor="let category of feed.categories" href="https://www.typea.info/blog/index.php/category/{{category | globCategory :'_'}}" target="_blank" class="category-link">
  {{category}}
</a>

Modules

Http

RxJs

リアクティブプログラミングは、データストリームと変更の伝播に関する非同期プログラミングのパラダイムです。 RxJS (Reactive Extensions for JavaScript) は、非同期またはコールバックベースのコード (RxJS Docs) の作成を容易にする observables を使用したリアクティブプログラミング用のライブラリ

  • 非同期処理の既存のコードを observables に変換する
  • ストリーム内の値を反復処理する
  • 異なる型への値のマッピング
  • ストリームのフィルタリング
  • 複数のストリームの作成

Observable 作成関数

ベント、タイマー、promise などから observables を作成するプロセスを簡素化

promise から observable を作成

import { from } from 'rxjs';

// promise から Observable を作成
const data = from(fetch('/api/endpoint'));
// 非同期の結果の購読を開始
data.subscribe({
  next(response) { console.log(response); },
  error(err) { console.error('Error: ' + err); },
  complete() { console.log('Completed'); }
});

カウンターから observable を作成する

import { interval } from 'rxjs';

// 一定間隔でで値を発行するObservable を作成
const secondsCounter = interval(1000);
// 発行される値の購読を開始
secondsCounter.subscribe(n =>
  console.log(`It's been ${n} seconds since subscribing!`));

イベントから observable を作成する

import { fromEvent } from 'rxjs';

const el = document.getElementById('my-element');

// マウスの移動を発行する Observable の作成
const mouseMoves = fromEvent(el, 'mousemove');

// マウス移動イベントを購読開始 
const subscription = mouseMoves.subscribe((evt: MouseEvent) => {
  console.log(`Coords: ${evt.clientX} X ${evt.clientY}`);

  // スクリーンの左端をマウスが越えたら購読中止
  if (evt.clientX < 40 && evt.clientY < 40) {
    subscription.unsubscribe();
  }
});

AJAX リクエストから observable を作成

import { ajax } from 'rxjs/ajax';

// AJAXリクエストを生成する Observable の作成
const apiData = ajax('/api/data');
// リクエストの生成を購読
apiData.subscribe(res => console.log(res.status, res.response));

オペレーター

  • オペレーターは、コレクションの高度な操作を可能にするために、observables 基盤上に構築される関数です。たとえば RxJS は map()、filter()、concat()、flatMap() のようなオペレーターを定義
  • オペレーターは設定オプションをとり、ソースとなる observable を受け取る関数を返します。この返された関数を実行するとき、オペレーターは observable が出力する値を観測、変換し、変換された値の新しい observable を返します
  • RxのflatMapの使い方

Map operator

import { map } from 'rxjs/operators';

const nums = of(1, 2, 3);

const squareValues = map((val: number) => val * val);
const squaredNums = squareValues(nums);

squaredNums.subscribe(x => console.log(x));

// Logs
// 1
// 4
// 9

パイプ

パイプを使用するとオペレーターをリンクすることができます。パイプを使用すると、複数の機能を1つの機能にまとめることができます。pipe() 関数は、結合する関数を引数としてとり、実行時に順次関数を実行する新しい関数を返します。

observable に適用される�オペレーターのセットは、レシピ、つまり関心のある値を生成するための一連の命令です。それだけではレシピは何もしません。レシピを通して結果を出すには subscribe() を呼び出す必要があります。

単独利用

import { filter, map } from 'rxjs/operators';

const nums = of(1, 2, 3, 4, 5);

// Observable を受け入れる関数の生成
const squareOddVals = pipe(
  filter((n: number) => n % 2 !== 0),
  map(n => n * n)
);

// filter と map 関数を実行する Observable を生成
const squareOdd = squareOddVals(nums);

// 結合された関数の実行を購読
squareOdd.subscribe(x => console.log(x));

Observable.pip

pipe() 関数は RxJS Observable のメソッドでもあるため、短く書き換え

import { filter, map } from 'rxjs/operators';

const squareOdd = of(1, 2, 3, 4, 5)
  .pipe(
    filter(n => n % 2 !== 0),
    map(n => n * n)
  );

squareOdd.subscribe(x => console.log(x));

コンポーネントからサービスのデータを購読する

Service
import { Subject } from 'rxjs/subject';
import { User } from './user';
 
@Injectable()
export class AccountService {
  private userChangeAnnouncedSource = new Subject<User>();
  userChangeAnnounced$ = this.userChangeAnnouncedSource.asObservable();
    :
  announceUserChange(user: User) {
    this.userChangeAnnouncedSource.next(user);
  }
    :
Component
import { Subscription } from 'rxjs/Subscription';
  
export class AccountComponent implements OnInit, OnDestroy {
    user: User;
    subscription: Subscription

  ngOnInit() {
    this.subscription = this.accountService.userChangeAnnounced$.subscribe((user:User) => {
      this.user = user;
    });  
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
       :

DI

依存関係を @Optional として @Host で検索を制限

  • コンポーネントが依存関係を要求すると、Angularはそのコンポーネントのインジェクターから開始し、 最初の適切なプロバイダーが見つかるまでインジェクターツリーを検索
  • @Optional プロパティデコレーターは、依存関係が見つからない場合にnullを返す
  • @Host プロパティデコレーターは、上方向への検索を ホストコンポーネント で停止==

@Inject を使用してカスタムプロバイダーを設定

  • カスタムプロバイダーを使用すると、組み込みのブラウザAPIなど、暗黙的な依存関係に対する具体的な実装を提供できる

コンポーネントの DOM 要素を注入

プロバイダーを定

  • 値プロバイダー: useValue
  • クラスプロバイダー: useClass
  • エイリアスプロバイダー: useExisting
  • ファクトリープロバイダー: useFactory

UI

Angular Material

レスポンシブレイアウト

テスト

ユニットテスト

Karma

Firebase

Firestoreの利用

Tips

機能

Chrome拡張機能

augury 0168 chrome extension.jpg

Facebook SDK

Hello.js 認証をまとめるJavaScriptライブラリ

文法

Javascriptのグローバルオブジェクトを利用

declare const hoge;

コード

コードからルーティング

import { Router } from '@angular/router';
   :
constructor(private router: Router) {}
   :
this.router.navigate(['/hoge']) 

CSRF

CORS対応

参照

Firebase