HTML52017.03.03 12:19


https://angular.io/docs/ts/latest/cli-quickstart.html


시작부터 정리 시작!


CLI로 프로젝트를 시작하는 방법에 대한 안내이다.


1. 개발환경 설정


NodeJs와 NPM설치

node는 6.9.x이상, npm은 3.x.x이상


angular cli는 npm install -g @angular/cli 로 설치


2. 프로젝트 생성


ng new my-app


3. Serve the app


cd my-app

ng serve --open


ng serve는 서버를 기동하고 파일을 감시하고 리빌드한다.


4. 이제 너의 콤포넌트들을 코딩하기 시작해라..


프로젝트 파일 리뷰


src 폴더


root 폴더 

신고
Posted by 삼스
HTML52017.03.03 10:48


Crisis 예제에서 "Contact"버튼으로 메세지를 보낼 수 있는 팝업뷰를 띄우려고 한다.

팝업은 앱내에서 다른 페이지전환이 있어도 유지되기를 원한다. 사용자가 명시적으로 닫기전까지는


지금까지는 단일 아웃렛과 차일드아우트까지 사용하는 법에 대해 알아보았다. 라우터는 이름없는 아웃렛은 템플릿내에 하나만 허용한다.

하지만 템플릿은 이름을 부여(네임드아웃렛)하면 여러개의 아웃렛을 사용할 수 있다. 각 명명된 아웃렛은 자신면의 콤포넌트를 갖는 라우트셋을 가질수 있다. 여러개의 아웃렛이 서로다은 라우트를 가지고 동시에 컨텐츠를 표시할 수 있다.


AppComponent에 "popup"이라고 이름 붙인 아웃렛을 추가한다.


src/app/app.component.ts (outlets) <router-outlet></router-outlet> <router-outlet name="popup"></router-outlet>


팝업이 보여질 위치가 된다.


두번째 라우트


명명된 아웃렛은 두번째라우트의 타겟이다.


두번째 라우트도 첫번째 라우트와 유사하다. 몇가지점에서 다른데.

  • 서로 독립적이다.
  • 다른 라우트와 조화된다.
  • 명명된 아웃렛에 표시된다.

ComposeMessageComponent를 src/app/compose-message.component.ts에 추가한다.


아래와 같은 화면이다.



src/app/compose-message.component.ts
import { Component, HostBinding } from '@angular/core';
import { Router }                 from '@angular/router';
import { slideInDownAnimation }   from './animations';
@Component({
  moduleId: module.id,
  templateUrl: './compose-message.component.html',
  styles: [ ':host { position: relative; bottom: 10%; }' ],
  animations: [ slideInDownAnimation ]
})
export class ComposeMessageComponent {
  @HostBinding('@routeAnimation') routeAnimation = true;
  @HostBinding('style.display')   display = 'block';
  @HostBinding('style.position')  position = 'absolute';
  details: string;
  sending: boolean = false;
  constructor(private router: Router) {}
  send() {
    this.sending = true;
    this.details = 'Sending Message...';
    setTimeout(() => {
      this.sending = false;
      this.closePopup();
    }, 1000);
  }
  cancel() {
    this.closePopup();
  }
  closePopup() {
    // Providing a `null` value to the named outlet
    // clears the contents of the named outlet
    this.router.navigate([{ outlets: { popup: null }}]);
  }
}


src/app/compose-message.component.html <h3>Contact Crisis Center</h3> <div *ngIf="details"> {{ details }} </div> <div> <div> <label>Message: </label> </div> <div> <textarea [(ngModel)]="message" rows="10" cols="35" [disabled]="sending"></textarea> </div> </div> <p *ngIf="!sending"> <button (click)="send()">Send</button> <button (click)="cancel()">Cancel</button> </p>


다른 콤포넌트들과 비슷해 보인다. 다른게 두가지가 있는데. send 메서드가 1초동안 대기했다가 팝업을 닫고 있고 closePopup은 "popup"아웃렛을 null로 호출하고 있다.

다른 콤포넌트들과 마찬가지로 AppModule에 declareations에 ComposeMessageComponent를 추가한다.


두번째 라우트 추가


AppRoutingModule에 라우트를 추가한다.

src/app/app-routing.module.ts (compose route)
{
  path: 'compose',
  component: ComposeMessageComponent,
  outlet: 'popup'
},

outlet속성이 추가되었다. ComposeMessageComponent가 popup outlet에 표시될것이다.

html코드내에서는 아래와 같이 호출한다.

src/app/app.component.ts (contact-link) <a [routerLink]="[{ outlets: { popup: ['compose'] } }]">Contact</a>

 compose 라우트가 popup 라우트에 고정되었도 RouterLink디렉티브에 라우트를 지정하기에는 충분하지 않다. 링크파라메터를 지정하고 속성바인딩으로 RouterLink에 바인딩해야 한다.

링크파라메터에는 outlets속성이 객체로 하나 존재하며 popup 아웃렉 속성과 값으로 다른 링크파라메터가  compose 라우트가 지정되어 있다.

이제 사용자가 링크를 클릭하면 compose 라우트가 popup아웃렛에 표시될것이다.


두번째 라우트 네비게이션 : 라우트머징


Crisis Center에서 Contact를 클릭하면 아래와 같은 주소가 찍히는 것을 확인할 수 있다.


http://.../crisis-center(popup:compose)


관심이 쏠리는것이 바로 ... 부분인데.


  • cirsis-center는 첫번째 네비게이션
  • 괄호부분은 두번째 네이게이션
  • 두번째 네비게이션은 popup 아웃랫과 라우트를 의미

 Heros링크를 누르면 이번에 주소가 


http://.../heros(popup:compose)


첫번째주소부분이 변경되었다. 두번째 라우트는 동일하다.

라우터는 첫번째와 두번째 브랜치를 유지한다. 그리고 URL를 표현해준다.

수많은 아웃렉과 라우트를 탑레벨이나 하위레벨로 만들어낼 수 있다. 라우터는 알어서 url을 만들어낸다.


두번째 라우트 정리


아웃렛의 콤포넌트는 새로운 콤포넌트로 이동하기전까지는 계속 유지된다. 두번째 아웃렛도 마찬가지이다. 

모든 두번째 아웃렛은 자체 네비게이션을 가지며 독립적으로 수행된다. 다른 아웃렛의 변경이 영향을 미치지 않는다는 것이다. 그래서 Crisis나 Heroes로 이동해도 Compose는 계속 보여질 수 있는 것이다.

위 예에서 보면 아래와 같이 popup을 제거하는 것을 알 수 있다.


src/app/compose-message.component.ts (closePopup)
closePopup() {
  // Providing a `null` value to the named outlet
  // clears the contents of the named outlet
  this.router.navigate([{ outlets: { popup: null }}]);
}




신고
Posted by 삼스
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 삼스

티스토리 툴바