読者です 読者をやめる 読者になる 読者になる

Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

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'])
  }
}

}

まとめ

  • Http モジュールは、いまのところかなり便利ぽい。
  • json()データのfilterも比較的シンプルにできる。
  • defaultデータがない変数にjsonデータを代入する場合は、*ngIfでデータの有無を確認する必要(データの読み込みのtry?)が必要なのか?

Angular4入門_Vol.10

参考サイト

https://www.codingforentrepreneurs.com/projects/try-angular-v4/

Http Requests

  • videoListの中身を、assets/json/videos.jsonファイルに移す。json形式に修正する。
[
  {
    "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

<!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を改良して、使うと良さそう。

Angular4入門_Vol.5

参考サイト

https://www.codingforentrepreneurs.com/projects/try-angular-v4/

Safely Embed a Video

  • youtubeのembed(埋め込み)linkを挿入してみる。
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'video-list',
  templateUrl: './video-list.component.html',
  styleUrls: ['./video-list.component.css']
})
export class VideoListComponent implements OnInit {
  title = "Video List!";
  // videoList = ["Item 1", "Item 2", "Item 3"];
  videoList = [
    {
      name: "Item 1",
      slug: "item-1",
      embed: `<iframe width="560" height="315" src="https://www.youtube.com/embed/kzjMI00A1f8" frameborder="0" allowfullscreen></iframe>`
    },
    {
      name: "Item 2",
      slug: "item-2",
      embed: `<iframe width="560" height="315" src="https://www.youtube.com/embed/kzjMI00A1f8" frameborder="0" allowfullscreen></iframe>`
    },
    {
      name: "Item 3",
      slug: "item-3",
      embed: `<iframe width="560" height="315" src="https://www.youtube.com/embed/kzjMI00A1f8" frameborder="0" allowfullscreen></iframe>`
    },
  ]
  constructor() { }

  ngOnInit() {
  }

}
  • video-list.component.html を修正する。
<p>
  {{ title }}
</p>

<p *ngFor='let item of videoList'>
  <a href="videos/{{ item.slug }}" >{{ item.name }}</a>
  {{ item.embed }}
</p>
  • embed linkなはずだが、うまく埋め込まれていない。さらに修正してみる。
<p>
  {{ title }}
</p>

<div *ngFor='let item of videoList'>
  <a href="videos/{{ item.slug }}" >{{ item.name }}</a>
  <div [innerHTML]='item.embed'></div>
  <div [innerHTML]='"<h1>Hi there</h1>"'></div>
</div>
  • これでもembedは機能しない。ので、いろいろと実験してみる。video-listにsomeItemという変数を設定してみる。
export class VideoListComponent implements OnInit {
  title = "Video List!";
  someItem = "<h1>HiHi</h1>";
...
}
<p>
  {{ title }}
</p>

<div *ngFor='let item of videoList'>
  <a href="videos/{{ item.slug }}" >{{ item.name }}</a>
  <div [innerHTML]='item.embed'></div>
  <div [innerHTML]='someItem'></div>
</div>
  • HiHiの方は、htmlが読み込まれた状態で表示される。ただ、埋め込みリンクはうまくいかない。だから、別の方法で、埋め込みリンクを利用してみる。
<p>
  {{ title }}
</p>

<div *ngFor='let item of videoList'>
  <a href="videos/{{ item.slug }}" >{{ item.name }}</a>
  <div [innerHTML]='item.embed'></div>
  <div [innerHTML]='someItem'></div>
  <iframe width="560" height="315" src="https://www.youtube.com/embed/{{ item.embed }}" frameborder="0" allowfullscreen></iframe>
</div>
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'video-list',
  templateUrl: './video-list.component.html',
  styleUrls: ['./video-list.component.css']
})
export class VideoListComponent implements OnInit {
  title = "Video List!";
  someItem = "<h1>HiHi</h1>";
  // 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() { }

  ngOnInit() {
  }

}
  • これでもうまくいかない、、、なぜだ。errorは、: unsafe value used in a resource URL context (see http://g.co/ng/security#xss)と表示されているので、たぶんこれが問題だろう。srcの部分をng仕様に変更する。また、embed urlも少し加工したurlを渡すため、getEmbedUrlを定義する。
<p>
  {{ title }}
</p>

<div *ngFor='let item of videoList'>
  <a href="videos/{{ item.slug }}" >{{ item.name }}</a>
  <div [innerHTML]='item.embed'></div>
  <div [innerHTML]='someItem'></div>
  <iframe width="560" height="315" [src]="getEmbedUrl()" frameborder="0" allowfullscreen></iframe>
</div>
  • video-list.component.tsに追記する。
getEmbedUrl(){
  return "https://www.youtube.com/embed/{{ item.embed }}";
}
  • これでもだめ!angularのDomSanitizerメソッドをimportして利用してみる。
import { DomSanitizer } from '@angular/platform-browser';
...
constructor(private sanitizer: DomSanitizer) { } // constructorで、sanitizerのアノテーションをDomSanitizerに設定する。
...
getEmbedUrl(){
  return this.sanitizer.bypassSecurityTrustResourceUrl('https://www.youtube.com/embed/{{ item.embed }}');
}
  • これでどうだ。。。表示された!が、うまく動画が再生されない。{{ item.embed }} の部分の挙動がおかしいので、修正する。
getEmbedUrl(item){
  return this.sanitizer.bypassSecurityTrustResourceUrl('https://www.youtube.com/embed/' + item.embed);
}
  • video-list.component.htmlの方も修正する。
<p>
  {{ title }}
</p>

<div *ngFor='let item of videoList'>
  <a href="videos/{{ item.slug }}" >{{ item.name }}</a>
  <div [innerHTML]='item.embed'></div>
  <div [innerHTML]='someItem'></div>
  <iframe width="560" height="315" [src]="getEmbedUrl(item)" frameborder="0" allowfullscreen></iframe>
</div>
  • これで無事表示された!!さらに、item.embedに値が入っている場合のみembedを表示するようにする。
<iframe *ngIf='item.embed' width="560" height="315" [src]="getEmbedUrl(item)" frameborder="0" allowfullscreen></iframe>

まとめ

  • jsonオブジェクトからのリンクの安全を確保するには、[src]を使うと有効かも?
  • *ngIf, [src] などの文脈で、contextを利用する場合は、'' シングルクオテーションで囲むだけでOK。
  • それ以外のhtml内で利用する場合は、{{}}で囲んで利用する。