Djangoroidの奮闘記

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

Angularのための、TypeScript入門 その3

参考サイト

https://www.codingforentrepreneurs.com/projects/getting-started-typescript/

Declaring Variables - let vs var

  • let と varの違い
//letの場合
function f(input: boolean){
  let a = 100
  if (input) {
    let b = a + 100012
    return b;
  }
  return b; //ここでb の定義がされていないというerrorが出る。
}

//varの場合

function f(input: boolean){
  let a = 100
  if (input) {
    var b = a + 100012
    return b;
  }
  return b; // varの場合は、よりscope範囲が広いので、errorがでない。
}

console.log(f(true))
console.log(f(false))
  • 結論的には、let使ったほうが無難というか、python のclassに近い使い方ができるような気がするから、それでいいようなきがする。

Run & Compile with Webpack

  • Run と Compileにtsc, http-serverの代わりに、webpackを使うという話かな。

  • $ webpack --watchをターミナルで打ってみる。。。error!

ERROR in Entry module not found: Error: Can't resolve 'ts-loader' in 'path'
  • とりあえず、globalではなく、localに、$ npm install ts-loaderとinstallしたら、解決した!でもたぶん、全部globalでやるのが本当はいいんだろうなとおもいつつもとりあえず、これでやってしまおう。

  • もしかすると、npm link ts-loaderとかすれば解決したのかなぁ。。まあいいや。今度考えよう。

  • webpack のoutputは、bundle.jsにまとめられるため、<script src='/dist/bundle.js'></script> として、index.htmlを修正する。

  • ためしに、http://127.0.0.1:8080/にアクセスしてみる。。。予想通りの挙動で成功!!

  • つぎに、src/coffeeフォルダを作成する。そしてその中身を、getcoffee.tsを作成する。

// getcoffee.ts
class MustHaveCoffee{
  constructor(){ //constructorは、newでインスタンスが作成されたときに、実行される
    console.log("Make it bulletproof")
  }
}
  • つぎに、main.tsにimportする。
import { MustHaveCoffee } from './src/coffee/getcoffee';

function f(input: boolean){
  let a = 100
  if (input) {
    let b = a + 100012
    return b;
  }
  return a;
}

console.log(f(true))
console.log(f(false))
  • ただ実はこのままだとだめで、getcoffee.tsの方に、exportをつけなければいけない。
export class MustHaveCoffee{}

まとめ

  • webpackは、tsファイルを1つにまとめるので、動作が早いとかファイルがコンパクトになるとかいう効果があるのかな。いま流行ってるぽいし、要チェックだね。

  • webpack>bundle.jsなどのように、trans compileしたデータを1つにまとめる。

  • justinの講座わかりやすい!

Angularのための、TypeScript入門 その2

Typescript - Classes - Part 1

参考サイト

https://www.codingforentrepreneurs.com/projects/getting-started-typescript/

概要

  • typescriptのclassをゼロから作ってみる。
class SweetSweetBasil {
  constructor(name:string){ // constructorは、インスタンスがnew演算子で新規作成されたときに、実行される関数
    console.log("Hello " + name)
  }
}

let basil = new SweetSweetBasil("World") // newでclassのinstanceを作成する
let basil = new SweetSweetBasil("basil")
  • new 演算子は、コンストラクタ関数を持ったユーザ定義のインスタンスを作成する。つまりここでは、new SweetSweetBasilのインスタンスを作成する。SweetSweetBasilは、newでインスタンスが作成されると、SweetSweetBasil classのコンストラクタ関数を実行する。

  • SweetSweetBasilにcolor()メソッドを追記してみる。

class SweetSweetBasil {
  constructor(name:string){ // constructorは読み込みと同時に実行される
    console.log("Hello " + name)
  }
  color(){ // color メソッドを定義する。
    console.log("Green")
  }
}

let world = new SweetSweetBasil("World")
let basil = new SweetSweetBasil("basil")

basil.color()
world.color()

Typescript - Classes - Part 2

  • classの内容を少し複雑にしてみる。
class SweetSweetBasil {
  color: string; // 変数アノテーションを指定する。
  constructor(name:string, startColor:string){ // constructorは読み込みと同時に実行される
    console.log("Hello " + name)
    this.color = startColor //pythonで言うところの、self.color, new instance されたときに、this.colorにstartColorを代入する
  }
  getColor(){
    console.log("Green")
  }
}
let world = new SweetSweetBasil("World", "green")
let basil = new SweetSweetBasil("basil", "bright green")
basil.getColor()
world.getColor()

console.log(basil.color)
console.log(world.color)
  • classをextends で継承してみる。
class SweetSweetBasil {
  color: string;
  constructor(name:string, startColor:string){ // constructorは読み込みと同時に実行される
    console.log("Hello " + name)
    this.color = startColor //pythonで言うところの、self.color
  }
  getColor(){
    console.log("Green")
  }
}
let world = new SweetSweetBasil("World", "green")
let basil = new SweetSweetBasil("basil", "bright green")
basil.getColor()
world.getColor()

console.log(basil.color)
console.log(world.color)


class Basil extends SweetSweetBasil { // extendsで、classを継承
  constructor(name:string, startColor:string){ // constructorの引数は省略dけいない
    super(name, startColor) // super()で継承する必要あり、ここも省略できない
  }
}

let basil3 = new Basil("basil", "light green")

basil3.getColor()
console.log(basil3.color)
  • constructorと継承の昨日の確認をしてみる。
class SweetSweetBasil {
  color: string;
  constructor(name:string, startColor:string){
    console.log("Hello " + name)
    this.color = startColor  
  }
  getColor(){
    console.log(this.color) // ここをthis.color(self.color)に修正する。
  }
}


class Basil extends SweetSweetBasil {
  constructor(name:string, startColor:string){
    super(name, startColor)
  }
  setNewColor(newColor:string){
    this.color = newColor
  }
}

let basil3 = new Basil("basil", "light green")

basil3.getColor() //lightgreen になる。
basil3.setNewColor("Red") //Red
basil3.getColor() //red
console.log(basil3.color)

Angularのための、TypeScript入門 その1

Typescript for Angular

参考サイト

https://www.codingforentrepreneurs.com/projects/getting-started-typescript/ https://www.codingforentrepreneurs.com/blog/typescript-setup-guide/

install node.js

  • 公式ホームページからinstallする

  • npmをアップグレードしておく。

$ sudo npm install -g npm

Configure Typescript

  • Typescriptに必要なパッケージをnpmでinstallする。
$ npm install -g typescript
$ npm install -g webpack
$ npm install -g ts-loader
$ npm install -g jquery
$ npm install -g http-server
  • main.tsファイルを作成する。
