Angular4入門_Vol.12
参考サイト
https://www.codingforentrepreneurs.com/projects/try-angular-v4/
Two Way Data Binding
search機能を実装する。
$ ng g component search
でsearch componentを作成する。search.component.tsの、selectorを、searchに変更する。
また、app.component.htmlの、navbar formの箇所をコメントアウトする。
<search></search> <!-- <form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search"> </div> <button type="submit" class="btn btn-default">Submit</button> </form> -->
無事search works!が表示された!
次にコメントアウトしたformのhtmlをsearch.component.htmlに移動させる。
<form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search"> </div> <button type="submit" class="btn btn-default">Submit</button> </form>
これで無事、search boxが表示された(^o^)
two way bindingを試してみる。まずは, ngModelを指定する。
<form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search" [(ngModel)='searchQuery']> </div> <button type="submit" class="btn btn-default">Submit</button> </form>
- error 発生!
Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.
とのことなので、他のフォームと区別するために、name attributeの設定が必要ぽい。
<form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search" name='searchQuery' [(ngModel)]='searchQuery' > </div> <button type="submit" class="btn btn-default">Submit</button> </form>
- searchQueryを、
{{ searchQuery }}
で挿入すると、inputされた文字列などがそこに反映される。すごい!!
<form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search" name='searchQuery' [(ngModel)]='searchQuery' > </div> <button type="submit" class="btn btn-default">Submit {{ searchQuery }}</button> </form>
まとめ
- two way bindingめっちゃ便利ぽいな。ngModelを指定 > 入力された値をngModelで指定した変数?に代入 > 指定した変数は、{{}} でcallすることが可能!
Angular4入門_Vol.11
参考サイト
https://www.codingforentrepreneurs.com/projects/try-angular-v4/
Http & Featured
- home.component.tsのimageのlinkもvideos.JSONデータに組み込む。
[ { "name": "テスト1", "slug": "item-1", "embed": "kzjMI00A1f8", "image": "assets/images/nature/4.jpg" }, { "name": "Item 2", "slug": "item-2", "embed": "kzjMI00A1f8", "image": "assets/images/nature/5.jpg" }, { "name": "Item 3", "slug": "item-3", "embed": null, "image": "assets/images/nature/6.jpg" } ]
- home.component.htmlを修正する。homeImageListをJSONデータから読むこむ前提で、プロパティなどを設定する。
<div class="row"> <div class="col-sm-12"> <carousel class='text-center'> <slide *ngFor='let imageObj of homeImageList'> <a class='' href="/videos/video-1" (click)='preventNormal($event, imageObj)'> <img class='img-main-carousel' [src]="imageObj.image" alt="First slide"> </a> <div class="carousel-caption"> <h3>{{imageObj.name}}</h3> <p><a class='btn btn-default' href='/videos/{{ imageObj.slug }}'>View</a></p> </div> </slide> </carousel> </div> </div>
- home.component.tsを修正する。
import { Component, OnInit, OnDestroy } from '@angular/core'; // OnDestroyを追加でimportする import { Router } from '@angular/router'; import { Http } from '@angular/http'; // Httpモジュールをimport @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit, OnDestroy { // implementsにOnDestroyを追加 private req: any; // req変数を定義 homeImageList = [ {image: "assets/images/nature/4.jpg", name: "Image 4", slug:'video-1'}, //JSONデータに合わせて、修正する。 {image: "assets/images/nature/5.jpg", name: "Image 5", slug:'video-1'}, {image: "assets/images/nature/6.jpg", name: "Image 6", slug:'video-1'} ] constructor(private http:Http, private router:Router) { } // http:Httpで、型定義する。 ngOnInit() { this.req = this.http.get('assets/json/videos.json').subscribe(data=>{ //subscribeしてsuccessのときの処理を定義する。 console.log(data.json()) this.homeImageList = data.json(); }) } ngOnDestroy(){ // ngOnDestroyで、unsubscribeする。 this.req.unsubscribe() } preventNormal(event:MouseEvent, image:any){ if (!image.prevented){ event.preventDefault() // image.link = '/videos' // image.prevented = true; this.router.navigate(['./videos']) } } }
これでとりあえず、一旦homeにアクセスしてみる。。。無事表示された!(^^)
次にhomeImageListのデフォルトデータを削除して、
*ngIf
で、homeImageListの有無を確認して、データを読み込むように設定する。(前回の講義で発生したerrorを避けるため)
<div class="row"> <div class="col-sm-12"> <carousel class='text-center' *ngIf='homeImageList'> <slide *ngFor='let imageObj of homeImageList'> <a class='' href="/videos/video-1" (click)='preventNormal($event, imageObj)'> <img class='img-main-carousel' [src]="imageObj.image" alt="First slide"> </a> <div class="carousel-caption"> <h3>{{imageObj.name}}</h3> <p><a class='btn btn-default' href='/videos/{{ imageObj.slug }}'>View</a></p> </div> </slide> </carousel> </div> </div>
これで、再度homeにアクセスしてみる。。。できた!(^^)
JSONデータにあらたにFeaturedという項目を追記する。
[ { "name": "テスト1", "slug": "item-1", "embed": "kzjMI00A1f8", "image": "assets/images/nature/4.jpg", "featured": true }, { "name": "Item 2", "slug": "item-2", "embed": "kzjMI00A1f8", "image": "assets/images/nature/5.jpg", "featured": true }, { "name": "Item 3", "slug": "item-3", "embed": null, "image": "assets/images/nature/6.jpg" } ]
- home.component.tsの方で、featuredでhomeImageListにfilterをかける。
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Router } from '@angular/router'; import { Http } from '@angular/http'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit, OnDestroy { private req: any; homeImageList = [ // {image: "assets/images/nature/4.jpg", name: "Image 4", slug:'video-1'}, // {image: "assets/images/nature/5.jpg", name: "Image 5", slug:'video-1'}, // {image: "assets/images/nature/6.jpg", name: "Image 6", slug:'video-1'} ] constructor(private http:Http, private router:Router) { } ngOnInit() { this.req = this.http.get('assets/json/videos.json').subscribe(data=>{ console.log(data.json()) data.json().filter(item=>{ // ngOnInitで、featuredの値でfilterする。 if(item.featured){ this.homeImageList.push(item) } }) // this.homeImageList = data.json(); }) } ngOnDestroy(){ this.req.unsubscribe() } preventNormal(event:MouseEvent, image:any){ if (!image.prevented){ event.preventDefault() // image.link = '/videos' // image.prevented = true; this.router.navigate(['./videos']) } } }
まとめ
Angular4入門_Vol.10
参考サイト
https://www.codingforentrepreneurs.com/projects/try-angular-v4/
Http Requests
[ { "name": "Item 1", "slug": "item-1", "embed": "kzjMI00A1f8" }, { "name": "Item 2", "slug": "item-2", "embed": "kzjMI00A1f8" }, { "name": "Item 3", "slug": "item-3", "embed": null } ]
- video-list.component.ts に追記する。
import { Component, OnInit, OnDestroy } from '@angular/core'; // OnDestroyをimportする import { Http } from '@angular/http'; // Http moduleをimportする @Component({ selector: 'video-list', templateUrl: './video-list.component.html', styleUrls: ['./video-list.component.css'] }) export class VideoListComponent implements OnInit { private req:any; // req アノテーションをanyで定義する title = "Video List!"; someItem = "<h1>HiHi</h1>"; todayDate; // videoList = ["Item 1", "Item 2", "Item 3"]; videoList = [ { name: "Item 1", slug: "item-1", embed: `kzjMI00A1f8` }, { name: "Item 2", slug: "item-2", embed: `kzjMI00A1f8` }, { name: "Item 3", slug: "item-3", embed: "" }, ] constructor(private http:Http) { } //http の型定義をHttpでセットする。 ngOnInit() { this.todayDate = new Date(); this.req = this.http.get('assets/json/videos.json').subscribe(data=>{ console.log(data.json()) }); //httpのgetmethodで、jsonファイルからデータを持ってくる。成功したら、data(jsonファイルの中身)をconsole.log(data.json())という形で、console.logに代入する。 } ngOnDestroy(){ this.req.unsubscribe() } // ngOnDestroyお設定しておく。(インスタンスが破棄されたときに、reqのsubscribeを中止する。) getEmbedUrl(item){ return 'https://www.youtube.com/embed/' + item.embed; } }
- videoListの読み込みをjsonからに変更する。
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Http } from '@angular/http'; @Component({ selector: 'video-list', templateUrl: './video-list.component.html', styleUrls: ['./video-list.component.css'] }) export class VideoListComponent implements OnInit { private req:any; title = "Video List!"; someItem = "<h1>HiHi</h1>"; todayDate; videoList: [any]; // videoList: [any] ; でclass定義する。 constructor(private http:Http) { } ngOnInit() { this.todayDate = new Date(); this.req = this.http.get('assets/json/videos.json').subscribe(data=>{ console.log(data.json()) this.videoList = data.json() as [any]; // videoListに、data.json()をリストany型として代入する。 }); } ngOnDestroy(){ this.req.unsubscribe() } getEmbedUrl(item){ return 'https://www.youtube.com/embed/' + item.embed; } }
- video-detail.component.ts にもjsonデータを使ってみる。
import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Http } from '@angular/http'; //Httpをimport @Component({ selector: 'video-detail', templateUrl: './video-detail.component.html', styleUrls: ['./video-detail.component.css'] }) export class VideoDetailComponent implements OnInit, OnDestroy { private routeSub:any; private req:any; slug: string; constructor(private route: ActivatedRoute, private http: Http) { } // httpのclassを定義 ngOnInit() { this.routeSub = this.route.params.subscribe(params => { console.log(params) this.slug = params['slug'] this.http.get('assets/json/videos.json').subscribe(data=>{ data.json().filter(item=>{ console.log(item) // jsonデータを取得>subscribe>success>data.json()にitemでfilterをかけて、itemを表示する。itemは、 }) }) }) } ngOnDestroy(){ this.routeSub.unsubscribe() this.req.unsubscribe() } }
- video変数を定義して、itemオブジェクトをvideo変数に代入する。
import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Http } from '@angular/http'; @Component({ selector: 'video-detail', templateUrl: './video-detail.component.html', styleUrls: ['./video-detail.component.css'] }) export class VideoDetailComponent implements OnInit, OnDestroy { private routeSub:any; private req:any; video:any; //videoを設定する。 slug: string; constructor(private route: ActivatedRoute, private http: Http) { } ngOnInit() { this.routeSub = this.route.params.subscribe(params => { console.log(params) this.slug = params['slug'] this.http.get('assets/json/videos.json').subscribe(data=>{ data.json().filter(item=>{ // console.log(item) if (item.slug == this.slug){ this.video = item //itemオブジェクトをvideoに代入する。 } }) }) }) } ngOnDestroy(){ this.routeSub.unsubscribe() this.req.unsubscribe() } }
- video変数をvideo-detail.component.html に反映する。
<h1>{{ video.name }}</h1> <p> {{ video.slug }} </p>
- error!以下のようにngifを入れるとerrorが解消される、、、なぜだ。
<div *ngIf='video'> <h1>{{ video.name }}</h1> <p> {{ video.slug }} </p> </div>
- どうも、videoのdefault valueが設定されていないと、htmlを読み込んだ時点で、videoオブジェクトが無いと判断されて、video.nameが呼び出すことができないということ?とりあえず、video-detail.component.tsのvideoのvalueを設定する。
video = { name: "Default", slug: "item-1", embed: "kzjMI00A1f8" };
- そして、htmlも、ngIfを外してみる。
<h1>{{ video.name }}</h1> <p> {{ video.slug }} </p>
無事表示された。やはりdefaultが設定されていないことが原因らしい。
最終的に、htmlファイルを以下のように修正した。
<!-- ngIfで、videoの有無を判断 >videoオブジェクトが無い場合は、Loading表示 >ある場合は、videoオブジェクトから、プロパティを取り出す。 video?の部分は、videoがあるかないかを確認してある場合に、nameプロパティを取り出す--> <div *ngIf='!video'>Loading...</div> <h1>{{ video?.name }}</h1> <p> {{ video?.slug }} </p>
- video-detail.component.tsも以下のようにdefaultを設定していない状態に戻す。(これでも今回はうまく表示されるはず)
import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Http } from '@angular/http'; @Component({ selector: 'video-detail', templateUrl: './video-detail.component.html', styleUrls: ['./video-detail.component.css'] }) export class VideoDetailComponent implements OnInit, OnDestroy { private routeSub:any; private req:any; // video = { // name: "Default", // slug: "item-1", // embed: "kzjMI00A1f8" // }; video:any; slug: string; constructor(private route: ActivatedRoute, private http: Http) { } ngOnInit() { this.routeSub = this.route.params.subscribe(params => { console.log(params) this.slug = params['slug'] this.http.get('assets/json/videos.json').subscribe(data=>{ data.json().filter(item=>{ // console.log(item) if (item.slug == this.slug){ this.video = item } }) }) }) } ngOnDestroy(){ this.routeSub.unsubscribe() this.req.unsubscribe() } }
まとめ
- httpインスタンスのgetメソッドなどは使いやすそうやな。たぶん、フォームとかだとpostを使うんやろな。
- default valueが設定されているか、ngIfなどで、オブジェクトの有無を判断しないとうまく表示されない場合があるというのは、覚えておこう。
Angular4入門_Vol.9
参考サイト
https://www.codingforentrepreneurs.com/projects/try-angular-v4/
Angular Click Events
- home.component.html にclickeventを追記する。
<a class='' href="/videos/video-1" (click)='preventNormal($event)'>
- home.component.tsにpreventNormalを定義する。
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit { constructor() { } ngOnInit() { } preventNormal(event:any){ console.log(event) event.preventDefault() alert("Working...") } }
これでDefaultで起動する、指定URLへの移動がpreventされて、alertが表示されるようになる!
ただ、なぜ$eventでないといけないかがよくわからない。。。(^^;)
home.component.tsの、event:anyの箇所を修正する。
preventNormal(event:MouseEvent){ console.log(event) event.preventDefault() alert("Working...") }
- 次にimageのリンクを取得して、それを(click)に代入してみる。
<div class="row"> <div class="col-sm-12"> <carousel class='text-center'> <slide> <a class='' #imageOne href="/videos/video-1" (click)='preventNormal($event, imageOne)'> <img class='img-main-carousel' src="assets/images/nature/4.jpg" alt="First slide"> </a> <div class="carousel-caption"> <h3>First slide label</h3> <p>Nulla vitae elit libero, a pharetra augue mollis interdum.</p> <p><a class='btn btn-default' href='/videos/video-1'>Video 1</a></p> </div> </slide> <slide> <a class='' #imageTwo href="/videos/video-1" (click)='preventNormal($event, imageTwo)'> <img class='img-main-carousel' src="assets/images/nature/5.jpg" alt="Second slide"> </a> <div class="carousel-caption"> <h3>Second slide label</h3> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <p><a class='btn btn-default' href='/videos/video-1'>Video 1</a></p> </div> </slide> <slide> <a class='' #imageThree href="/videos/video-1" (click)='preventNormal($event, imageThree)'> <img class='img-main-carousel' src="assets/images/nature/6.jpg" alt="Third slide"> </a> <div class="carousel-caption"> <h3>Third slide label</h3> <p>Praesent commodo cursus magna, vel scelerisque nisl consectetur.</p> <p><a class='btn btn-default' href='/videos/video-1'>Video 1</a></p> </div> </slide> </carousel> </div> </div>
- home.component.tsに追記する。
preventNormal(event:MouseEvent, image:any){ event.preventDefault() console.log(image.getAttribute("href")) // imageは、elementのため、getAttributeで、属性を取得できる! // alert("Working...") }
- さらに、hrefに新しいurlをsetできるようにsetAttributeを使ってみる。
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit { prevented = false; // preventedを定義する。 constructor() { } ngOnInit() { } preventNormal(event:MouseEvent, image:any){ if (!this.prevented){ // preventedは、初期状態ではfalseのため、最初は実行される。 event.preventDefault() // MouseEventのデフォルトの挙動をpreventする。 console.log(image.getAttribute("href")) image.setAttribute("href", "/videos") // hrefに、/videosを代入(set)する this.prevented = true // 最後にpreventedをtrueにする。 } // alert("Working...") } }
- さらに, preventedをimageが持つpreventedに変更してみる。
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit { prevented = false; constructor() { } ngOnInit() { } preventNormal(event:MouseEvent, image:any){ if (!image.prevented){ event.preventDefault() // console.log(image.getAttribute("href")) image.setAttribute("href", "/videos") image.prevented = true } // alert("Working...") } }
*ngFor
を利用して、htmlを書き直すために、home.component.tsを修正する。
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit { // ここを追記する。 homeImageList = [ {image: "assets/images/nature/4.jpg", title: "Image 4", link:'/videos/video-1'}, {image: "assets/images/nature/5.jpg", title: "Image 5", link:'/videos/video-1'}, {image: "assets/images/nature/6.jpg", title: "Image 6", link:'/videos/video-1'}, {image: "assets/images/nature/1.jpg", title: "Image 1", link:'/videos/video-1'} ] constructor() { } ngOnInit() { } preventNormal(event:MouseEvent, image:any){ if (!image.prevented){ event.preventDefault() image.setAttribute("href", "/videos") image.prevented = true } } }
*ngFor
を追記する。
<div class="row"> <div class="col-sm-12"> <carousel class='text-center'> <slide *ngFor='let imageObj of homeImageList'> <a class='' href="/videos/video-1" (click)='preventNormal($event, imageObj)'> <img class='img-main-carousel' [src]="imageObj.image" alt="First slide"> </a> <div class="carousel-caption"> <h3>{{imageObj.title}}</h3> <p><a class='btn btn-default' href='{{ imageObj.link }}'>Video 1</a></p> </div> </slide> </carousel> </div> </div>
ERROR TypeError: image.setAttribute is not a function
が表示される。なぜなら、imageObjは、Jsonオブジェクトのため、setAttributeをもっていない。最終的に以下のようにRouterのrouting機能を利用してセットした。
import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; //Routeはimportする。 @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit { homeImageList = [ {image: "assets/images/nature/4.jpg", title: "Image 4", link:'/videos/video-1'}, {image: "assets/images/nature/5.jpg", title: "Image 5", link:'/videos/video-1'}, {image: "assets/images/nature/6.jpg", title: "Image 6", link:'/videos/video-1'}, {image: "assets/images/nature/1.jpg", title: "Image 1", link:'/videos/video-1'} ] constructor(private router:Router) { } //routerをconstructorにセットする。 ngOnInit() { } preventNormal(event:MouseEvent, image:any){ if (!image.prevented){ event.preventDefault() // image.link = '/videos' // image.prevented = true; this.router.navigate(['./videos']) // navigate機能を利用して、'videos'にアクセスする。 } } }
まとめ
- $サインの意味は、
以下のように$eventオブジェクトを渡してください。$eventは、AngularJSで提供されるイベントオブジェクトです。
とのことなので、angularの場合は、eventは、$をセットする必要がある! imageOne -> imageOneが代入できる仕組み (たぶんid=imageOneという意味で、指定のtagの情報を取得できる)
Angular4入門_Vol.8
参考サイト
https://www.codingforentrepreneurs.com/projects/try-angular-v4/ http://getbootstrap.com/ https://www.npmjs.com/package/ngx-bootstrap http://valor-software.com/ngx-bootstrap/#/
ngx-bootstrap carousel
home componentを作成する。
$ng g component home
import { HomeComponent } from './home/home.component';
を、app.routing.tsに追記する。
import { NgModule } from '@angular/core'; import { RouterModule, Routes} from '@angular/router'; import { HomeComponent } from './home/home.component'; import { VideoListComponent } from './video-list/video-list.component'; import { VideoDetailComponent } from './video-detail/video-detail.component'; const appRoutes: Routes = [ { path:"", component: HomeComponent, }, { path:"videos", component: VideoListComponent, }, { path:"videos/:slug", component: VideoDetailComponent, }, ] @NgModule({ imports: [ RouterModule.forRoot( appRoutes ) ], exports:[ RouterModule ] }) export class AppRoutingModule{ }
- app.module.tsに、carousel をimportする。
// RECOMMENDED (doesn't work with system.js) import { CarouselModule } from 'ngx-bootstrap/carousel'; // or import { CarouselModule } from 'ngx-bootstrap'; @NgModule({ imports: [CarouselModule.forRoot(),...] }) export class AppModule(){}
- home.component.htmlにngx-carouselのsample codeをコピペしてみる。
<carousel> <slide> <img src="assets/images/nature/4.jpg" alt="First slide"> <div class="carousel-caption"> <h3>First slide label</h3> <p>Nulla vitae elit libero, a pharetra augue mollis interdum.</p> </div> </slide> <slide> <img src="assets/images/nature/5.jpg" alt="Second slide"> <div class="carousel-caption"> <h3>Second slide label</h3> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> </div> </slide> <slide> <img src="assets/images/nature/6.jpg" alt="Third slide"> <div class="carousel-caption"> <h3>Third slide label</h3> <p>Praesent commodo cursus magna, vel scelerisque nisl consectetur.</p> </div> </slide> </carousel>
うまく表示された〜!
最終的に、style.css, home.component.htmlを以下のように修正した。
/* You can add global styles to this file, and also import other style files */ .main-container{ min-height: 800px; margin-bottom: 20px; } .img-main-carousel{ max-height: 500px; margin: 0 auto; } .carousel-control.right, .carousel-control.left{ cursor: pointer; }
<div class="row"> <div class="col-sm-12"> <carousel class='text-center'> <slide> <a class='' href="/videos/video-1"> <img class='img-main-carousel' src="assets/images/nature/4.jpg" alt="First slide"> </a> <div class="carousel-caption"> <h3>First slide label</h3> <p>Nulla vitae elit libero, a pharetra augue mollis interdum.</p> <p><a class='btn btn-default' href='/videos/video-1'>Video 1</a></p> </div> </slide> <slide> <a class='' href="/videos/video-1"> <img class='img-main-carousel' src="assets/images/nature/5.jpg" alt="Second slide"> </a> <div class="carousel-caption"> <h3>Second slide label</h3> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <p><a class='btn btn-default' href='/videos/video-1'>Video 1</a></p> </div> </slide> <slide> <a class='' href="/videos/video-1"> <img class='img-main-carousel' src="assets/images/nature/6.jpg" alt="Third slide"> </a> <div class="carousel-caption"> <h3>Third slide label</h3> <p>Praesent commodo cursus magna, vel scelerisque nisl consectetur.</p> <p><a class='btn btn-default' href='/videos/video-1'>Video 1</a></p> </div> </slide> </carousel> </div> </div>
まとめ
- ngx-bootstrap からmoduleをimportするというところ以外は普通のbootstrapと同じように使えるところが便利やなぁ(^^)
Angular4入門_Vol.7
参考サイト
https://www.codingforentrepreneurs.com/projects/try-angular-v4/ http://getbootstrap.com/ https://www.npmjs.com/package/ngx-bootstrap http://valor-software.com/ngx-bootstrap/#/
Bootstrap for Angular - ngx-bootstrap
ngx-bootstrapを使う。以下のサイトに従って、導入をすすめる。 http://valor-software.com/ngx-bootstrap/#/
$ npm install ngx-bootstrap --save
src/index.html に、Bootstrap3を読み込む
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Srvup</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <app-root>Loading...</app-root> </body> </html>
- src/app/app.component.html にnavbarのコードをそのまま転記してみる。
<nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Brand</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> <li role="separator" class="divider"></li> <li><a href="#">One more separated link</a></li> </ul> </li> </ul> <form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search"> </div> <button type="submit" class="btn btn-default">Submit</button> </form> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <router-outlet></router-outlet>
うまく、bootstrapをよみこんで表示された!これだけで、bootstrapが使えるようになるぽいな。
DROPDOWNS moduleをapp.module.tsにimportしてみる。
// RECOMMENDED (doesn't work with system.js) import { BsDropdownModule } from 'ngx-bootstrap/dropdown'; // or import { BsDropdownModule } from 'ngx-bootstrap'; @NgModule({ imports: [BsDropdownModule.forRoot(),...] }) export class AppModule(){}
- dropdown, dropdownToggle,
*dropdownMenu
を埋め込んでみる。
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li> <li><a href="#">Link</a></li> <li class="dropdown" dropdown> <a href="#" dropdownToggle class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul *dropdownMenu class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> <li role="separator" class="divider"></li> <li><a href="#">One more separated link</a></li> </ul> </li> </ul>
- うまく表示された!
まとめ
bootstrapをangularで使う場合は、通常のbootstrapのコードに、angularで定義したtag(selector?)の追記が必要。
ngx-bootstrap がとりあえず、使いやすいぽいぞ。
Angular4入門_Vol.6
参考サイト
https://www.codingforentrepreneurs.com/projects/try-angular-v4/ https://angular.io/docs/ts/latest/guide/pipes.html
Pipes & Custom Pipes
- Pipesを使ってみる。機能みるために、todayDateという変数を定義しておく。
... todayDate; // videoList = ["Item 1", "Item 2", "Item 3"]; ... ngOnInit() { this.todayDate = new Date(); }
<p> {{ title }} {{ todayDate }} </p>
すごい、これだけで、いまの日付が表示されるようになった。Date()はimportしなくていいのか。あと、ngOnInit()とconstructorの違いもよくわかってないな。
dateの表示形式を指定してみる。これがpipeの機能の一種らしい。他にも色々あるらしい!builtin pipeはかなり利用できそうな気がするな。 https://angular.io/docs/ts/latest/guide/pipes.html
<p> {{ title }} {{ todayDate | date:"MM/dd/yy" }} </p>
(多分)カスタムpipeを作成するために、src/app/utility フォルダを作成する。
pipe safe をinstallする。rootフォルダに移動して、pipe safeをinstallする。
$ ng g pipe safe
safe.pipe.tsと、safe.pipe.spec.tsが作成されると思うので、そのファイルをutilityフォルダに移動する。
さらに、app.module.tsのSafePipeのimport fromの箇所のルートをそれに合わせて修正する。
import { SafePipe } from './utility/safe.pipe';
- safe.pipe.tsを以下のようにコードを追記する。
import { Pipe, PipeTransform } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; // video-list.component.tsからコピペしてくる。 @Pipe({ name: 'safe' }) export class SafePipe implements PipeTransform { constructor(private sanitizer: DomSanitizer){} // constructorを定義する。DomSanitizerをsanitizerのアノテーション transform(value: any, args?: any): any { return this.sanitizer.bypassSecurityTrustResourceUrl(value); //ここもvideo-list.componentからコピーしてくくる。引数にはvalueを設定する。 // valueは、transformの引数のvalue } }
- video-list.component.htmlで、
[src]="getEmbedUrl(item) | safe"
をpipeを使って追記する。nameの由来は、以下のコードのようだ。
@Pipe({ name: 'safe' })
ng serve
してみる。。。無事表示された!custom pipeは便利そうだな!itemのembed urlが、nullとか空の場合にerrorがでることがあるようなので、safe pipeに、if文を追記しておく。
import { Pipe, PipeTransform } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; // video-list.component.tsからコピペしてくる。 @Pipe({ name: 'safe' }) export class SafePipe implements PipeTransform { constructor(private sanitizer: DomSanitizer){} // constructorを定義する。DomSanitizerをsanitizerのアノテーション transform(value: any, args?: any): any { if(value){ return this.sanitizer.bypassSecurityTrustResourceUrl(value); } //ここもvideo-list.componentからコピーしてくくる。引数にはvalueを設定する。 // valueは、transformの引数のvalue } }
- でも、
*ngIf
で、表示制限してるから、safe.pipe.tsで制御しなくても、問題なく動作はするぽいな。
まとめ
- pipeはかなりつかえそうなきがするな。builtinをできるだけ使って、builtinでできないものは、builtinを改良して、使うと良さそう。