CHAPTER 15. Router

Intro

이 장에서는 마지막 라우터 API (v3)를 사용한다. 즉, 라우터 패키지의 버전 번호가 3.0.0이고 다른 패키지의 버전 번호가 2.0.0 인 경우이다.

URL을 응용 프로그램의 상태로 매핑하는 것이 일반적이다. 즉, 사용자가 페이지를 북마크에 추가하고 돌아올 수 있기를 원하면 전체적으로 더 나은 환경을 제공 할 수 있다. 이 작업을 담당하는 부분을 라우터라고 하며, 모든 프레임 워크에는 고유한 (또는 몇 가지) 프레임 워크가 있다.

Angular 2의 라우터는 앱의 상태를 반영하는 의미있는 URL을 허용하고 각 URL에 대해 페이지에서 어떤 구성 요소를 초기화하고 삽입해야 하는지를 간단히 알 수 있다. 페이지를 새로 고치지 않고 백엔드 서버에 대한 새로운 요청을 트리거하지 않고 이 모든 것을 실행할 수 있다.

코어 팀에서 ngRoute라는 모듈로 관리하는 AngularJS 1.x에 이미 라우터가 있다는 것을 알고 있을 것이다. 또한 매우 단순하다는 것을 알 수 있다. 간단한 응용 프로그램에서는 OK이지만 URL 당 단일보기 만 허용하고 중첩이 불가능 했다. 조회수가 많은 대형 앱에서 작업하는 데 약간의 제한이 있었다. 많은 사람들이 사용하고 있으며 정말 훌륭한 일을 하고 있는 ui-router라고 하는 매우 인기있는 커뮤니티 모듈이 있었다.

Angular 2의 팀은 격차를 줄이기로 결정하고 RouterModule이라는 새로운 모듈을 작성했다. 이 모듈은 우리의 모든 요구를 충족시킬 것이다! 새로운 기능 중 일부는 정말 흥미로울 것이다.

En route

라우터 사용을 시작해보자. 이 모듈은 코어 프레임 워크에 포함되지 않는 선택적 모듈이다.

다른 모듈에서 보았 듯이 모듈을 사용하려면 루트 모듈에 모듈을 포함해야 한다. 그러나 이를 위해서는 URL과 구성 요소 간의 매핑을 정의하는 구성이 필요하다. 일반적으로 app.routes.ts와 같은 이름의 전용 파일로 구성 할 수 있으며 구성을 나타내는 배열을 포함한다.

import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { RacesComponent } from './races/races.component';
export const ROUTES: Routes = [
  { path: '', component: HomeComponent },
  { path: 'races', component: RacesComponent }
];

그런 다음 루트 모듈에서 라우터 모듈을 가져 와서 적절한 구성으로 초기화 해야한다.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { ROUTES } from './app.routes';
import { HomeComponent } from './home/home.component';
import { RacesComponent } from './races/races.component';
@NgModule({
  imports: [BrowserModule, RouterModule.forRoot(ROUTES)],
  declarations: [PonyRacerAppComponent, HomeComponent, RacesComponent],
  bootstrap: [PonyRacerAppComponent]
})
export class AppModule {
}

또한 루트 모듈의 선언 속성에서 라우터 모듈이 사용하는 모든 구성 요소를 선언해야 한다. 보시다시피 Routes는 객체의 배열이며 각 객체는 경로이다. 라우트 구성은 보통 한 쌍의 등록 정보이다.

• path : 탐색을 트리거 할 URL • component : 초기화되고 삽입 될 컴포넌트

구성 요소가 페이지에 삽입 될 위치 당신은 궁금 할 것이다, 그것은 좋은 질문이다. 위의 예에서 RacesComponent와 같이 앱에 포함될 구성 요소의 경우 주 구성 요소의 템플릿에 특수 태그를 사용해야 한다.

이것은 물론 Angular 지시문이다. 이 지시문은 현재 경로의 구성 요소 템플릿에 대한 자리 표시자 역할을 수행한다. 우리의 앱 템플릿은 다음과 같다.

<header>
  <nav>...</nav>
</header>
<main>
  <router-outlet></router-outlet>
  <!-- the component's template will be inserted here-->
</main>
<footer>made with &lt;3 by Ninja Squad</footer>

