Angular2のルーティング設定、RESTのサンプル作成

RESTのサンプルを作成しながらAngular2のルーティング設定の方法を確認しました。
※Angular2の2.0.0版、TypeScriptを使って確認したものです。(デモはv4.4.3、v2.0.0で動作確認)

※目次をクリックすると目次の下部にコンテンツが表示されます。

Angular2のルーティング設定
1)メインのHTMLファイル
 
<base>要素を追加
<head>要素の後にアプリケーションのルートパスを指定する。
<base href=”/sample/”>
 
2)ルーティング設定を行うコンポーネント
 
①ルーターライブラリからRoutesとRouterModuleをインポート
 
import { Routes, RouterModule } from ‘@angular/router’;
 
②RouterModuleを使ってルート設定
 
・ルーティングのパスを”path”、ルーティング先のコンポーネント名を”component”で指定。
・ルートパラメータを指定する場合は、”:id”のようにコロンをプレフィックスして指定する。

const appRoutes: Routes = [
//  { path: '', redirectTo: 'users', terminal: true },
  { path:'',
    component: UserIndexComponent},
  { path: 'new',
    component: UserNewComponent},
  { path:'users',
    component: UserIndexComponent},
  { path: 'users/:id',
    component: UserShowComponent},
  { path: 'users/:id/edit',
    component: UserEditComponent},
]

 
③RouterModuleのforRoot関数を使って②で設定したルートを適用し、インポー
 

export const routing = RouterModule.forRoot(appRoutes);

@NgModule({
  imports: [
    BrowserModule,
    HttpModule,
    routing
  ],
 :

 
④テンプレート内にナビゲーションメニュー作成
 
例)
<ul class=”nav nav-tabs”>
 <li><a [routerLink]=”[‘/users’]”>ユーザー一覧</a></li>
 <li><a [routerLink]=”[‘/new’]”>ユーザー登録</a></li>
</ul>
・routerLinkディレクティブで、リンク先のパスを指定。
 
⑤ルーティング先のコンテンツ表示位置を指定
 
router-outletディレクティブ内にルーティング先のコンポーネントからリターンされるビューが表示される
 
<router-outlet></router-outlet>
 
3)ルーティング先のコンポーネント
 
①”Router”などをインポート
 
ルートパラメータを使用する場合は”ActivatedRoute”もインポート。
import { Router, ActivatedRoute } from ‘@angular/router’;
 
②コンストラクタに上記インポートした”Router”などを指定
 

  constructor(
    private _router:Router,
    private _route: ActivatedRoute,
    private _userService:UserService){}

 
③ルートパラメータの値を取得
 

  private _sub: any;

  ngOnInit() {
    this._sub = this._route.params.subscribe(params => {
      let id = +params['id'];
      this._userService.show(id)
             .subscribe(
               user => this.user = user,
               error => alert(`Server error. Try again later`));
    });
  }

 
④他のルーティング先へ遷移
 
・Routerのnavigateメソッドを使用。
・引数にルーティング先のパス、ルートパラメータ(使用する場合)を指定。
例)
this._router.navigate([‘/users’]);
edit(id: number) {
this._router.navigate([‘/users/’, id, ‘edit’]);
};

RESTアプリのサンプル
・以下の仕様のRESTアプリ作成
URL HTTPメソッド 処理内容
/users GET ユーザー一覧表示
/users POST 新規ユーザー登録
/users/:id GET 指定したIDのユーザー情報表示
/users/:id PUT 指定したIDのユーザー情報更新実行
/users/:id DELETE 指定したIDのユーザー削除実行
/users/:id/edit GET 指定したIDのユーザー情報変更ビュー表示
 
1)ルーティング設定
 

const appRoutes: Routes = [
//  { path: '', redirectTo: 'users', terminal: true },
  { path:'',
    component: UserIndexComponent},
  { path: 'new',
    component: UserNewComponent},
  { path:'users',
    component: UserIndexComponent},
  { path: 'users/:id',
    component: UserShowComponent},
  { path: 'users/:id/edit',
    component: UserEditComponent},
]

 
2)HTTPリクエスト処理を行うサービスを作成
 