class SweetSweetClass { //
    constructor() {
        console.log("Even sweeter")
    }
}
let basil = new SweetSweetClass()
  • tsconfig.jsonを作成する。
{
  "compilerOptions": {
    "module": "commonjs",
    "outDir": "dist/",
    "noImplicitAny": true,
    "removeComments": true,
    "preserveConstEnums": true
  },
  "include": [
    "*"
  ],
  "exclude": [
      "node_modules",
      "**/*.spec.ts"
  ]
}
  • tsconfig.jsonを作成する。
{
  "compilerOptions": {
    "module": "commonjs",
    "outDir": "dist/",
    "noImplicitAny": true,
    "removeComments": true,
    "preserveConstEnums": true
  },
  "include": [
    "*"
  ],
  "exclude": [
      "node_modules",
      "**/*.spec.ts"
  ]
}
  • $tsc をshellで実行して、tsファイル>jsファイルにtrans compileする! > dist/main.js が作成されたすごい!

  • index.html を作成する。scriptで、tsファイルではなく、jsファイルを指定しているところが重要なポイントか。

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
<script src='/dist/main.js'></script>
</body>
</html>
  • $tsc --watchを実行すると、tsファイルを変更したときに即時jsファイルにtsc してくれる。なので、これからはこれを起動中にしておくことが多いかも知んないな。

  • $ http-server -c-1を実行する。-c-1は、Using the flag -c-1 makes the browser never cache the files. とのこと。

感想

  • justinやっぱ教えるのうまいな〜。英語だけどめっちゃわかりやすい。

Angular2 The Tour of Heroes tutorial に挑戦してみる 7

HTTP

https://angular.io/docs/ts/latest/tutorial/toh-pt6.html

javascriptの基礎

http://qiita.com/ukiuni@github/items/5f3d8620187905aea3d4

  • Convert the service and components to use Angular’s HTTP service.

  • Get the hero data from a server.

  • Let users add, edit, and delete hero names. CRUD
  • Save the changes to the server.

Providing HTTP Services

  • HttpModuleは、core moduleではない。あくまでもoptionの1つ。
  • app.module.tsに、HttpModuleをimportする
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';

import { HttpModule }    from '@angular/http'; //ここをimportする

import { AppRoutingModule } from './app-routing.module';
import { AppComponent }         from './app.component';
import { DashboardComponent }   from './dashboard.component';
import { HeroesComponent }      from './heroes.component';
import { HeroDetailComponent }  from './hero-detail.component';
import { HeroService }          from './hero.service';
@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule, // ここにもimportする
    AppRoutingModule
  ],
  declarations: [
    AppComponent,
    DashboardComponent,
    HeroDetailComponent,
    HeroesComponent,
  ],
  providers: [ HeroService ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

Simulate the web API

  • We recommend registering app-wide services in the root AppModule providers.app-wide servicesは、root Appmoduleのprovidersに登録しておくのがおすすめ。HeroServiceのことかな。どこからでもアクセスできるからかな。
  • とりあえず、in-memory web APIでテストする。
  • app.module.tsに追記する。
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';
import { HttpModule }    from '@angular/http';
import { AppRoutingModule } from './app-routing.module';
// Imports for loading & configuring the in-memory web api
import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; //ここを追記
import { InMemoryDataService }  from './in-memory-data.service'; //ここを追記
import { AppComponent }         from './app.component';
import { DashboardComponent }   from './dashboard.component';
import { HeroesComponent }      from './heroes.component';
import { HeroDetailComponent }  from './hero-detail.component';
import { HeroService }          from './hero.service';
@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    InMemoryWebApiModule.forRoot(InMemoryDataService), // InMemoryDataServiceからデータを持ってくるとかそれぐらいの意味なのかな?forRootってのは、dataを持ってくるときにつかうのか。
    AppRoutingModule
  ],
  declarations: [
    AppComponent,
    DashboardComponent,
    HeroDetailComponent,
    HeroesComponent,
  ],
  providers: [ HeroService ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }
  • forRoot method は、the name of the method is forRoot, Angular will know how to use it: it will create the module context and the services declared in providers.ということらしい。
  • とりあえず、forRootは、Serviceをimportと同じような意味と考えておこう。
  • module内に、Serviceをimportする。

  • つぎにin-memory-data.service.tsを作成する。

import { InMemoryDbService } from 'angular-in-memory-web-api';
export class InMemoryDataService implements InMemoryDbService { // implementsで、InMemoryDbServiceを継承する。
  createDb() { //createDbはたぶん、InMermoryDbServiceのメソッド南じゃないかな。
    let heroes = [ //letは、変更可能な変数
      {id: 11, name: 'Mr. Nice'},
      {id: 12, name: 'Narco'},
      {id: 13, name: 'Bombasto'},
      {id: 14, name: 'Celeritas'},
      {id: 15, name: 'Magneta'},
      {id: 16, name: 'RubberMan'},
      {id: 17, name: 'Dynama'},
      {id: 18, name: 'Dr IQ'},
      {id: 19, name: 'Magma'},
      {id: 20, name: 'Tornado'}
    ];
    return {heroes};
  }
}

Heroes and HTTP

  • Now convert getHeroes() to use HTTP. hero.service.tsをupdateする。
private heroesUrl = 'api/heroes';  // URL to web api
constructor(private http: Http) { } // Httpのclassをimportしておく必要あり、httpの型を定義する。
getHeroes(): Promise<Hero[]> { //getHeroesをPromiseで1回呼び出す、getHeroesの型は、Hero[]リストの型
  return this.http.get(this.heroesUrl) // とにかく、httpで、heroesUrlにアクセスする
             .toPromise() // Promise objectに変換するという意味か!
             .then(response => response.json().data as Hero[]) // =>はarrow関数といういうのか。arrow関数は無記名関数の省略形。
             //  function(response) { return response.json().data as Hero[] }
             .catch(this.handleError);
             // try exceptのようなものと考えていいか?handleErrorは、↓で定義する。
}
private handleError(error: any): Promise<any> { // errorのclassは、anyでOK。
  console.error('An error occurred', error); // for demo purposes only // consolenerrorを表示する、error内容も表示する。
  return Promise.reject(error.message || error); //静的なPromise.reject 関数はリジェクトされたPromiseを返します。デバッグとエラーをキャッチするために、 reasonをinstanceof Errorするのは役に立ちます。とのことなのえ、rejectされた、error.messageとerrorを返すということでOKか。
}
  • Promise.reject は、関数はリジェクトされたPromiseを返します。デバッグとエラーをキャッチするために、 reasonをinstanceof Errorするのは役に立ちます。とのこと。

Get hero by id

  • 現在の、getheroでは、すべてのheroesを取ってきて、そこからfilterして、idがmatchするものを選択していたが、これは無駄が多い。そのため、idで検索してmatchするdataのみ表示するように変更する。
