Angular4入門_Vol.15
参考サイト
https://www.codingforentrepreneurs.com/projects/try-angular-v4/
Passing Data to Components
- search.component.ts にコードを追記する。
import { Component, OnInit, Input } from '@angular/core'; //Input をimportする。 import { Router } from '@angular/router'; @Component({ selector: 'search', templateUrl: './search.component.html', styleUrls: ['./search.component.css'] }) export class SearchComponent implements OnInit { searchLocation = 'Newport Beach'; searchQuery: string; //文字列型のsearchQuery変数を定義する。 @Input() //Inputデコレータを使う passedQuery: string; //passedQueryを文字列型で定義する。 constructor(private router: Router ) { } ngOnInit() { console.log(this.passedQuery) //console.logに追記する。 } submitSearch(event, formData){ console.log(event) console.log(formData.value) let searchedQuery = formData.value['q'] if (searchedQuery) { this.router.navigate(['/search', {q: searchedQuery}]) } } searchQueryChange(){ this.searchLocation = 'California' } }
- search-detail.component.htmlのsearch tagに、passedQuery の値を代入してみる。
<search class='text-center search-inline' passedQuery='hello'></search> <p *ngIf='query'> Searched for <b>{{ query }}</b> </p>
Inputは、tagから取ってこれるのか?よくわかんないな。。。
試しに、queryを代入してみる。
<search class='text-center search-inline' [passedQuery]='query'></search> <!-- * passedQueryを[]で囲む * queryは、search-detailからのcontextで、それをsearchtagの元のcomponentの、search.component.tsのpassedQueryに代入される。 * query > passedQuery > console に表示されるという流れになっている。 --> <p *ngIf='query'> Searched for <b>{{ query }}</b> </p>
- さらにsearch.component.tsを修正する。
import { Component, OnInit, Input } from '@angular/core'; import { Router } from '@angular/router'; @Component({ selector: 'search', templateUrl: './search.component.html', styleUrls: ['./search.component.css'] }) export class SearchComponent implements OnInit { searchLocation = 'Newport Beach'; searchQuery: string; @Input() passedQuery: string; constructor(private router: Router ) { } ngOnInit() { // console.log(this.passedQuery) if (this.passedQuery){ //passedQueryがある場合 this.searchQuery = this.passedQuery // passedQueryをsearchQueryに代入する。 } } submitSearch(event, formData){ console.log(event) console.log(formData.value) let searchedQuery = formData.value['q'] if (searchedQuery) { this.router.navigate(['/search', {q: searchedQuery}]) } } searchQueryChange(){ this.searchLocation = 'California' } }
- navbarのsearchboxに、同じような処理を追加するために、app.component.tsにコードを追記する。
import { Component, OnInit } from '@angular/core'; //OnInitを追加する import { ActivatedRoute } from '@angular/router'; // ActivatedRouteをimport @Component({ selector: 'app-root', templateUrl: './app.component.html', // template: `<h1>{{ title }} is cool!</h1> <p>{{ description }}</p>`, //templateには、直接htmlの記述もできる styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { // OnInitをimplementsする。 title = 'Hello srvup!!2 '; description = '新しいアプリケーションです'; query: string; // queryを定義 private routeSub:any; // routeSubを定義する。 constructor(private route: ActivatedRoute){} // constructorで、routeを定義する。 ngOnInit() { this.routeSub = this.route.params.subscribe(params=>{ //paramsを代入する console.log(params) this.query = params['q'] }) } }
- app.component.htmlを修正する。
<search [passedQuery]='query'></search> <!-- search.component.tsの、passedQuery変数に、app.component.tsのqueryを代入する -->
- ngOnInitではなく、constructorにコードを記載しても機能する。
import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-root', templateUrl: './app.component.html', // template: `<h1>{{ title }} is cool!</h1> <p>{{ description }}</p>`, //templateには、直接htmlの記述もできる styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { title = 'Hello srvup!!2 '; description = '新しいアプリケーションです'; query: string; private routeSub:any; constructor(private route: ActivatedRoute){ this.routeSub = route.params.subscribe(params=>{ console.log(params) this.query = params['q'] }) } ngOnInit() { } ngOnDestroy(){ this.routeSub.unsubscribe() } }
まとめ
- Inputデコーレータは、ほぼ予想通り、html内で、独自の属性を持たせることができるデコレータのようだ。
<search [passedQuery]='query'></search> <!-- 上記の場合 search.component.tsに、 @Input() passedQuery: string; と定義することで、searchタグ内で、passedQueryという属性を利用することができる。 -->
@Input
については、以下のサイトがわかりやすかったです! http://qiita.com/armorik83/items/a14a4298ead3d18b58a3また、以下のような記述も可能。ただ、これだとtwo way data bindingができないのかな。
<search passedQuery='{{ query }}'></search> <!-- <search [passedQuery]='query'></search> と同じような意味だが、微妙に挙動がちがう。。。 -->
Angular4入門_Vol.14
参考サイト
https://www.codingforentrepreneurs.com/projects/try-angular-v4/
Search Detail
- search.component.tsにrouterをつける。
import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; // import Router @Component({ selector: 'search', templateUrl: './search.component.html', styleUrls: ['./search.component.css'] }) export class SearchComponent implements OnInit { searchLocation = 'Newport Beach' constructor(private router: Router ) { } // constructorに、routerを定義する。 ngOnInit() { } submitSearch(event, formData){ console.log(event) console.log(formData.value) let query = formData.value['q'] // formDataから、qの値を取り出し、queryという変数に代入する。 if (query) { this.router.navigate(['/search', {q: query}]) // navigateで、/search;q= というurlにあくせすするように設定する。 } } searchQueryChange(){ this.searchLocation = 'California' } }
- これで、searchを実行してみる。。。Error発生!
Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'search;q=ddd' Error: Cannot match any routes. URL Segment: 'search;q=ddd'
- urlを設定する必要あり。app.routing.tsに追記する。
const appRoutes: Routes = [ ... { path:"search", // /search の場合のcomponentをセット component: VideoListComponent, } ... ]
これで、searchをクリックすると、video-listのpageにアクセスされていればOK!
search-detail componentを作成する。
ng g component search-detail
を実行する。app.routing.tsのpath: “search"の箇所のcomponentを、
SearchDetailComponent
に変更する。
import { SearchDetailComponent } from './search-detail/search-detail.component'; ... { path:"search", component: SearchDetailComponent, },
searchボタンをクリックして、
search-detail works!
が表示されていればOK!search-detail.component.tsを編集する。
import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; // ActivatedRouteをimportする。Routerとの違いは何か? @Component({ selector: 'app-search-detail', templateUrl: './search-detail.component.html', styleUrls: ['./search-detail.component.css'] }) export class SearchDetailComponent implements OnInit, OnDestroy { private routeSub:any; // routeSubを定義する。 constructor(private route: ActivatedRoute) { } // route: ActivatedRouteを定義する ngOnInit() { this.routeSub = this.route.params.subscribe(params=>{ console.log(params) //route.paramsをsbuscribeして、成功した場合、paramsをconsoleに表示する。 }) } ngOnDestroy(){ //OnDestroyのときに、unsubscribeする。 this.routeSub.unsubscribe() } }
- search-detail.component.tsをさらに修正する。
import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-search-detail', templateUrl: './search-detail.component.html', styleUrls: ['./search-detail.component.css'] }) export class SearchDetailComponent implements OnInit, OnDestroy { private routeSub:any; query: string; // queryを定義する。 constructor(private route: ActivatedRoute) { } ngOnInit() { this.routeSub = this.route.params.subscribe(params=>{ console.log(params) this.query = params['q'] // paramsから、qのvalueを取り出して、queryに代入する。 }) } ngOnDestroy(){ this.routeSub.unsubscribe() } }
- queryを、search-detail.component.htmlに表示させる。
<p> Searched for {{ query }} </p>
searchボタンをクリックしたら、q= 以降のvalueが、queryに代入されて、表示される。
search boxの値が空の場合と、そうでない場合で分ける。
<p *ngIf='query'> Searched for <b>{{ query }}</b> </p> <div class="text-center" *ngIf='!query'> <h1>Search Something</h1> <search></search> </div>
- サーチボックスの体裁を整える。
<search class='text-center search-inline'></search> <p *ngIf='query'> Searched for <b>{{ query }}</b> </p>
- style.cssに、search-inlineを追記する。
/* 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; } .search-inline .navbar-left, .search-inline .navbar-right{ float: none !important; }
まとめ
RouterとActivatedRouteの違いは、Routerは、別のurlにアクセスするためのモジュールで、ActivatedRouteは、現在のurlからparamsなどのvalueを取得するためのモジュール。
search componentのRouterで、path:search にアクセス。path:searchにアクセスしたあとは、search-detail.componentが実行されるという二段構えになっている。
Angular4入門_Vol.13
参考サイト
https://www.codingforentrepreneurs.com/projects/try-angular-v4/
ngForm Basics
- navbarのformにngFormを使う準備をする。まず通常のformで実装するときのコードを確認する。search.component.htmlに追記する。
<!-- methodとactionを追加 --> <form class="navbar-form navbar-left" method="get" action="/search/"> <div class="form-group"> <!-- name="q"に変更 --> <input type="text" class="form-control" placeholder="Search" name='q' [(ngModel)]='searchQuery' > </div> <button type="submit" class="btn btn-default"><span *ngIf='!searchQuery'>Search</span><span *ngIf='searchQuery'>Search for... {{ searchQuery }}</span></button> </form>
- ngSubmitを使ってみる。search.component.htmlに追記する。
<form class="navbar-form navbar-left" (ngSubmit)='submitSearch($event)'> <div class="form-group"> <input type="text" class="form-control" placeholder="Search" name='q' [(ngModel)]='searchQuery' > </div> <button type="submit" class="btn btn-default"><span *ngIf='!searchQuery'>Search</span><span *ngIf='searchQuery'>Search for... {{ searchQuery }}</span></button> </form>
- submitSearch をsearch.component.tsに定義する。
submitSearch(event){ console.log(event) }
submitをクリックしたときに、consoleにeventの内容が表示されれば成功!
ngFormをhtmlタグ内で、挿入して、submitSearchメソッドの引数として設定する。
<form #searchForm=(ngForm) class="navbar-form navbar-left" (ngSubmit)='submitSearch($event, searchForm)'>
- search.component.ts にngFormの内容を、formDataという引数で受けるように、追記する。
submitSearch(event, formData){ console.log(event) console.log(formData) }
1点errorが発生!
#searchForm='ngForm'
に修正(間違えて、(ngform)
と設定してしまっていた。)consoleに表示する内容を、formData.valueに設定してみる。
submitSearch(event, formData){ console.log(event) console.log(formData.value) }
Object {q: "ddd"} q:"ddd"
と、formData(ngForm)のObjectが表示される。input tagのnameはここででてくるのか!例えば、
<input type='hidden' name='loc' value='Newport Beach' [(ngModel)]='searchLocation'/>
というように別のnameの ngModelを挿入してみる。認識はしたが、loc: undefinedになってしまう。。。searchLocation のdefault valueは、search.component.tsで設定する必要あり。
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'search', templateUrl: './search.component.html', styleUrls: ['./search.component.css'] }) export class SearchComponent implements OnInit { searchLocation = 'Newport Beach' searchQuery = "New Search" constructor() { } ngOnInit() { } submitSearch(event, formData){ console.log(event) console.log(formData.value) } }
これで無事、searchLocationがconsoleに表示されるようになる。
Object {loc: "Newport Beach", q: "Search"}
といった具合。changeメソッドを使ってみる。
<input type="text" class="form-control" placeholder="Search" name='q' [(ngModel)]='searchQuery' (change)='searchQueryChange()'>
- changeメソッドに代入するsearchQueryChangeを定義する。
searchQueryChange(){ this.searchLocation = 'California' // searchLocationの内容を変更するだけ }
- これを実行すると、
Object {loc: "California", q: "ddd"}
という感じで、two way bindingで、locの内容を書き換えできる!
まとめ
- ngSubmit, ngModel, ngForm, change のangularのmethodを使うと、two way bindingな formが作れる!
<!-- ngFormで、formの内容を取得 > ngSubmitで、submitしたときに、formの内容を引数としたfunctionを実行できる --> <form #searchForm='ngForm' class="navbar-form navbar-left" (ngSubmit)='submitSearch($event, searchForm)'> <!-- ngModelで、dataの双方向のやりとりが可能になる --> <!-- changeで、変更があったときに、実行するメソッドを指定できる。 --> <input type='hidden' name='loc' [(ngModel)]='searchLocation'/> <div class="form-group"> <input type="text" class="form-control" placeholder="Search" name='q' [(ngModel)]='searchQuery' (change)='searchQueryChange()'> </div> <!-- {{}}で、ngModelで定義した変数の中身を表示できる。 --> <button type="submit" class="btn btn-default"><span *ngIf='!searchQuery'>Search</span><span *ngIf='searchQuery'>Search for... {{ searchLocation }}{{ searchQuery }}</span></button> </form>
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の情報を取得できる)