サービス作成とHTTP処理については以下の記事参照
HTTPでサーバーからJSONデータ取得
HTTP POST送信

@Injectable()
export class UserService {
  constructor (private http: Http) {}

  index() {
    let url = '/rest/users';
    return this.http.get(url)
                    .map(res => <User[]> res.json())
                    .catch(this.logAndPassOn);
  }

  create(data: User) {
    let url = '/rest/users';
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });
    let post_data = JSON.stringify(data);
    return this.http.post(url,post_data,options)
                    .map(res => res.json())
                    .catch(this.logAndPassOn);
  }

  update(data: User) {
    let url = '/rest/users';
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });
    let put_data = JSON.stringify(data);
    return this.http.put(url,put_data,options)
                    .map(res => res.json())
                    .catch(this.logAndPassOn);
  }

  show(id: number) {
    let url = '/rest/users/' + id;
    return this.http.get(url)
                    .map(res => <User> res.json())
                    .catch(this.logAndPassOn);
  }

  destroy(id: number) {
    let url = '/rest/users/'+ id;
    return this.http.delete(url)
                    .map(res => res.json())
                    .catch(this.logAndPassOn);
  }
}

 
3)各コンポーネント作成
 
user-index.component.tsを例に示します。
 
●ngOnInitライフサイクルフック
 
ページ表示時にuserServiceからユーザー一覧情報を取得します。
この処理をコンストラクタ内で行う事もできますが、Angular2のドキュメントでは、ngOnInitライフサイクルフックの使用を推奨しています。
 
Angular2では、生成、更新、削除などの各ライフサイクルに応じて実行されるライフサイクルフックを用意しています。ngOnInitメソッドを使うと開始時に実行されるので、コンストラクタ内でデータを取得する必要がなくなります。
 
ngOnInitライフサイクルフックを使用するには、”OnInit”をインポートし、実装します。
import {OnInit} from ‘@angular/core’
export class UserIndexComponent implements OnInit {
・・・

import {Component,OnInit} from '@angular/core'
import {Router} from '@angular/router';
import {UserService,User} from './user.service.ts';

@Component({
  selector: 'user-index',
  template:`
・・・
<button class="btn btn-primary" (click)="show(item.id)">詳細表示</button>
<button class="btn btn-primary" (click)="destroy(item.id)">削除</button>
・・・
<strong>実行結果</strong><br />
<span>{{Result|json}}</span><br />
<button class="btn btn-primary" (click)="cancel()">戻る</button>
  `
})
export class UserIndexComponent implements OnInit {
  public users: User[];
  public Result;

  constructor(private _userService: UserService, private _router: Router) { }
  ngOnInit() {
    this._userService.index()
                     .subscribe(
                       users => this.users = users,
                       error => alert(`Server error. Try again later`));
  }
  show(id: number) {
    this._router.navigate(['/users/', id]);
  }

  destroy(id: number) {
    this._userService.destroy(id)
                     .subscribe(
                       res => {
                         res.comment = "デモ用なのでユーザー一覧画面は更新されません";
                         this.Result = res;
                       },
                       err => this.Result = "エラー!ステータスコード:" + err.status)
  }