탐색 할 때 모든 항목 (머리글, 기본 및 바닥 글)이 유지되며 현재 경로와 일치하는 구성 요소가 RouterOutlet 지시문 바로 뒤에 삽입된다.

Transforming data

Observable 객체를 응답으로 받고 있으므로 데이터를 쉽게 변형 할 수 있다는 사실을 잊지 마시오. 경주 이름의 목록을 원하는가? 인종을 요청하고 이름을 매핑하면 된다! 이렇게 하려면 RxJS에 대한 mapoperator를 가져와야 한다. Angular 팀은 앱에 전체 RxJS 라이브러리를 포함하기를 원하지 않으므로 명시 적으로 필요한 연산자를 가져와야 한다.

import 'rxjs/add/operator/map';

이제 사용할 수 있다.

http.get(`${baseUrl}/api/races`)
// extract json body
  .map(res => res.json())
  .subscribe(races => {
   // store the array of the races in the component
   this.races = races;
  });

이러한 종류의 작업은 일반적으로 전용 서비스로 수행된다. RaceService와 같은 서비스를 만드는 경향이 있다. RaceService는 모든 작업이 완료된 곳이다. 그런 다음 내 구성 요소는 서비스 방법을 구독 해야하며 후드 내부에서 무슨 일이 일어나고 있는지 알지 못한다.

raceService.list()
  .subscribe(races => {
   // store the array of the races in the component
   this.races = races;
  });

또한 RxJS를 사용하여 실패한 요청을 몇 번 다시 시도 할 수 있다.

raceService.list()
  // if the request fails, retry 3 times
  .retry(3)
  .subscribe(races => {
   // store the array of the races in the component
   this.races = races;
  });

다른 구성 요소를 어떻게 탐색 할 수 있을까? 글쎄, 수동으로 URL을 입력하고 페이지를 새로 고침 할 수는 있지만 그다지 편리하지는 않는다. 그리고 우리는 "로"고전적인 "링크를 사용하고 싶지 않다. 실제로 링크를 클릭하면 브라우저가 해당 URL에서 페이지를 로드하고 전체 Angular 응용 프로그램을 다시 시작한다. 그러나 Angular의 목표는 이러한 페이지 다시 로드를 피하는 것이다. 단일 페이지 응용 프로그램을 만들고 싶다. 물론 내장된 솔루션이 있다.

템플릿에서 이동하려는 경로를 가리키는 지시문 RouterLink가 포함 된 링크를 삽입 할 수 있다. 루트 모듈이 RouterModule을 가져와 RouterModule의 내 보낸 모든 지시문을 루트 모듈에 사용할 수 있으므로 이 지시문을 사용할 수 있다. outerLink 지시문은 이동하려는 경로를 나타내는 상수 또는 경로 및 해당 매개 변수를 나타내는 문자열 배열을 받을 수 있다. 예를 들어 RacesComponent 템플릿에서 HomeComponent로 이동하려면 상상해 보라.

<a href="" routerLink="/">Home</a>
<!-- same as -->
<a href="" [routerLink]="['/']">Home</a>

런타임에 링크 href는 라우터에 의해 계산되고 /를 가리 킵니다.

경로의 선행 슬래시가 필요하다. 포함되어 있지 않다면, RouterLink는 URL을 현재 경로에 상대적으로 작성한다 (나중에 보게 될 것처럼 중첩 된 구성 요소에 유용 할 수 있다). 슬래시를 추가하면 응용 프로그램 기본 URL에서 URL을 계산해야 함을 나타낸다.

RouterLink 지시문은 RouterLinkActive 지시문과 함께 사용할 수 있다. 지시문은 링크가 현재 경로를 가리키는 경우 CSS 클래스를 자동으로 설정할 수 있다. 예를 들어, 현재 페이지를 가리킬 때 선택된 메뉴 항목의 스타일을 지정할 수 있다.

<a href="" routerLink="/" routerLinkActive="selected-menu">Home</a>

라우터 서비스와 해당 메소드 navigate ()를 사용하여 코드에서 탐색 할 수도 있다. 작업 후 사용자를 리디렉션하려는 경우에 편리하게 사용된다.

export class RacesComponent {
  constructor(private router: Router) {
  }
  saveAndMoveBackToHome() {
   // ... save logic ...
   this.router.navigate(['']);
  }
}

이 메서드는 첫 번째 요소로 탐색 할 경로가있는 매개 변수 배열을 사용한다. URL에 매개 변수를 포함하는 것도 가능하며 동적 URL을 정의하는 것이 좋다. 예를 들어 ponyies / the-pony- / the-pony-name-the-pony 같은 이 페이지의 의미있는 URL이 있는 조랑말에 대한 세부 정보 페이지를 표시하려고 한다.

그렇게 하기 위해 하나 또는 여러 개의 동적 매개 변수로 구성에서 경로를 정의하자

export const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'races', component: RacesComponent },
  { path: 'races/:raceId/ponies/:ponyId', component: PonyComponent }
];

그런 다음 routerLink를 사용하여 동적 링크를 정의 할 수 있다.

<a href="" [routerLink]="['/races', race.id, 'ponies', pony.id]">See pony</a>

물론 대상 구성 요소는 주어진 식별자로 포니를 로드하고 표시 할 수 있도록 이러한 매개 변수에 액세스 해야 한다. 매개 변수 값을 얻기 위해 라우터는 ActivatedRoute라는 구성 요소에 물론 삽입 할 수있는 서비스를 제공한다. 이 객체는 ngOnInit 내부에서 사용할 수 있으며 매우 유용한 필드 인 스냅 샷이 있다. 이 필드에는 params에있는 URL의 모든 매개 변수가 있다!

export class PonyComponent implements OnInit {
  pony: any;
  constructor(private ponyService: PonyService, private route: ActivatedRoute) {
  }
  ngOnInit() {
   const id = this.route.snapshot.params['ponyId'];
   this.ponyService.get(id).subscribe(pony => this.pony = pony);
  }
}

이 고리는 볼 수있는 것 처럼 구성 요소의 초기화 작업을 수행하기에도 좋다. 당신이 발견했을지도 모르는 것에 따라, 우리는 스냅 사진을 사용하고 있다. 스냅 샷이 아닌 버전이 있는가? 그렇다. 그리고 그것은 매개 변수 변경을 구독 할 수있는 방법을 제공한다. 이 관찰 가능한 것을 params라고 한다.

이것은 매우 중요하다 : 라우터는 가능한 경우 구성 요소를 재사용 한다! 우리 어플리케이션이 다음 조랑말을 볼 수있는 "다음"버튼을 가지고 있다고 가정 해 보자. 사용자가 클릭 할 때 URL은 / ponies / 1에서 / ponies / 2로 변경된다. 그러면 라우터가 구성 요소 인스턴스를 다시 사용한다. 즉, ngOnInit이 다시 호출되지 않는다! 이러한 종류의 탐색을 위해 구성 요소를 업데이트하려면 params observable을 사용하는 것 외에는 다른 방법이 없다!

export class PonyReusableComponent implements OnInit, OnDestroy {
  pony: any;
  paramsSubscription: Subscription;
  constructor(private ponyService: PonyService, private route: ActivatedRoute) {
  }
  ngOnInit() {
   this.paramsSubscription = this.route.params.subscribe(params => {
   const id = params['ponyId'];
   this.ponyService.get(id).subscribe(pony => this.pony = pony);
   });
  }
  ngOnDestroy() {
   this.paramsSubscription.unsubscribe();
  }
}

여기서는 ActivatedRoute에서 제공하는 관찰 가능 항목을 구독하고 필드에 구독을 저장하므로 나중에 ngOnDestroy에서 구독을 취소하여 메모리 누수를 피할 수 있다. 이제 URL이 '/ ponies / 1'에서 '/ ponies / 2'로 바뀔 때마다 관찰 할 수있는 매개 변수가 이벤트를 발생시키고 화면에 표시 할 올바른 조랑말을 가져온다. params 업데이트를 구독 한 내부의 PonyService 결과에 가입하는 대신 좀더 우아한 switchMap 연산자를 사용할 수 있다.

export class PonySwitchMapComponent implements OnInit, OnDestroy {
  pony: any;
  paramsSubscription: Subscription;
  constructor(private ponyService: PonyService, private route: ActivatedRoute) {
  }
  ngOnInit() {
   this.paramsSubscription = this.route.params
   .map(params => params['ponyId'])
   .switchMap(id => this.ponyService.get(id))
   .subscribe(pony => this.pony = pony);
  }
  ngOnDestroy() {
   this.paramsSubscription.unsubscribe();
  }
}

Reference URL

Last updated