getHero(id: number): Promise<Hero> { // 引数であるidをnumber型で設定する。Promise<Hero>で、getHeroの型をHero classに設定するかつPromiseを有効にする
  const url = `${this.heroesUrl}/${id}`; // url は、constで、private変数のheroesUrl/idに設定する。
  return this.http.get(url) // httpのget methodで上記のURLにアクセスする
    .toPromise() // Promiseオブジェクトに変換する
    .then(response => response.json().data as Hero) // function(response){ return this.response.json().dataを1行で表した=>(arrow関数)
    .catch(this.handleError); //try, exceptのexceptの部分。handleErrorメソッドを呼び出す。
}
  • ${}は、template literalの中に、変数を入れるときの、typescriptの文法

Updating hero details

  • hero-detail.component.html にsave ボタンを追加する。
<button (click)="save()">Save</button>
  • hero-detail.component.ts
save(): void { // void関数
  this.heroService.update(this.hero) //heroServiceのupdateメソッドを実行する
    .then(() => this.goBack()); // 成功した場合、goBackメソッドを実行する。 function(){ return this.goBack()} になるのか?
}
  • arrow関数がイマイチわかってないな、、、() => this.goBack()は、return this.goBack()と同じ意味になるのか?それとも、function(){ return this.goBack() }になるのか?

Add a hero service update() method

  • さっきのsave()メソッドで使った、update()メソッドをhero.service.tsファイル内で定義する。
private headers = new Headers({'Content-Type': 'application/json'}); // headersをprivate変数として定義する。newで新しいHeadersオブジェクトを作成する
update(hero: Hero): Promise<Hero> { //updateメソッドの引数は、hero(型はHero)、classは、Hero
  const url = `${this.heroesUrl}/${hero.id}`; // ${}は、template literal内で、変数を使うときに利用する。this.heroesUrl/hero.id
  return this.http
    .put(url, JSON.stringify(hero), {headers: this.headers}) //httpのputメソッドで、urlにアクセスする。
    // headers : 開いているのページのheadersを設定する(python風にいうと、self.request.headersとかになるのかな)
    .toPromise()
    .then(() => hero)
    .catch(this.handleError);
}
  • Http classのメソッドの一覧は以下にある模様。

https://angular.io/docs/ts/latest/api/http/index/Http-class.html

  • put(url: string, body: any, options?: RequestOptionsArgs) : Observable<Response> とのことなので、put メソッドで、指定したurlに、bodyの内容を、headersをjsonで指定して、送付するということか〜〜!!

  • でもなんで、headersの情報が必要なんだ。たぶん、The body content type (application/json) is identified in the request header.ということなので、content typeを明確にするために、ということなのかな。

Add the ability to add heroes

<div>
  <label>Hero name:</label> <input #heroName />
  <button (click)="add(heroName.value); heroName.value=''">
    Add
  </button>
</div>
  • heroes.components.ts を作成する。
add(name: string): void { // addの引数であるname は、stringで型を定義する。関数の返り値は、voidにしておく。
  name = name.trim(); // trimメソッドは両端から空白を取り除いた文字列を返します。 元の文字列自身は変更しません。とのこと。
  if (!name) { return; } // nameの値ががない場合は、そこで終了するという意味なのかな?
  this.heroService.create(name) // !name出ない場合は、ここから実行する。create メソッドを実行する
    .then(hero => { // 成功した場合は、function (hero) {return this.heroes.push(this.hero), this.selectedHero = null} を実行するというのと同じ意味?
      this.heroes.push(hero); // pushは、listに値を追加するという意味らしい
      this.selectedHero = null;
    });
}
  • hero.service.ts にcreateメソッドを作成する。
create(name: string): Promise<Hero> { // create メソッドの引数は、nameで、引数は、string 返り値のclass定義は、Promiseの変数型Hero
  return this.http // http メソッドのpostで送信する。
    .post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers}) // headersで,送信する型を定義する。
    .toPromise() //Promise型に変換する
    .then(res => res.json().data as Hero) // postが成功したら、function(res){return this.res.json().data as Hero}
    .catch(this.handleError); // errorが出た場合の処理メソッド
}

Add the ability to delete a hero

  • heroes.component.html にdeleteボタンを追記する。
<li *ngFor="let hero of heroes" (click)="onSelect(hero)"
    [class.selected]="hero === selectedHero">
  <span class="badge">{{hero.id}}</span>
  <span>{{hero.name}}</span>
  <button class="delete"
    (click)="delete(hero); $event.stopPropagation()">x</button>
</li>
  • heroes.component.ts にdeleteメソッドを追記する。
delete(hero: Hero): void { //引数hero(変数型Hero), 返り値は、void
  this.heroService
      .delete(hero.id) //deleteメソッドを実行
      .then(() => { // 成功した場合以下の関数を実行する? function(){this.heroes = this.heroes.filter( function(h){ return  h !== hero } )} だから、filterは、h以外のheroを返り値として返すということか!
        this.heroes = this.heroes.filter(h => h !== hero);
        if (this.selectedHero === hero) { this.selectedHero = null; } //this.selectedHeroがheroの場合は、selectedHeroはnullにする。
      });
}
  • ==と、=== は、違う。==は、等価演算子で、===は、厳密等価演算子
  • this.heroes = this.heroes.filter(h => h !== hero); って何気に難しいな。結論的には、h 以外のheroをthis.heroesに返すという機能。this.heroes = this.heroes.filter(function(h){return(h !== hero)})をアロー関数で表したもの。
  • filter機能は、以下のサイトがわかりやすかった! http://qiita.com/itagakishintaro/items/29e301f3125760b81302#filter
var filtered = [12, 5, 8, 130, 44].filter(function(element, index, array) {
    return (element >= 10);
});

Hero service delete() method

  • hero.service.ts にdeleteメソッドを定義する。
delete(id: number): Promise<void> { // 引数は、id(変数型はnumber), 返り値は、Promise<void>
  const url = `${this.heroesUrl}/${id}`; // template literalの${}
  return this.http.delete(url, {headers: this.headers}) //httpのdeleteで、urlにアクセス
    .toPromise() // Promise型に変換
    .then(() => null) // 成功したら、null を返す function(){return null}
    .catch(this.handleError);
}