  cancel() {
    this._router.navigate(['/users']);
  }
}

デモ
Angular : 4.4.3(2.0.0でも動作確認済み)
Bootstrap : 3.x

 
Loading…

<html>
  <head>
    <title>Angular 2 Demo</title>
    <script src="https://npmcdn.com/core-js/client/shim.min.js"></script>
    <script src="https://unpkg.com/zone.js@0.7.4?main=browser"></script>
    <script src="https://npmcdn.com/systemjs@0.19.39/dist/system.src.js"></script>
    <script src="./js/systemjs.config.js"></script>
    <script>
      System.import('./ts/app.ts').catch(function(err){ console.error(err);  });    </script>
    <link rel="stylesheet" href="./css/bootstrap.min.css">
    <link rel="stylesheet" href="./css/styles.css">
  </head>
  <body>
    <div class="container">
      <my-app>Loading...</my-app>
    </div>
  </body>
</html>

(systemjs.config.js)
(function (global) {
  var ngVer = '@4.4.3';
  System.config({
    transpiler: 'ts',
    typescriptOptions: {
      "target": "es5",
      "module": "commonjs",
      "moduleResolution": "node",
      "sourceMap": true,
      "emitDecoratorMetadata": true,
      "experimentalDecorators": true,
      "lib": ["es2015", "dom"],
      "noImplicitAny": true,
      "suppressImplicitAnyIndexErrors": true,
      "strict": true
    },
    meta: {
      'typescript': {
        "exports": "ts"
      }
    },
    paths: {
      'npm:': 'https://unpkg.com/'
    },
    map: {
      'app': 'app',
      '@angular/animations': 'npm:@angular/animations' + ngVer + '/bundles/animations.umd.js',
      '@angular/animations/browser': 'npm:@angular/animations' + ngVer + '/bundles/animations-browser.umd.js',
      '@angular/core': 'npm:@angular/core' + ngVer + '/bundles/core.umd.js',
      '@angular/common': 'npm:@angular/common' + ngVer + '/bundles/common.umd.js',
      '@angular/common/http': 'npm:@angular/common' + ngVer + '/bundles/common-http.umd.js',
      '@angular/compiler': 'npm:@angular/compiler' + ngVer + '/bundles/compiler.umd.js',
      '@angular/platform-browser': 'npm:@angular/platform-browser' + ngVer + '/bundles/platform-browser.umd.js',
      '@angular/platform-browser/animations': 'npm:@angular/platform-browser' + ngVer + '/bundles/platform-browser-animations.umd.js',
      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic' + ngVer + '/bundles/platform-browser-dynamic.umd.js',
      '@angular/http': 'npm:@angular/http' + ngVer + '/bundles/http.umd.js',
      '@angular/router': 'npm:@angular/router' + ngVer + '/bundles/router.umd.js',
      '@angular/router/upgrade': 'npm:@angular/router' + ngVer + '/bundles/router-upgrade.umd.js',
      '@angular/forms': 'npm:@angular/forms' + ngVer + '/bundles/forms.umd.js',
      '@angular/upgrade': 'npm:@angular/upgrade' + ngVer + '/bundles/upgrade.umd.js',
      '@angular/upgrade/static': 'npm:@angular/upgrade' + ngVer + '/bundles/upgrade-static.umd.js',

      // other libraries
      'rxjs':                      'npm:rxjs@5.0.1',
      'tslib':                     'npm:tslib/tslib.js',
      'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js',
      'ts':                        'npm:plugin-typescript@5.2.7/lib/plugin.js',
      'typescript':                'npm:typescript@2.3.2/lib/typescript.js',

    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
      app: {
        main: './main.ts',
        defaultExtension: 'ts',
        meta: {
          './*.ts': {
            loader: 'systemjs-angular-loader.js'
          }
        }
      },
      rxjs: {
        defaultExtension: 'js'
      }
    }
  });

})(this);

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { NgModule } from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { Component } from '@angular/core';
import { HttpModule } from '@angular/http';
import { Routes, RouterModule }   from '@angular/router';

import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/toPromise';
import {UserService} from './rest/user.service.ts';
import {UserNewComponent}   from './rest/user-new.component.ts';
import {UserIndexComponent} from './rest/user-index.component.ts';
import {UserShowComponent} from './rest/user-show.component.ts';
import {UserEditComponent} from './rest/user-edit.component.ts';

const appRoutes: Routes = [
//  { path: '', redirectTo: 'users', terminal: true },
  { path:'',
    component: UserIndexComponent},
  { path: 'new',
    component: UserNewComponent},
  { path:'users',
    component: UserIndexComponent},
  { path: 'users/:id',
    component: UserShowComponent},
  { path: 'users/:id/edit',
    component: UserEditComponent},
]

export const routing = RouterModule.forRoot(appRoutes);

@Component({
  selector: 'my-app',
  template: `
    <nav>
      <a [routerLink]="['/new']">ユーザー登録</a>
      <a [routerLink]="['/users']">ユーザー一覧</a>
    </nav>
    <router-outlet></router-outlet>
  `
})
export class AppComponent {
}

@NgModule({
  imports: [
    BrowserModule,
    HttpModule,
    routing
  ],
  declarations: [ 
    AppComponent,
    UserNewComponent,
    UserIndexComponent,
    UserShowComponent,
    UserEditComponent
 ],
  providers: [
    UserService
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

platformBrowserDynamic().bootstrapModule(AppModule);

user.service.ts
import {Injectable} from '@angular/core'; import {Http,Headers,RequestOptions} from '@angular/http'; import {Observable} from 'rxjs/Observable'; export interface User { id: number, firstname: string, lastname: string, address: string, email: string } @Injectable() export class UserService { constructor (private http: Http) {} index() { let url = '/rest/users'; return this.http.get(url) .map(res => <User[]> res.json()) .catch(this.logAndPassOn); } create(data: User) { let url = '/rest/users'; let headers = new Headers({ 'Content-Type': 'application/json' }); let options = new RequestOptions({ headers: headers }); let post_data = JSON.stringify(data); return this.http.post(url,post_data,options) .map(res => res.json()) .catch(this.logAndPassOn); } update(id: number, data: User) { let url = '/rest/users/' + id; let headers = new Headers({ 'Content-Type': 'application/json' }); let options = new RequestOptions({ headers: headers }); let put_data = JSON.stringify(data); return this.http.put(url,put_data,options) .map(res => res.json()) .catch(this.logAndPassOn); } show(id: number) { let url = '/rest/users/' + id; return this.http.get(url) .map(res => <User> res.json()) .catch(this.logAndPassOn); } destroy(id: number) { let url = '/rest/users/'+ id; return this.http.delete(url) .map(res => res.json()) .catch(this.logAndPassOn); } private logAndPassOn (error: Error) { console.error(error); return Observable.throw(error); } } user-new.component.ts
import {Component} from '@angular/core'; import {UserService,User} from './user.service.ts'; import {Router} from '@angular/router'; @Component({ selector: 'user-new', template: ` <div class="panel panel-default"> <div class="panel-heading">ユーザー登録</div> <div class="panel-body"> <span>ユーザー登録データ</span><br /> <ul> <li>FirstName:{{new_data.firstname}}</li> <li>LastName:{{new_data.lastname}}</li> <li>Address:{{new_data.address}}</li> <li>Email:{{new_data.email}}</li> </ul> <button class="btn btn-primary" (click)="create()">ユーザー登録</button> <button class="btn btn-primary" (click)="cancel()">戻る</button><br /> <strong>実行結果</strong><br /> <span>{{Result|json}}</span><br /> </div> </div> ` }) export class UserNewComponent{ public Result; public new_data: User = { firstname: "first4_new", lastname: "last4_new", address: "address4_new", email: "email4_new" }; constructor(private _userService: UserService, private _router: Router) { } create() { this._userService.create(this.new_data) .subscribe( res => this.Result = res, err => this.Result = "エラー!ステータスコード:" + err.status); }; cancel() { this._router.navigate(['/users']); }; } user-index.component.ts
import {Component,OnInit} from '@angular/core' import {Router} from '@angular/router'; import {UserService,User} from './user.service.ts'; @Component({ selector: 'user-index', template:` <table class="table table-striped table-bordered"> <thead> <tr><th>ID</th><th>First Name</th><th>Last name</th><th></th></tr> </thead> <tbody> <tr *ngFor="let item of users"> <td>{{item.id}}</td> <td>{{item.firstname}}</td> <td>{{item.lastname}}</td> <td> <button class="btn btn-primary" (click)="show(item.id)">詳細表示</button> <button class="btn btn-primary" (click)="destroy(item.id)">削除</button> </td> </tr> </tbody> </table> <strong>実行結果</strong><br /> <span>{{Result|json}}</span><br /> <button class="btn btn-primary" (click)="cancel()">戻る</button> ` }) export class UserIndexComponent implements OnInit { public users: User[]; public Result; constructor( private _userService: UserService, private _router: Router) { } ngOnInit() { this._userService.index() .subscribe( users => this.users = users, error => alert(`Server error. Try again later`)); } show(id: number) { this._router.navigate(['/users/', id]); } destroy(id: number) { this._userService.destroy(id) .subscribe( // res => this.Result = "id:" + id + "のユーザーを削除しました。デモ用なのでユーザー一覧画面は更新されません。", res => { res.comment = "デモ用なのでユーザー一覧画面は更新されません"; this.Result = res; }, err => this.Result = "エラー!ステータスコード:" + err.status) } cancel() { this._router.navigate(['/users']); } } user-show.component.ts
import { Component, OnInit } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import {User, UserService} from './user.service.ts'; @Component({ template: ` <div class="panel panel-default"> <div class="panel-heading">ユーザー詳細情報</div> <div class="panel-body"> <ul *ngIf="user"> <li>ID:{{user.id}}</li> <li>FirstName:{{user.firstname}}</li> <li>LastName:{{user.lastname}}</li> <li>Address:{{user.address}}</li> <li>Email:{{user.email}}</li> </ul> <button class="btn btn-primary" (click)="edit(user.id)">ユーザ情報変更</button> <button class="btn btn-primary" (click)="cancel()">戻る</button> </div> </div> `, }) export class UserShowComponent implements OnInit { public user: User; private _sub: any; constructor( private _router:Router, private _route: ActivatedRoute, private _userService:UserService){} ngOnInit() { this._sub = this._route.params.subscribe(params => { let id = +params['id']; // (+) converts string 'id' to a number console.log(id); this._userService.show(id) .subscribe( user => this.user = user, error => alert(`Server error. Try again later`)); }); } edit(id: number) { this._router.navigate(['/users/', id, 'edit']); }; cancel() { this._router.navigate(['/users']); } } user-edit.component.ts
import { Component, OnInit } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import {UserService,User} from './user.service.ts'; @Component({ selector: 'user-edit', template: ` <div class="panel panel-default"> <div class="panel-heading">ユーザー情報変更</div> <div class="panel-body"> <span>ユーザー変更内容</span><br /> <ul *ngIf="user"> <li>FirstName:{{user.firstname}}</li> <li>LastName:{{user.lastname}}</li> <li>Address:{{user.address}}</li> <li>Email:{{user.email}}</li> </ul> <button class="btn btn-primary" (click)="update(user)">変更実行</button> <button class="btn btn-primary" (click)="cancel()">戻る</button><br /> <strong>実行結果</strong><br /> <span>{{Result|json}}</span><br /> </div> </div> ` }) export class UserEditComponent implements OnInit { public Result; public user: User; private _id: number; private _sub: any; constructor( private _router:Router, private _route: ActivatedRoute, private _userService:UserService){} ngOnInit() { this._sub = this._route.params.subscribe(params => { this._id = +params['id']; // (+) converts string 'id' to a number this._userService.show(this._id) .subscribe( user => { this.user = user; this.user.firstname += "_rev"; this.user.lastname += "_rev"; this.user.address += "_rev"; this.user.email += "_rev"; }, err => this.Result = "エラー!ステータスコード:" + err.status) }); } update(user) { this._userService.update(this._id,user) .subscribe( res => this.Result = res, err => this.Result = "エラー!ステータスコード:" + err.status) } cancel() { this._router.navigate(['/users']); } }

関連記事の目次

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください