HTML52017.03.03 09:37


이번엔 라우트 안의 라우트를 알아볼것이다.


앱을 개발하다보면 화면단위 혹은 기능단위로 피쳐를 구성하게 된다.
보통 피쳐는 폴더에 대응되며 하나의 피쳐에는 아래 그림에서와 같이 여러개의 콤포넌트들을 포함할 수 있다.



위 그림에서 App Root Component는 Feature A, B, C를 가지며 Feature A는 A1, A2, A3을 가진다.

여기서 A1, A2, A3이  바로 차일드 라우트에 해당한다.


다음 코드는 앵귤러가이드문서의 예이다.


import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { CrisisCenterHomeComponent } from './crisis-center-home.component';
import { CrisisListComponent }       from './crisis-list.component';
import { CrisisCenterComponent }     from './crisis-center.component';
import { CrisisDetailComponent }     from './crisis-detail.component';

const crisisCenterRoutes: Routes = [
  {
    path: 'crisis-center',
    component: CrisisCenterComponent,
    children: [
      {
        path: '',
        component: CrisisListComponent,
        children: [
          {
            path: ':id',
            component: CrisisDetailComponent
          },
          {
            path: '',
            component: CrisisCenterHomeComponent
          }
        ]
      }
    ]
  }
];

@NgModule({
  imports: [
    RouterModule.forChild(crisisCenterRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class CrisisCenterRoutingModule { }


Feature에 해당하는게 CrisisCenter이고 그 안에 CrisisCenterComponent가 Feature의 루트콤포넌트이고 CrisisListComponent, CrisisDetailComponent, CrisisCenterHomeComponent가 있다.

RouterModule.forChild메서드를 사용한것을 눈여겨 보아야 한다. forRoot가 아니다.


이렇게 정의한 모듈은 AppModule즉 루트모듈에 추가해준다. 이 때 앞선 포스트에서도 언급한데로 import순서가 url pattern matching 의 순서와 동일하다는것에 유의해야 한다.


상대적인 네비게이션


crisis center feature를 빌드후 detail component를 보려면 /로 시작하는 절대 주소로 접근하게 된다. 

라우터는 라우트설정의 맨 위에서부터 절대주소를 매칭한다.

절대경로를 통해서 Crisis center feature에 접근이 가능하지만 상위라우팅구조에 링크가 고정된다. 만일 상위 경로(/crisis-center)를 변경하게 되면 링크파라메터를 반드시 변경해주어야 한다.

this.router.navigate(['/hero', hero.id]); // 이런코드가 존재한다면 '/hero'부분을 수정해주어야 한다는 의미로 보임.


이는 현재 URL segment에 상대경로를 정의함으로써 디펜던시로부터 자유로울수 있다. feature영역내에서의 네비게이션은 상위 라우트경로에 상관없이 유지될수 있다.


./ 또는 /가 없이 시작하면 현재 레벨을 의미

../ 는 한단게 상위 레벨을 의미


Router.navigate 메서드로 상대경로로 이동하려면 어떤 라우트트리에 있는지에 대한 정보가 필요하기 때문에 ActivatedRoute를 제공해야 한다.

링크파라메터배열을 링크한 후에 relativeTo속성에 ActivatedRoute를 셋해준다. 그러면 라우터는 현재 액티브된 라우트위치에 기반하여 URL을 계산해낸다.


항상 절대주소를 사용하려면 navigateByUrl메서드를 사용하면 된다.


다음은 Crisis detail을 상대주소로 접근하는 예이다.


src/app/crisis-center/crisis-list.component.ts
constructor(
  private service: CrisisService,
  private route: ActivatedRoute,
  private router: Router
) {}

onSelect(crisis: Crisis) {
  this.selectedId = crisis.id;

  // Navigate with relative link
  this.router.navigate([crisis.id], { relativeTo: this.route });
}


Router서비스가 아니고 RouterLink를 사용하게 된다면 동일한 링크파라메터를 사용한다. relativeTo속성이 보이지 않는데 이는 RouterLink가 내장하고 있다.


template: `

  <ul class="items">

    <li *ngfor="let crisis of crises | async">

      <a [routerlink]="[crisis.id]" [class.selected]="isSelected(crisis)">

        <span class="badge">{{ crisis.id }}</span>

        {{ crisis.name }}

      </a>

    </li>

  </ul>`


CrisisDetailComponent에서  뒤로가기를 하여 다시 리스트로 가고자 한다면 상대경로 이동으로 아래와 같이 시도할 수 있다.


src/app/crisis-center/crisis-detail.component.ts (relative navigation)
// Relative navigation back to the crises
this.router.navigate(['../', { id: crisisId, foo: 'foo' }], { relativeTo: this.route });


신고
Posted by 삼스
분류없음2017.03.02 22:20


https://angular.io/docs/ts/latest/guide/router.html


라우트에 파라메터 적용하기


이전 예제에서 정의한 라우트정의를 다시 보자


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


:id는 path에서 라우트파라메터의 슬롯을 생성한다. 이 경우 라우터는 이 슬롯에 hero의 id를 삽입한다.


heroid 15번의 상세화면으로 이동하고자 한다면 아래와 같이 접근하면 된다.


localhost:3000/hero/15



다음은 hero list를 표시하는 템플릿이다.


template: `

  <h2>HEROES</h2>

  <ul class="items">

    <li *ngFor="let hero of heroes | async"

      (click)="onSelect(hero)">

      <span class="badge">{{ hero.id }}</span> {{ hero.name }}

    </li>

  </ul>


  <button routerLink="/sidekicks">Go to sidekicks</button>

`


그리고 hero를 클릭했을 때 호출된 함수는 아래와 같다.


  onSelect(hero: Hero) {

    this.router.navigate(['/hero', hero.id]);

  }


this.router를 사용하기 위해서는 생성자에서 Router를 inject해야 한다.


constructor(

  private router: Router,

  private service: HeroService

) {}


위와 같이 하면 Hero를 선택하면 HeroDetailComponent에 id값이 파라메터로 전달된다. 코드를 보면 알겠지만 파라메터는  배열로 전달된다.

HeroDetailComponent에서는 id값에 어떻게 접근할 수 있을까?

ActivatedRoute서비스로 가능하다.

ActivatedRoute : 라우팅정보를 위한 원스탑서비스이다.
라우팅서비스에서 얻어지는 ActivatedRoute는 다음과 같은 정보를 제공한다.

url: 라우트된  path의 obserserable로 라우트경로의 배열료 구성된다.
data : 라두트에서 제공되는 데이터 객체를 포함하는 observerable
params : 옵셔널파라메터
queryParams : 모든 라우트에서 유효한 query parameter
fragment : 모들 라우트에서 유효한 URL fragment
outlet : 라우트를 렌더할때 사용되는 RouterOutlet
routeConfig : origin path를 포함하는 라우트에 사용되는 설정정보
parent : 자식 라우트의 경우 부모의 ActivatedRoute
firstChild : 자식 라우트가 있을 경우 첫번째 ACtivatedRoute
children : 현재 라우트 이하 모든 라우트들

다음과 같이 Router, ActivatedRoute, Params를 import 해야 한다.

import { Router, ActivatedRoute, Params } from '@angular/router';

switchMap연산자를 import하면 Observerable 라우트 파라메터들을 처리할 수 있다.

import 'rxjs/add/operator/switchMap';

해당 모듈을 사용하기 위해 inject를 구현한다.

constructor(
  private route: ActivatedRoute,
  private router: Router,
  private service: HeroService
) {}

이 후 ngOnInit에서 ActivatedRoute서비스를 통해서 파라메터에 접근이 가능하다.

ngOnInit() {
  this.route.params
    // (+) converts string 'id' to a number
    .switchMap((params: Params) => this.service.getHero(+params['id']))
    .subscribe((hero: Hero) => this.hero = hero);
}

파라메터가 Observerable로 제공되기 때문에 switchMap연산자를 id파라메터를 이름으로 접근하여 HeroService에 전달이 가능하다.
subscribe메서드로 id가 변경되면 감지해서 Hero 정보를 다시 셋할 수 있다.
이렇게 하면 id정보의 변경을 감지하여 화면은 유지한체 정보를 업데이트 할 수 있다.
shapshot의 경우는 non-observerable로 무조건 list에서만 detail로 진입한다면 사용이 가능하다.

ngOnInit() {
  // (+) converts string 'id' to a number
  let id = +this.route.snapshot.params['id'];

  this.service.getHero(id)
    .then((hero: Hero) => this.hero = hero);
}





신고
Posted by 삼스
분류없음2017.03.02 21:49


다음으로는 세가지에 대해 알아보겠다.

  • App과 route들을 module을 사용해서 feature 영역에 배치하기
  • 콤포넌트에서 다른 콤포넌트로 네이게이트하기
  • 필수 또는 옵셔널한 정보를 라우트파라메터로 전달하기

대부분의 앱을 개발하다보면 업무별로 구분하기 위해서 폴더들을 생성하여 그 아래에 소스들을 배치하게 된다.

src/app/heroes |- hero-detail.component.ts |- hero-list.component.ts |- hero.service.ts |- heroes.module.ts


heros.module.ts는 아래와 같이 정의되어 있다.


import { NgModule }       from '@angular/core';
import { CommonModule }   from '@angular/common';
import { FormsModule }    from '@angular/forms';
import { HeroListComponent }    from './hero-list.component';
import { HeroDetailComponent }  from './hero-detail.component';
import { HeroService } from './hero.service';
@NgModule({
  imports: [
    CommonModule,
    FormsModule,
  ],
  declarations: [
    HeroListComponent,
    HeroDetailComponent
  ],
  providers: [ HeroService ]
})
export class HeroesModule {}


heros feature에는 두개의 콤포넌트가 있다. herolist와 herodetail. 리스트뷰는 조회된 결과를 표시하고 detail은 선택된 hero의 상세정보를 표시한다. list에서 detail로 넘어갈때는  hero id를 넘겨야 한다.


다음은 라우트 설정이다.


src/app/heroes/heroes-routing.module.ts

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroListComponent }    from './hero-list.component';
import { HeroDetailComponent }  from './hero-detail.component';
const heroesRoutes: Routes = [
  { path: 'heroes',  component: HeroListComponent },
  { path: 'hero/:id', component: HeroDetailComponent }
];
@NgModule({
  imports: [
    RouterModule.forChild(heroesRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class HeroRoutingModule { }


두개의 콤포넌트에 대한 라우팅설정을 완료하였다. 하나는 path에 파라메터를 전달할 수 있도록 정의하였다. 그리고 HeroRoutingModule을 export하였다.

그리고 RouterModule에 등록하고 있는데  AppRoutingModule과 다른점은 forRoot가 아니라 forChild를 사용한것이다. feature 모듈에서는 forChild를 사용해야 한다. forRoot는 앱레벨이다.

이제 HerosModule에 추가해야 한다.


import { NgModule }       from '@angular/core';
import { CommonModule }   from '@angular/common';
import { FormsModule }    from '@angular/forms';
import { HeroListComponent }    from './hero-list.component';
import { HeroDetailComponent }  from './hero-detail.component';
import { HeroService } from './hero.service';
import { HeroRoutingModule } from './heroes-routing.module';
@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    HeroRoutingModule
  ],
  declarations: [
    HeroListComponent,
    HeroDetailComponent
  ],
  providers: [ HeroService ]
})
export class HeroesModule {}

앱 메인라우터 설정과 중복이 있더라도 feature 라우터 우선으로 동작하는데는 문제가 없으나 중복은 좋지 않기 때문에 메인 라우터에 중복이 있다면 제거한다.


import { NgModule }              from '@angular/core';
import { RouterModule, Routes }  from '@angular/router';

import { CrisisListComponent }   from './crisis-list.component';
// import { HeroListComponent }  from './hero-list.component';  // <-- delete this line
import { PageNotFoundComponent } from './not-found.component';

const appRoutes: Routes = [
  { path: 'crisis-center', component: CrisisListComponent },
  // { path: 'heroes',     component: HeroListComponent }, // <-- delete this line
  { path: '',   redirectTo: '/heroes', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];

@NgModule({
  imports: [
    RouterModule.forRoot(appRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class AppRoutingModule {}


HorosModule로 별도로 분리된 모듈의 경우 AppModule에 추가해주어야 한다.

app.module.ts에 imports에 추가해준다.

app.module.ts에 import되는 순서에 따라 라우팅시 패턴매칭의 순서가 정해진다. 원하는대로 동작시키길 원한다면 순서에 유의해야 한다.



신고
Posted by 삼스

티스토리 툴바