Observables

  • Each Http service method returns an Observable of HTTP Response objects. Httpservice method は、HTTP Response objects である、Observableを返り値として返す。
  • The HeroService converts that Observable into a Promise and returns the promise to the caller. This section shows you how, when, and why to return the Observable directly.HeroServiceは、Observable を Promiseに変換して、関数に、the promiseを返す。
  • An Observable is a stream of events that you can process with array-like operators.Observableは、eventsのstreamで、promiseは、1回の呼び出しが基本ぽい。
  • Converting to a Promise is often a good choice. You typically ask http.get() to fetch a single chunk of data. (Observable をPromiseに変換することはいい選択であることが多い。普通は、http.get()で、single chunk of dataをfetchするからね。)
  • When you receive the data, you’re done. The calling component can easily consume a single result in the form of a Promise.(dataを受け取ったとき、処理完了。呼び出したcomponentは、Promiseの形式に従って、速やかにsingle resultを消化できる。)
  • But requests aren’t always done only once. You may start one request, cancel it, and make a different request before the server has responded to the first request.(でも一回で終わらないときもある。first requestが帰ってくる前に別のrequestを始めたり、cancelしたりするかもしれない。)
  • A request-cancel-new-request sequence is difficult to implement with Promises, but easy with Observables.(requestをcancelして、新しいrequestを始めるといったsequence は、Promisesでは実装が難しいけど、Observablesなら簡単に実装できる。)

Add the ability to search by name

  • hero-search.service.ts をObservableで処理するように変更する。
import { Injectable } from '@angular/core';
import { Http }       from '@angular/http';
import { Observable }     from 'rxjs/Observable'; // Observableをimport
import 'rxjs/add/operator/map'; // mapをimport
import { Hero }           from './hero';
@Injectable()
export class HeroSearchService {
  constructor(private http: Http) {} // private変数の httpの変数型Httpに設定する。constructorは、classから、オブジェクトを作成したときに、自動的に実行されるメソッド。
  search(term: string): Observable<Hero[]> { //term の変数型string に設定。返り値は、Observableの変数型Hero[]で設定
    return this.http
               .get(`app/heroes/?name=${term}`) // template literal 内に${term}で、検索ワードを代入したurlを設定する。
               .map(response => response.json().data as Hero[]);
               // map(function(response){return response.json().data as Hero[]})
               // mapの意味?
  }
}
  • you return the Observable from the the http.get(), after chaining it to another RxJS operator, map(), to extract heroes from the response data. (ObservableをanotherRXJSoperatorであるmap()とchainingした後に、http.get()から受け取る。map()は、response dataからheroesを抽出するために必要な処理。)
  • RxJS operator chaining makes response processing easy and readable. (RxJS operatorのchainingは、response processを、簡単で読み込みやすくする)

HeroSearchComponent

  • src/app/hero-search.component.htmlを作成する。
<div id="search-component">
  <h4>Hero Search</h4>
  <input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
  <!-- search()を実行する -->
  <div>
    <!-- async属性が指定されているので、これ以降は利用開始時に非同期通信で処理される -->
    <div *ngFor="let hero of heroes | async"
         (click)="gotoDetail(hero)" class="search-result" >
      {{hero.name}}
    </div>
  </div>
</div>
  • asyncは、利用開始とともに、スクリプトは非同期で実行されます。一方でasync属性でなければ…ユーザエージェントがページを解析する前に、スクリプトが読み込まれ、直ちに実行されます。とのこと。

http://www.aiship.jp/knowhow/archives/20559

  • But as you’ll soon see, the heroes property is now an Observable of hero arrays, rather than just a hero array. (heroes propertyは、Observable array。)
  • The *ngFor can’t do anything with an Observable until you route it through the async pipe (AsyncPipe). (async pipe を使って、Observableにrouteするまでは、ngforが実行されないようにしている。)
  • The async pipe subscribes to the Observable and produces the array of heroes to *ngFor.(async pipeは、subscribes Observableしている。heroesのarrayをproduce する)

  • src/app/hero-search.component.css 作成する。

.search-result{
  border-bottom: 1px solid gray;
  border-left: 1px solid gray;
  border-right: 1px solid gray;
  width:195px;
  height: 16px;
  padding: 5px;
  background-color: white;
  cursor: pointer;
}
.search-result:hover {
  color: #eee;
  background-color: #607D8B;
}
#search-box{
  width: 200px;
  height: 20px;
}
  • HeroSearchComponentを作成する。
import { Component, OnInit } from '@angular/core';
import { Router }            from '@angular/router';
import { Observable }        from 'rxjs/Observable';
import { Subject }           from 'rxjs/Subject';
// Observable class extensions
import 'rxjs/add/observable/of';
// Observable operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import { HeroSearchService } from './hero-search.service';
import { Hero } from './hero';
@Component({
  selector: 'hero-search',
  templateUrl: './hero-search.component.html',
  styleUrls: [ './hero-search.component.css' ],
  providers: [HeroSearchService]
})
export class HeroSearchComponent implements OnInit {
  heroes: Observable<Hero[]>; //heroesの変数型は、Observableの<Hero[]>
  private searchTerms = new Subject<string>(); // searchTermsは、Subjectのstring型。
  constructor(
    private heroSearchService: HeroSearchService, // heroSearchServiceに、HeroSearchService型を代入する。
    private router: Router) {} // router: Router で設定。
  // Push a search term into the observable stream.
  search(term: string): void { // search methodを定義する。引数のterm は、string型を設定、返り値はvoid
    this.searchTerms.next(term); //Each call to search() puts a new string into this subject's observable stream by calling next().nextメソッドによって、新しい次のobservable streamを返す。
    // console.log(this.searchTerms);を書くと、その機能を確認できる。

  }
  ngOnInit(): void {
    this.heroes = this.searchTerms //searchTermsをheroesに代入する
      .debounceTime(300)        // wait 300ms after each keystroke before considering the term(検索に300ms待機)
      .distinctUntilChanged()   // ignore if next search term is same as previous(search termが前と同じものだったら、無視する)
      .switchMap(term => term   // switch to new observable each time the term changes
        // return the http search observable(switchMapによって、Observableに変換したサーチ結果をheroesに返す)
        ? this.heroSearchService.search(term) //? の役割は、tryくらいの意味かな? // こっちは、heroSearchServiceの方のsearchメソッド, term
        // or the observable of empty heroes if there was no search term
        : Observable.of<Hero[]>([])) // search termがない場合は、空のheroesリストを持つobservableオブジェクトを返す。
      .catch(error => {
        // TODO: add real error handling
        console.log(error);
        return Observable.of<Hero[]>([]);
      });
  }
  gotoDetail(hero: Hero): void { // Detail pageへのリンク
    let link = ['/detail', hero.id];
    this.router.navigate(link);
  }
}
  • Subjectは、さっぱりわからなかった。以下のサイトを参照にさせてもらいました〜。 http://qiita.com/ralph/items/f7205c8171826cc2153b#subject%E3%81%A8%E3%81%AF

  • Subjectは、短く言うと「SubscriberとObservableの2つの機能を併せ持ったもの」です。とのこと。

  • next()メソッドは、searchTerms(Subject型)が変更になるたびに、新しいsearchTermsを表示するように取り出すためのメソッド?

