Angular2/4にてbootstrapのmodalウィンドウを開き、閉じた時に親コンポーネントに値を反映させる
Angular2/4を使ってmodalウィンドウを表示する
bootstrap標準のmodalウィンドウを、
Angularから開こうと頑張った話です。
bootstrapとAngular以外は使っておりません。
0. 準備
0-1. プロジェクトの作成
新規プロジェクトを作成し、サーバをスタートさせます。
ng new modal ng serve
0-2. ngx-bootstrapのパッケージをinstall
npm install ngx-bootstrap --save
0-3. 必要なmoduleを読み込む
// app.module.ts import { FormsModule } from '@angular/forms'; // 追加 import { ModalModule } from 'ngx-bootstrap'; // 追加
0-4. 必要なmoduleをimportする
@NgModule({ imports: [ ModalModule.forRoot(), // 追加 FormsModule // 追加 ], })
0-5. bootstrap.min.cssを読み込む
<!-- index.html --> ... <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> ...
1. ダイアログコンポーネントの作成と編集
1-1. ダイアログコンポーネントの作成
ng g component dialog
1-2. dialog.component.htmlを編集
<!-- dialog.component.html --> <div class="modal fade" bsModal #lgModal="bs-modal" id="sampleModal" tabindex="-1"> <div class="modal-dialog modal-sm"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" (click)="lgModal.hide()"><span>×</span></button> <h4 class="modal-title">テスト</h4> </div> <div class="modal-body"> <input type="text" [(ngModel)]="dialogData"> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" (click)="lgModal.hide()" data-dismiss="modal">キャンセル</button> <button type="button" class="btn btn-info" (click)="onClose()" data-dismiss="modal">決定</button> </div> </div> </div> </div>
1-3. dialog.component.tsを編集
import { Component, OnInit, ViewChild, Input, Output, EventEmitter } from '@angular/core'; import {ModalDirective} from "ngx-bootstrap"; export class DialogComponent implements OnInit { /** modal要素を取得 */ @ViewChild("lgModal") modalRef :ModalDirective; /** 親からdialogDataを受け取る */ @Input() dialogData; /** 親へのイベント通知用 */ @Output() clickCloseButton = new EventEmitter<any>(); /** modal内で扱うデータ */ public data; ... /** 親コンポーネントから呼び出される */ openDialog() { /** ダイアログを開いた時、親データから値を取り直す */ this.data = this.dialogData; this.modalRef.show(); } /** モーダルの閉じるボタンを押した時に呼び出される */ onClose() { this.modalRef.hide(); /** 親コンポーネントへイベントを通知 */ this.clickCloseButton.emit({data: this.dialogData}); } }
2. appコンポーネントの編集
2-1. app.component.htmlを編集
<!-- app.component.html --> {{parentData}} <button (click)="openDialog()" class="btn btn-primary">ダイアログを開く</button> <!-- ダイアログコンポーネント --> <app-dialog [dialogData]="parentData" (clickCloseButton)="getDialogData($event)" #dialog></app-dialog>
親コンポーネントから、
子であるダイアログコンポーネントにparentDataをわたしています。
また、
子コンポーネントから親コンポーネントにclickCloseというイベントをemitし、
getDialogDataという関数で受け取っています。
2-2. app.component.tsを編集
import { Component, ViewChild // 追加 } from '@angular/core'; import { DialogComponent } from './dialog/dialog.component'; // 追加 /** ここで子コンポーネントであるダイアログコンポーネントを取得しています */ @ViewChild("dialog") dialogComponent: DialogComponent; parentData :string = "hello"; openDialog() { this.dialogComponent.openDialog(); } getDialogData(e) { /** 親コンポーネントのデータ書き換えを行います */ this.parentData = e.data; }
結果
参考
【AngularでModal設定】Angular2でダイアログを開く方法 | 思いのままを綴る
お世話になりました。
ソースコード
最近の出来事を元に脚本を書いてみる。その5
はじめに
この話はいい感じにフィクションです。
登場人物
わたし(26)…会社員
ボーカル(20代後半)
ギター(20代後半)
M
時刻は22時を回ったところ。
吉祥寺サンロードは、まるで一方通行になってしまったみたいだ。
人々はみんな、駅の方へ向かって歩いている。
SE
カッティングで弾かれているアコースティックギター
M
ふとギターとすごくいい声が聞こえてきた。
ハスキーな男の声。
すこし路地に入ると、いた。20代後半くらいだろうか。
アフロの兄ちゃんが、朗々と声を張り上げてうたっていた。
M
足をとめると、兄ちゃんにウィンクされた。
そのまま曲が終わり、2,3人の観客からまばらな拍手がおくられる。
兄ちゃん「いい夜だねぇ!」
M
私は曖昧な笑顔を返した。
兄ちゃんがダンボールを出してくれる。そこに座る私。
ダンボール on 路上。ひんやりしている。
兄ちゃん「今日何してたの?」 私「吉祥寺で買い物を」 兄ちゃん「あ〜!俺と一緒だ!」
M
周りから「うそでしょ」とからかわれる兄ちゃん。
おちゃめな人だ。
私「バンド名なんていうんすか?」 兄ちゃん「村上製作所!」 私「村田製作所から来てるんですか?」 兄ちゃん「そーそー! まあ俺の方が先だけどね!!」
M
そういって豪快に笑う。つられて私も笑ってしまった。
??「おーい!」 兄ちゃん「おお!何してたん?」 ??「エフェクターをさ、買ってた」 兄ちゃん「あ、こいつうちのギター!」 私「どうも」 ギターの人「どもども」
M
ギターの人はおもむろにギターを取り出した。セッションするらしい。
M
深夜のサンロードに、
アコースティックギターと少しディストーションがかかったエレキギター。
朗々としたハスキーボイス。
確かに、良い夜かもしれない。
M
曲が終わった。先ほどまで楽しそうにうたっていた兄ちゃんが、
今度は深刻そうな顔をしている。
兄ちゃん「あのさ」 兄ちゃん「今、今言っていい?いいんかなー? このタイミングで言っていいんかなー?」 ギターの人「なんだよ、早く言えよ」 兄ちゃん「……マサちゃん転勤だって。。。」 ギターの人「。。。えぇーーーー!」
M
サンロードに困惑した声が響き渡った。
ギターの人「ドラムいないとキツイよな。。。」 兄ちゃん「……探さないとな。。。」
M
ちょっと笑ってしまった。
僕は学生時代にバンドでドラムをやっていたからだ。
この状況、あの展開に似ている。
とつぜんメンバーの一人が負傷してギターが弾けなくなり、
途方に暮れるメンバーの前にあらわれる主人公。
普段はうだつのあがらない主人公が、そのときだけスターになる。
私「さっきの曲、途中で拍子が変わっておしゃれでしたね」 兄ちゃん「いや~!もうあそこ二度とやんない! 難しいんだよね!」
M
兄ちゃんはあれ、と首をかしげた
兄ちゃん「……ちょっとまって。君音楽やってたっしょ」 私「……昔ちょっとやってました」 兄ちゃん「やっぱそうだよな!あそこ普通の人はわかんねえもん」
M
ここで「ドラムやってました」といったらどうなるんだろう。
私「……じゃあ、私はそろそろ」 兄ちゃん「お。帰る?」
M
手をぶんぶん振る兄ちゃんと、ギターの人。
兄ちゃん「いい夢見ろよ!」 ギターの人「またね〜!」
M
僕はそのまま帰宅した。
【Git】時よ戻れ!〜RPGゲームの「セーブ」とGitの「commit」の比較から、commitとは何かについて考えてみる〜
commitとは結局なんなのか
結論
commitとは、
どのセーブ地点にも戻ることができる
高機能な「セーブ」
解説
RPGのセーブとGitのcommitを比較してみます。
RPGの「セーブ」
ここに冒険の書v0.1があります。
冒険が進んだので、冒険の書v0.2として保存しようとしました。
基本、セーブは上書きなので、
一度保存してしまうと前のデータには戻ることが出来ません。
Gitの「commit」
Gitのcommitは上書きではなく、
「写真をとる」イメージです。
git commit
とすると、パシャッとファイルの写真を取ってくれます。
冒険が進み、冒険の書v0.2としてコミットすると、
こんな感じになります。
お気づきでしょうか。
そう、前の「冒険の書v0.1」も写真としてしっかり残っているのです。
残っていると、そのファイルを復活させる(=戻る)ことができます。
セーブとcommitの違い
ズバリ、
戻れるか戻れないか
という違いです。
まずはこの点を抑えておきましょう。
commitは本当に戻れるのか
本当にcommitは戻れるのでしょうか。
ちょっと試してみましょう。
準備
以下のように、冒険の書を作成し、適当に3つコミットします。
戻してみる
では、冒険の書v0.2に戻してみましょう。
git checkout ハッシュ番号
を使います。
戻れた。
戻れることのメリット
- やりなおせる
バグを出しまくっても、どうしようもないコードを書きまくっても、
いつでも戻ってやりなおせる。
- 安心感
やりなおせるということから、安心感が生まれる。
まとめ
Gitは戻れる。便利。
どんどんコミットしていきましょう。
Angular2/4のngOnChangesはオブジェクトプロパティが変わったことを検知しない〜Lifecycle HooksのngOnChages, ngDoCheckの違い〜
ngOnChangesを使って、子に渡したプロパティが変わったことを検知する
/** app.component.ts */ export class AppComponent { public datas = 1; onClick() { this.datas ++; } }
<!-- app.component.html --> <button (click)="onClick()">押してください</button> <app-child [parentProperty]='datas'></app-child>
<!-- child/child.component.ts --> export class ChildComponent implements OnInit, OnChanges { @Input() parentProperty; constructor() { } ngOnInit() { } ngOnChanges() { console.log('changes'); } }
結果
clickするたび、changesが表示される。
ngOnChangesを使って、子に渡したオブジェクトのプロパティが変わったことを検知する
今度は子Componentにオブジェクトをわたして、
そのオブジェクトのプロパティを変更してみる。
<!-- app.component.ts --> export class AppComponent { public datas = { data1: 1, data2: 2 }; onClick() { this.datas.data1 ++; } }
結果
changesが表示されない。
オブジェクトプロパティの変更は検知されない!
stackoverflowで検索
Q. How can I detect if there is a change to any property on settings?
どうやったらオブジェクトプロパティの変更を検知できますか?
A. Angular will only notice if the object has been changed to a different object (i.e., the object reference changed), so ngOnChanges() can't be used to solve your problem. You could implement the ngDoCheck() method in your MyDirective class.
Angularはオブジェクト自体が変更された時(参照先が変わった時)に検知します。 なのでngOnChangesではダメです。 ngDoCheckを実装してください。
ngDoCheckを実装してみる
<!-- child/child.component.ts --> export class ChildComponent implements OnInit, OnChanges { @Input() parentProperty; constructor() { } ngOnInit() { } ngOnChanges() { console.log('changes'); } ngDoCheck() { console.log('do check'); } }
do checkが表示された。
オブジェクトプロパティの変更が検知されている。
Angular2/4にて、Cannot find name SimpleChangesエラーが出た時の対処法
Angular2/4にて、Cannot find name SimpleChangesエラー
変更検知をしたかったので、以下の記述をしたところ、
Cannot find name 'SimpleChanges'.
というコンパイルエラーが出た。
export class ChildComponent implements OnInit, OnChanges { @Input() parentProperty; private formattedData; constructor() { } ngOnInit() { } ngOnChanges(changes :SimpleChanges) { console.log('changes'); } }
解決方法
import { SimpleChanges } from '@angular/core';
を追加してやればOK.
参考
stackoverflowさん、いつもありがとうございます。
Angular2/4の*ngIfと[hidden]の使い分け/違い〜Component自体は削除せず非表示にするにはどうしたらよいか〜
結論
*ngIfを使うとComponent自体消えてしまうが、
[hidden]を使うと非表示になるだけでComponentはそのまま残る。
*ngIfの場合
<app-sample *ngIf="showFlg"></app-sample>
[hidden]の場合
<app-sample [hidden]="showFlg"></app-sample>
いきさつ
Angular2/4にて、こんなことがあった
2つのComponentA、ComponentBがある
タブAがアクティブの時ComponentA、
タブBがアクティブの時ComponentBが表示される
ComponentAの値の変化に応じて、ComponentBの値を変化させる
この時、はじめはこのように実装しようと思った。
初めに考えた方法
*ngIfを使って実装してみた。
<button (click)="onClick()">タブ切り替えボタン</button> <app-a *ngIf="showFlg"></app-a> <app-b *ngIf="!showFlg"></app-b>
export class AppComponent { public showFlg :boolean = true; /** タブ切り替えボタン */ onClick() :void { this.showFlg = !this.showFlg; } }
*ngIfの結果
↑ DOMごと消えてしまっている
この方法だと、
ComponentAが表示されているときはComponentBが破棄されてしまっているので、
Serviceを通じてComponentBの値を操作することが出来ない。悲しい。
次に考えた方法
Componentを削除せずに、非表示にしたい。と思ったので「ngif compnent not destroy」で検索。
angular - Angular2: ngIf without destroying component - Stack Overflow
Q. Is there an angular2 blessed way to hide a component without destroying the component? A. The [hidden] property is what you are looking for. It more or less replaced ng-show / ng-hide in Angular2.
ありがとうstackoverflow。
以下のように[hidden]を使って実験してみる。
<button (click)="onClick()">タブ切り替えボタン</button> <app-a [hidden]="showFlg"></app-a> <app-b [hidden]="!showFlg"></app-b>
[hidden]の結果
↑ hiddenが付くだけで、DOMとしては残っている
うまくComponentが消えずに残ってくれた!
Angular2/4にてjQueryを読み込んで使用する
Angular2/4にてjQueryを使いたい
どうしても使いたいときってありますよね。
0. プロジェクトを立ち上げる
ng new angular-jquery
1. index.htmlファイルにてjQueryを読み込む
<!-- index.html --> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> </head>
2. AppComponentにて「アンビエント宣言」をする
/** app.component.ts */ import { Component } from '@angular/core'; declare var jquery:any; // 追加 declare var $ :any; // 追加
declareは、アンビエント宣言と言います。
アンビエント宣言は、他のコンポーネント(例えば Web ブラウザや既存の JavaScript ライブラリ)から変数や関数などが提供されることをTypeScript コンパイラに伝えます。これは、既存の JavaScript ライブラリに静的型付けし、TypeScript で利用可能になることを意味します。
3. app.component.tsにjQueryの処理を書き込む
/** app.component.ts */ export class AppComponent { title = 'angular 4 with jquery'; toggleTitle(){ $('.title').slideToggle(); } }
4. app.component.htmlに処理をbind
<h1 class="title" style="display:none"> {{title}} </h1> <button (click)="toggleTitle()"> clickhere</button>
結果
参考
npm install jquery --save npm install --save-dev @types/jquery
を行って導入する方法もあります。