Add the search component to the dashboard

  • src/app/dashboard.component.htmlに、hero-searchを追記する。
<h3>Top Heroes</h3>
<div class="grid grid-pad">
  <a *ngFor="let hero of heroes"  [routerLink]="['/detail', hero.id]"  class="col-1-4">
    <div class="module hero">
      <h4>{{hero.name}}</h4>
    </div>
  </a>
</div>
<hero-search></hero-search>
  • app.module.tsにHeroSearchComponentのimportを追記する。
declarations: [
  AppComponent,
  DashboardComponent,
  HeroDetailComponent,
  HeroesComponent,
  HeroSearchComponent
],

まとめ

  • ひとまずこれで、tour of heroes は完了、typescript, javascriptがほとんどわからなかったので、かなり苦労した(^^;)明日からは、typescriptの文法書を読んでみることにする。

Angular2 The Tour of Heroes tutorial に挑戦してみる 6

ROUTING 後半戦

https://angular.io/docs/ts/latest/tutorial/toh-pt5.html

Add HeroService.getHero()

getHero(id: number): Promise<Hero> { // getHero(id) で、idを引数に取る関数
  // idの型はnumber, getHeroのclassは、Hero.
  return this.getHeroes() // getHeroesで値の取得に成功したら
             .then(heroes => heroes.find(hero => hero.id === id));
             // heroes は、heroesの中からid が一致するものになる
}
class HeroService():
...
  def getHero(id):
    if self.getHeroes():
      hero = heroes.get(id=id)  

Find the way back

  • 戻るボタンを作る
goBack(): void{
  this.location.back();
}
<div *ngIf="hero">
  <h2>{{hero.name}} details!</h2>
  <div>
    <label>id: </label>{{hero.id}}</div>
  <div>
    <label>name: </label>
    <input [(ngModel)]="hero.name" placeholder="name" />
  </div>
  <button (click)="goBack()">Back</button>
</div>
@Component({
  selector: 'hero-detail',
  templateUrl: './hero-detail.component.html',
})

Select a dashboard hero

<a *ngFor="let hero of heroes"  [routerLink]="['/detail', hero.id]"  class="col-1-4">
  • routerLink がhrefみたいなもんか。[]がついているということは、bindingされるということかな。

Refactor routes to a Routing Module

  • Routing機能を別ファイルに移す。app-routing.module.tsを作成する
import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent }   from './dashboard.component';
import { HeroesComponent }      from './heroes.component';
import { HeroDetailComponent }  from './hero-detail.component';

const routes: Routes = [ //const(変更不可)のroutesをRoutes classで定義する。Routes型は、@angular/routerからimportする。
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  { path: 'dashboard',  component: DashboardComponent },
  { path: 'detail/:id', component: HeroDetailComponent },
  { path: 'heroes',     component: HeroesComponent }
];

@NgModule({ // ngModuleデコレータを使う>なぜだ?
  imports: [ RouterModule.forRoot(routes) ], // RouterModuleのforRootで、routesをimportsする
  exports: [ RouterModule ] // それをRouterModuleとして、exportする。これでRouterModuleでどこでも使える
})
export class AppRoutingModule {} // 最後にこのModuleクラスをAppRoutingModule classとしてexportする。
  • The Routing Module pulls the routes into a variable. The variable clarifies the routing module pattern in case you export the module in the future.
  • The Routing Module adds RouterModule.forRoot(routes) to imports.
  • The Routing Module adds RouterModule to exports so that the components in the companion module have access to Router declarables, such as RouterLink and RouterOutlet.
  • There are no declarations. Declarations are the responsibility of the companion module.
  • If you have guard services, the Routing Module adds module providers. (There are none in this example.)

Select a hero in the HeroesComponent

template: `
  <h1>{{title}}</h1>
  <h2>My Heroes</h2>
  <ul class="heroes">
    <li *ngFor="let hero of heroes"
      [class.selected]="hero === selectedHero" // hero == selectedHeroの場合、class=selectedになる
      (click)="onSelect(hero)"> // click時に、onSelect(hero)を実行する
      <span class="badge">{{hero.id}}</span> {{hero.name}}
    </li>
  </ul>
  <hero-detail [hero]="selectedHero"></hero-detail>
`,
  • onSelect の中身は、以下の通り。
onSelect(hero: Hero): void {
  this.selectedHero = hero; // selectedHeroにheroを代入する
}
  • つまり heroをclick->selectedHeroに、heroを代入->hero-detail タグに、selectedHeroを[hero]に代入->hero-detail component起動 って感じかな!

Add the mini detail

<div *ngIf="selectedHero">
  <h2>
    {{selectedHero.name | uppercase}} is my hero
  </h2>
  <button (click)="gotoDetail()">View Details</button>
</div>
  • *ngif = “selectedHero” がある場合 > selectedHero.nameをすべて大文字で表示する。

  • ViewDetails をclick するとgotoDetai()が起動する。

Move content out of the component file

  • くっつけると以下のような感じ heroes.component.html。
<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes"
    [class.selected]="hero === selectedHero"
    (click)="onSelect(hero)">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>
<div *ngIf="selectedHero">
  <h2>
    {{selectedHero.name | uppercase}} is my hero
  </h2>
  <button (click)="gotoDetail()">View Details</button>
</div>
  • herose.component.tsは、以下のように修正する。
@Component({
  selector: 'my-heroes', // css-selector名
  templateUrl: './heroes.component.html', // templateファイルの場所
  styleUrls: [ './heroes.component.css' ] // styleファイルの場所
})
  • @Componentと export class Componentの関係は、レンダリングに近いのかな。

Update the HeroesComponent class

  • gotoDetailメソッドを定義する
gotoDetail(): void { // void returnが無い関数
  this.router.navigate(['/detail', this.selectedHero.id]); // thisってのは、templateをレンダリングしたページのこと?router.navigateで、/detail idのurlにアクセスするメソッド
}

Style the navigation links

  • app.component.tsのtemplateを修正する。
template: `
  <h1>{{title}}</h1>
  <nav>
    <a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
    <a routerLink="/heroes" routerLinkActive="active">Heroes</a>
  </nav>
  <router-outlet></router-outlet>
`,
  • routerLink でhrefでアクセスする場合は、何が違うのか?routerLinkでアクセスすると、router-outletが機能するとかそんな感じかな〜。
  • routerLinkActiveは、activeかどうかのstatusを設定できる。
  • router-outlet で、routerの接続先のcomponentの内容を表示できる。

まとめ

  • router-outlet, routerの使い方をざっくりまとめると以下のような流れなのかな。templateを基準に考えると自分は理解しやすい気がした。
<h1>{{title}}</h1>
<nav>
  <a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
  <a routerLink="/heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
<!-- ここで、routerLink先のcomponentが呼び出されて表示される -->
<!-- dashboardの場合は以下のtemplateが展開される
ここでは、さらにrouterLinkで、detailページが表示される-->
    <h3>Top Heroes</h3>
    <div class="grid grid-pad">
      <div *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]" class="col-1-4">
        <div class="module hero">
          <h4>{{hero.name}}</h4>
        </div>
      </div>
    </div>
<!-- dashboardの場合は以下のtemplateが展開される -->
<!-- routerLink で、heroのdetailページにアクセスすると以下のページが開く
(router-outletではないため、たぶん、Dashboardのtemplate自体が以下のページに変更される)-->
        <div *ngIf="hero">
          <h2>{{hero.name}} details!</h2>
          <div>
            <label>id: </label>{{hero.id}}</div>
          <div>
            <label>name: </label>
            <input [(ngModel)]="hero.name" placeholder="name" />
          </div>
          <button (click)="goBack()">Back</button>
        </div>
<!-- detailpage終了 -->
<!-- goBack()で、前の状態に戻る つまりdashboard viewに戻る-->

<!-- <a routerLink="/heroes" routerLinkActive="active">Heroes</a> にアクセスした場合-->
<!-- <router-outlet></router-outlet>にrouterLinkでアクセスしたcomponentのtemplateを展開する -->
      <h2>My Heroes</h2>
      <ul class="heroes">
        <li *ngFor="let hero of heroes"
          [class.selected]="hero === selectedHero"
          (click)="onSelect(hero)">
          <span class="badge">{{hero.id}}</span> {{hero.name}}
        </li>
      </ul>
      <div *ngIf="selectedHero">
        <h2>
          {{selectedHero.name | uppercase}} is my hero
        </h2>
        <button (click)="gotoDetail()">View Details</button>
      </div>
<!-- hero名をclickすると、idと名前がページ下部に表示される -->
<!-- clickすると、gotoDetail()が起動して、Detailページのtemplateが展開される -->
<div *ngIf="hero">
  <h2>{{hero.name}} details!</h2>
  <div>
    <label>id: </label>{{hero.id}}</div>
  <div>
    <label>name: </label>
    <input [(ngModel)]="hero.name" placeholder="name" />
  </div>
  <button (click)="goBack()">Back</button>
</div>

Angular2 The Tour of Heroes tutorial に挑戦してみる 5

ROUTING 前半戦

https://angular.io/docs/ts/latest/tutorial/toh-pt5.html

Add the Angular component router and learn to navigate among the views.

  • Add a Dashboard view. ダッシュボードビューの作成
  • Add the ability to navigate between the Heroes and Dashboard views. ダッシュボードと、Heroesの間をnavigateするabilityを追加する
  • When users click a hero name in either view, navigate to a detail view of the selected hero. hero nameをクリックした時に、detail view of selected hero へとnavigateする
  • When users click a deep link in an email, open the detail view for a particular hero. deep link in email をクリックした時に、open the detail view for a paticular hero.

Action plan

  • Turn AppComponent into an application shell that only handles navigation. AppComponentをnavigationだけをhandleするapplicationに変更する
  • Relocate the Heroes concerns within the current AppComponent to a separate HeroesComponent. AppComponent内の Heroesに関わる部分は、HeroesComponentとして分ける
  • Add routing. routingを追加する
  • Create a new DashboardComponent. DashboardComponentを作成する
  • Tie the Dashboard into the navigation structure. Dashboardを navigation structureとつなぐ
  • Routing is another name for navigation. The router is the mechanism for navigating from view to view.

  • このあたりは、djangoのviewsの処理と似てるかもな(^^)

Splitting the AppComponent

  • The AppComponent should only handle navigation, so you’ll move the display of Heroes out of AppComponent and into its own HeroesComponent. AppComponentは、navigationだけを担当するようにする。

HeroesComponent

  • 現時点で、AppComponentは、HeroesComponentようなもんなので、これをrenameする。
  • AppComponentは別途ファイルを作成する
@Component({
  selector: 'my-heroes',
})
export class HeroesComponent implements OnInit {
}

Create AppComponent

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <h1>{{title}}</h1>
    <my-heroes></my-heroes>
  `
})
export class AppComponent {
  title = 'Tour of Heroes';
}
  • djangoのviewsをmixins を継承して利用するようなイメージでいいのかな。
  • template内に、他のcomponentで設定した、selectorを利用できる。
  • ただ、そのためには、selector, componentをapp.module.tsに設定しておく必要あり
import { NgModule }       from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { FormsModule }    from '@angular/forms';

import { AppComponent }        from './app.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroesComponent }     from './heroes.component';
import { HeroService }         from './hero.service';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule
  ],
  declarations: [
    AppComponent,
    HeroDetailComponent,
    HeroesComponent
  ],
  providers: [
    HeroService
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule {
}
  • providers で HeroServiceも設定している?なぜだ。
  • Add HeroService to the providers array of AppModule because you’ll need it in every other view.とのことらしい。どこからでも参照できるように、AppModuleに加えておいたほうがいいらしい。
  • 重複するため、Remove HeroService from the HeroesComponent providers array とのこと。

Add routing

  • RouterModuleを使う。The router is a combination of multiple provided services (RouterModule), multiple directives (RouterOutlet, RouterLink, RouterLinkActive), and a configuration (Routes). とのことらしい。

  • index.htmlに、<base href="/">があることを確認する。

Configure routes

import { RouterModule }   from '@angular/router';
...
RouterModule.forRoot([
  {
    path: 'heroes',
    component: HeroesComponent
  }
])
  • RouterModuleを書く場所は、imports[]の中。
  • path は、 アクセスするurl
  • componentは、routerが起動したときに、作られるべきcomponentを書く
  • forRoot()メソッドは、configured routerがprovided at the apps root なので、呼び出されているらしい。意味がわからん。
  • The forRoot() method supplies the Router service providers and directives needed for routing, and performs the initial navigation based on the current browser URL. 要は、routingに必要なメソッドで、現在のURLにもとづいて、initial navigation を実行するメソッドとのこと。

Router outlet

  • /heroesをブラウザに貼り付けた場合 the routerは、herose routeとマッチして、HeroesComponentを表示する必要がある。ただ、そのためには、routerがどこに、componentを表示すればいいかを指定する必要がある。
  • そのために、<router-outlet>を使う。templateの最後に入れる
  • RouterOutletは、RouterModuleから提供されるdirectivesの中の1つ。
  • the routerは、<router-outlet>にcomponentを表示する
  • 要は、呼び出したcomponentを表示するのが、router-outletでOK?

Router links

  • たぶんそのままで、routerへのlinks
  • app.component.tsを修正すると以下のようになる。
template: `
   <h1>{{title}}</h1>
   <a routerLink="/heroes">Heroes</a>
   <router-outlet></router-outlet>
 `
  • /heroesのpathは、さっきapp.module.tsで設定したpath: 'heroes',と紐付いている
  • このtemplateは、RouterModuleで、設定したpath :'heroes'へのlinkをrouterLinkで設定して、pathにアクセスした時は、設定したcomponent: HeroesComponentを <router-outlet>の箇所に呼び出すという機能がある。
  • これはわかりやすい!これで、AppComponentが、attached to a router and displays routed views.
  • こういう、router機能だけを主にもつものをrouter componentというらしい。これは、djangoでは、urls.pyに近いのかな。

Add a dashboard

  • dashbord.component.ts を作成する
import { Component } from '@angular/core';

@Component({
  selector: 'my-dashboard',
  template: '<h3>My Dashboard</h3>'
})
export class DashboardComponent{}

Configure the dashboard route

{
  path: 'dashboard',
  component: DashboardComponent
},
  • Also import and add DashboardComponent to the AppModule’s declarations.
declarations: [
  AppComponent,
  DashboardComponent,
  HeroDetailComponent,
  HeroesComponent
],

Add a redirect route

  • dashboard viewを /のときに表示させるには、redirect を使う
{
  path: '',
  redirectTo: '/dashboard',
  pathMatch: 'full'
},

Add navigation to the template

  • app.component.tsのtemplateにdashboardのrouteを追記する。
template: `
   <h1>{{title}}</h1>
   <nav>
     <a routerLink="/dashboard">Dashboard</a>
     <a routerLink="/heroes">Heroes</a>
   </nav>
   <router-outlet></router-outlet>
 `
  • いまのところ

Add heroes to the dashboard

  • templateをtemplateUrlに変更してみる。 これは、djangoのviewのtemplateとほぼおなじ意味かな。
import { Component } from '@angular/core';

@Component({
  selector: 'my-dashboard',
  templateUrl: './dashboard.component.html'
})
export class DashboardComponent { }
<h3>Top Heroes</h3>
<div class="grid grid-pad">
  <div *ngFor="let hero of heroes" class="col-1-4">
    <div class="module hero">
      <h4>{{hero.name}}</h4>
    </div>
  </div>
</div>
  • heroとか読み込んでないので、まだ何も表示されないはず. これから、HeroServiceなどを再利用して、templateを完成させていく。

Get Heroese

  • dashboard.component.tsに、必要なmodule, componentをimportする
import { Component, OnInit } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
  • さらに、export class DashboardComponentを以下のように修正する。
export class DashboardComponent implements OnInit { // OnInitを実装(継承)
  heroes: Hero[] = []; // heroesの型(class)を定義する 初期値は空にしておく。
  constructor(private heroService: HeroService) { } //private変数を設定する
  ngOnInit(): void { //voidでreturnの無い関数をセットする
    this.heroService.getHeroes() // getHeroesが成功した場合 this.herose にheroesの1〜5をスライスしたリストを代入する
      .then(heroes => this.heroes = heroes.slice(1, 5));
  }
}

Navigating to hero details

  • HeroDetailComponentにアクセスできるようにする。
From the dashboard to a selected hero.
From the heroes list to a selected hero.
From a "deep link" URL pasted into the browser address bar.

Routing to a hero detail

  • <hero-detail [hero]="selectedHero"></hero-detail>これではうまくいかない。

Parameterized route

  • /detail/11のように、detail viewに、hero のidをつける

  • app.module.tsに追記する。

{
  path: 'detail/:id',
  component: HeroDetailComponent
},

Revise the HeroDetailComponent

// Keep the Input import for now, you'll remove it later:
import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, Params }   from '@angular/router';
import { Location }                 from '@angular/common';
import { HeroService } from './hero.service';
  • Inject the ActivatedRoute, HeroService, and Location services into the constructor, saving their values in private fields:
constructor(
  private heroService: HeroService,
  private route: ActivatedRoute,
  private location: Location
) {}
  • Import the switchMap operator to use later with the route parameters Observable.
import 'rxjs/add/operator/switchMap';
  • OnInit Interfaceを実装する。
export class HeroDetailComponent implements OnInit.
  • params からidをゲットして、idで指定したheroを表示するようにする。
ngOnInit(): void {
  this.route.params //paramsは、もともとあるのかな?
    .switchMap((params: Params) =>  this.heroService.getHero(+params['id'])) // switchMapで、もともとのparamsのidをgetHeroでgetしたidに変更する
    // idはnumberだけど、route paramsは、文字列じゃないといけないから、javascriptの+operattorで、文字列に変換してる
    .subscribe(hero => this.hero = hero); // subscribeの意味は不明
    // subscribeは3つの関数を引数に取る。(success, error, complete)
    // subscribe:変更を通知してもらうオブジェクト(subscripter)を登録する

}

まとめ

  • なんか普通にむずいな。。。

Angular2 The Tour of Heroes tutorial に挑戦してみる 4

SERVICES

https://angular.io/docs/ts/latest/tutorial/toh-pt4.html

Creating a hero service

In this page, you’ll move the hero data acquisition business to a single service that provides the data and share that service with all components that need the data.

Create the HeroService

  • Create a file in the app folder called hero.service.ts.

  • The naming convention for service files is the service name in lowercase followed by .service. For a multi-word service name, use lower dash-case. For example, the filename for SpecialSuperHeroService is special-super-hero.service.ts.

import { Injectable } from '@angular/core';

@Injectable()
export class HeroService {

}

Injectable services

  • Injecttable functionをimportして、@Injectable()デコレータをapplyさせる
@Injectable()
export class HeroService {
  getHeroes(): void {} // stub
}
  • The HeroService could get Hero data from anywhere—a web service, local storage, or a mock data source

Move the mock hero data

  • app.component.ts から、HEROESをカットして、mock-heroes.tsに貼り付ける
import { Hero } from './hero';

export const HEROES: Hero[] = [
  { id: 11, name: 'Mr. Nice' },
  { id: 12, name: 'Narco' },
  { id: 13, name: 'Bombasto' },
  { id: 14, name: 'Celeritas' },
  { id: 15, name: 'Magneta' },
  { id: 16, name: 'RubberMan' },
  { id: 17, name: 'Dynama' },
  { id: 18, name: 'Dr IQ' },
  { id: 19, name: 'Magma' },
  { id: 20, name: 'Tornado' }
];
  • exportは、変数にも適用できるんやな。classだけではなくて。
  • HEROES constは、exportされているので、HeroServiceなどどこからでもimportができる
モックについては以下の記事が分かりやすかったです。
http://uehaj.hatenablog.com/entry/20090427/1240815860

モックについて言うと「オブジェクト間の相互作用を見るためのテスト」をするときに用いるのがモック。以上。それ以上でもそれ以下でもない。便利さの度合いも試験の手軽さもスピードも関係ない(直交する問題)。

例えば、ある「細胞」に対して試験をするとします。このとき:

刺激を与え、その細胞の1個の内部の状態がどう変化するか、を見るのが、状態中心のテスト。酸性度が上がるのか、ミトコンドリアが分裂するのか、など。いわゆる状態中心のテスト、普通のユニットテストです。
刺激を与え、その刺激をきっかけとして、その細胞が他に対してどういう影 響を及ぼすのか、シナプス経由で電気信号を発するのか、化学物質を放出するのかを調べる相互作用の試験。細胞内部には興味ありませんし調べません。モックを用いたテストがこっちです。

モックは後者のためにしか使えないし、モックのあらゆる機能がそのために設計されているのが分かります。
  • app.component.ts から、HEROESをカットしたので、add an uninitialized heroes propertyをする。Heroのリストが入ることを想定して、Hero[]というHero classのリストを型として定義しておくのか?
export class AppComponent {
  title = 'Tour of Heroes';
  heroes: Hero[];
  selectedHero: Hero;
  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}

Return mocked hero data

  • HeroServiceに、HEROESをimportして、getHeroes()メソッドからそれを取得できるように設定する
import { Injectable } from '@angular/core';

import { Hero } from './hero';
import { HEROES } from './mock-heroes';

@Injectable()
export class HeroService { // HeroService classをexportする
  getHeroes(): Hero[]{ // getHeroesメソッドを定義する //データの型定義は、Hero[](Hero classのリスト)
    return HEROES; // getHeroesにアクセスしたら、HEROES(from mock-heroes)を返す
  }
}

Import the hero service

import { HeroService } from './hero.service';
  • これだけか、便利だな(^^)

Don’t use new with the HeroService

heroService = new HeroService(); // don't do this このやり方は、NG!!

Inject the HeroService

  • new を使う代わりに、Add a constructor that also defines a private property.と、Add to the component's providers metadata.で、HeroServiceを利用する。
constructor(private heroService: HeroService) { }
  • constructor は、コンストラクタとは、クラスからオブジェクトを作成した際に、自動的に実行されるメソッドのことで、メンバ変数の初期化などの主に行います。ということらしい。python/djangoでいうと、__init__とか、初期化メソッドのことかな。

  • The constructor itself does nothing. The parameter simultaneously defines a private heroService property and identifies it as a HeroService injection site.(コンストラクタ自身は特に何もしない。heroServiceのprivate変数の定義とそれがHeroService injection siteであることをidentifyするために。)

  • 上記のコードは、AppComponent に追記する。

export class AppComponent {
  title = 'Tour of Heroes';
  heroes: Hero[];
  selectedHero: Hero;

  constructor(private HeroService: HeroService){}

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}
  • なんで、ここに挿入するのかが、よくわからない。。。

  • ERROR Error: No provider for HeroService!が、表示されるので、providerを設定する

providers: [HeroService]
  • The providers array tells Angular to create a fresh instance of the HeroService when it creates an AppComponent、providers 配列は、Angular に、AppComponetを作成したときに、create fresh instance of the HeroService をするように命令する。

getHeroes() in the AppComponent

  • AppComponentに、getHeroesを設定する
export class AppComponent {
  title = 'Tour of Heroes';
  heroes: Hero[];
  selectedHero: Hero;

  constructor(private heroService: HeroService){}

  getHeroes(): void {
      this.heroes = this.heroService.getHeroes();
    }

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}

The ngOnInit lifecycle hook

  • You might be tempted to call the getHeroes() method in a constructor, but a constructor should not contain complex logic, especially a constructor that calls a server, such as as a data access method. constructorで、getHeroes()読んだほうがいいんじゃないかと思うかもしれないけど、あまり複雑なものを入れない方がいい。

  • To have Angular call getHeroes(), you can implement the Angular ngOnInit lifecycle hook. Angularに、getHeroesを呼び出させるためには、Angular ngOnInit lifecycle hook をimplementsとして使うといいかもしれんとのこと。

  • implememnts は、クラスの継承に似ている.例えば以下のようなコード。

interface MyInterface{
    name:string
}

class MyClass implements MyInterface{
    name:string;//このメンバーがない場合コンパイルエラーとなる。
}

こちらのコードはQiitaの以下の記事より参考にさせていただきました。 http://qiita.com/nogson/items/86b47ee6947f505f6a7b

  • 以下のコードは例(不要)
import { OnInit } from '@angular/core';

export class AppComponent implements OnInit {
  ngOnInit(): void {
  }
}
  • 実際のコードは以下の通り
export class AppComponent implements OnInit { // ここでOnInitを継承する
  title = 'Tour of Heroes';
  heroes: Hero[];
  selectedHero: Hero;

  constructor(private heroService: HeroService){}

  getHeroes(): void {
      this.heroes = this.heroService.getHeroes();
    }

  ngOnInit(): void{ // ngOnInitを設定する
    this.getHeroes();
  }

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}
  • なぜOnInitが必要なのか??
  • 流れとしては、

    • constructorで、初期化メソッドを実行(private変数として、heroServiceを設定)
    • getHeroesメソッドを定義する (中身は、this.heroes に、 this.heroService.getHeroes();を代入する)
    • ngOnInit()で、たぶん、読み込みのときに、getHeroes()メソッドを呼び出すようにする。
    • そのためには、OnInitと、ngOnInitをセットで使う必要がある。
  • constructorにgetHeroes定義すればいいじゃんと思うけど、それはイマイチらしい。

Async services and Promises

  • To coordinate the view with the response, you can use Promises, which is an asynchronous technique that changes the signature of the getHeroes() method.

The hero service makes a Promise

import { Injectable } from '@angular/core';

import { Hero } from './hero';
import { HEROES } from './mock-heroes';

@Injectable()
export class HeroService {
  getHeroes(): Promise<Hero[]> { //Promise<> にHero[]を代入する
    return Promise.resolve(HEROES); //Promiseで、resolveしたHEROESを返す
  }
}

Act on the Promise

  • Promiseがresolve されたときに、Promiseを実行するように implementationを変更する必要あり。
getHeroes(): void {
  this.heroService.getHeroes().then(heroes => this.heroes = heroes); //Pass the callback function as an argument to the Promise's then() method:
}

まとめ

  • サービスクラスを使って、データを他のcomponentで共有できる
  • ngOnInit , implements OnInitで、初期化メソッド(自動で呼び出すメソッド)を設定する
  • providersは、freshなinstanceを設定するように命令する
  • mock heroデータを別ファイルに設定する(serviceがそれを呼び出すようにする)
  • Promiseで、非同期通信が可能になる。