반응형

어씽크(asynchronous) 핸들러입니다. 솔찍히 Promise와 모가 다른지 모르겠지만... 말이죠.

 

Ajax와 같은 기존에 사용하던 기능이 있는데 생소한 Observable을 사용하려니 이만저만 힘든게 아니라는 생각이 들었습니다.

하지만 Router를 이용하면서 이미 Observable를 이용했다고 하니 외부 라이브러리를 사용할 필요가 없다는 것을 알았습니다.

 

Route.params.subscribe()에서 params가 Observable이였습니다.

 

다음은 Observable을 사용하는 간단한 예제입니다.

import { Component, OnDestroy, OnInit } from '@angular/core';

import { interval, Subscription } from 'rxjs';

export class HomeComponent implements OnInit, OnDestroy {

  private firstObsSubscription: Subscription;

  ngOnInit() {
    this.firstObsSubscription = interval(1000).subscribe(count => {
      console.log(count);
    });
  }

  ngOnDestroy(): void {
    this.firstObsSubscription.unsubscribe();
  }

}

직접 unsubscribe해야 합니다. route.params에서는 Angular가 뒤에서 unsubscribe까지 지원해 주었습니다.

 

data(customedName), error, complete를 이용하기 예제

const customIntervalObservable = Observable.create(observer => {
  let count = 0;
  setInterval(() => {
    observer.next(count);
    if (count == 5) {
      observer.complete();
    }
    if (count > 3) {
      observer.error(new Error('Count is greater 3!'));
    }
  count++;
  }, 1000);
      
this.firstObsSubscription = customIntervalObservable.subscribe(data => {
    console.log(data);
  }, error => {
    console.log(error);
    alert(error.message);
  }, () => {
    console.log('Completed!');
  });
}

error혹은 complete을 만나면 event emit은 끝납니다.

 

Operators를 이용해서 filter, map등 이용할 수 있습니다.

import { map, filter } from 'rxjs/operators';

customIntervalObservable.pipe(filter(data => {
      return data > 0;
    }), map((data: number) => {
      return 'Round: ' + (data+1);
    })).subscribe...

Subject는 service를 이용하여 컴포넌트간 통신을 할 때 유용합니다.

 

Observable대신, Subject로 할 수 있습니다. 두 개의 차이는 Passvie와 Active로 나눌 수 있습니다.

Subject를 사용하면 수동으로 next()를 사용해야 하는데,


다만 @Output를 사용할 때, EventEmitter대신 Subject를 사용시 주의해야 합니다.

 

Useful Resources:
Official Docs: https://rxjs-dev.firebaseapp.com/
RxJS Series: https://academind.com/learn/javascript/understanding-rxjs/
Updating to RxJS 6: https://academind.com/learn/javascript/rxjs-6-what-changed/

 

RxJS 6 - What Changed?

RxJS 6 changes everything! Or does it? It certainly breaks your imports and operator usage. Time for some (quick!) fixes!

academind.com

 

자료 출처 : https://www.udemy.com/share/1000imAkEeeVhSQ34=/

반응형

'프로그래밍 > Angular' 카테고리의 다른 글

FormHandling - Reavice  (0) 2019.07.02
Handling Form - Template Driven  (0) 2019.07.02
Router 정리  (0) 2019.06.25
Routes  (0) 2019.06.17
Providers - Hierarchical Injector  (0) 2019.06.16
반응형

Router를 정리하고 직접 구현에 적용해 보았습니다.

<ts.file>

 

app 모듈 등록시 '' path를 'localhost:4200'일 때만 적용하려면,

const appRoutes: Routes = 
[ 
  { path: '', component: HomeComponent},
  { path: 'users', component: UsersComponent},
  { path: 'servers', component: ServersComponent}, 
];

path: '' 일경우, 아래와 같이 전체 경로 주소임을 나타내는 full을 추가해 준다.

{path: '', redirectTo: '/recipes', pathMatch: 'full'},

동적경로를 추가 하고 싶을 때, :foo 와 같이 콜론(:)을 앞에 붙여 추가한다.

{path: ':id'}

Route.navigate() 매쏘드 사용법 - from[localhost:4200/recipes] to[localhost:4200/recipes/0/edit]

id: number;

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

this.router.navigate(["/recipes", this.id, "edit"]) // 절대경로
this.router.navigate([this,id, "edit"], {relativeTo: this.route}) // 상대경로
this.router.navigate(["../", this,id, "edit"], {relativeTo: this.route}) // 한단계 이전 위치에서 상대경로 이동

 

<HTML>

 

routerLinkActive : 선택된 Element에 active class를 추가시켜줌으로써, css효과를 추가해 준다.

routerLink : /로 시작하지 않으면, 상대 주소기 때문에, 현 위치를 기준으로 아래 주소를 써주면 된다.

(ex: localhost:4200/recipes/0 으로 이동하는 링크 [현 위치: localhost:4200/recipes]의 child)

<a
  style="cursor: pointer;"
  [routerLink]="[index]"
  routerLinkActive="active"
  class="list-group-item clearfix">
  <div class="pull-left">
    <h4 class="list-group-item-heading">{{ recipe.name }}</h4>
    <p class="list-group-item-text">{{ recipe.description }}</p>
  </div>
  <span class="pull-right">
        <img
          [src]="recipe.imagePath"
          alt="{{ recipe.name }}"
          class="img-responsive"
          style="max-height: 50px;">
      </span>
</a>

[routerLink]="[index]"는 자동적으로 localhost:4200/recipes/index로 이동하게 된다.

 

 

반응형

'프로그래밍 > Angular' 카테고리의 다른 글

Handling Form - Template Driven  (0) 2019.07.02
Observable & Subject  (0) 2019.06.26
Routes  (0) 2019.06.17
Providers - Hierarchical Injector  (0) 2019.06.16
custom attribute Directive  (0) 2019.06.09
반응형

1. routerModule 등록

2. routerLink 사용하기

3. router 코딩하기

4. 상대주소 사용하기

5. 유저 불러오기(예제)

6. Query Parameter & Fragment

7. Child(Nested)Routes

8. Redirecting & Wildcard Routes

9. Outsourcing the Foute Configuration

10. Guard

11. Data with resolve Guard


1. app.module.ts (등록하기)

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

const appRoutes: Routes = [
  { path: '', component: HomeComponent},
  { path: 'users', component: UsersComponent},
  { path: 'servers', component: ServersComponent},
]

imports: [
  BrowserModule,
  FormsModule,
  RouterModule.forRoot(appRoutes)

path는 맨 앞의 / 를 제외합니다. ("localhost:4200/")가 이미 앞에 있음으로, "/"를 생략하고 입력해 줍니다.

RouterModule의 forRoot에 선언한 route를 등록합니다.

 

main.html

<div class="row">
  <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
    <app-home></app-home>
  </div>
</div>
<div class="row">
  <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
    <app-users></app-users>
  </div>
</div>
<div class="row">
  <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
    <app-servers></app-servers>
  </div>
</div>

아래와 같이 변경 간단하게 줄일 수 있습니다.

<div class="row">
  <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
    <router-outlet></router-outlet>
  </div>
</div>

 

router-outlet은 directive랍니다.

 

하지만 이렇게만 바꾸면, 새로운 페이지가 로딩됩니다.

물론, 네비게이션 클릭시, 
혹은 주소 입력시 서버에 해당 주소로 이동하도록 요청을 보내고, 
서버에서 페이지를 내려 받기 때문에, 
리로딩이 되는것이 맞습니다.(주소창에 해당 주소를 치는것과 같은 효과) 하지만 베스트가 아닙니다.

스테이트를 잃어버리기 때문에 사용자에게 제공하려는 서비스가 아닙니다.


main.html(네비게이션)

<div class="row">
  <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
    <ul class="nav nav-tabs">
      <li role="presentation" class="active"><a href="/">Home</a></li>
      <li role="presentation"><a href="/servers">Servers</a></li>
      <li role="presentation"><a href="/users">Users</a></li>
    </ul>
  </div>
</div>

href 속성 대신, routerLink라는 directive를 사용합니다.

<ul class="nav nav-tabs">
  <li role="presentation" class="active"><a routerLink="/">Home</a></li>
  <li role="presentation"><a routerLink="/servers">Servers</a></li>
  <li role="presentation"><a [routerLink]="'/users'">Users</a></li>
</ul>

[routerLink]으로 property(속성)으로 사용할 경우 자바스크립트에선 invaild name이기 때문에 ''로 한번 더 감싸야 합니다.

 

또 다른 방법으로, 

<li role="presentation"><a [routerLink]="['/users']">Users</a></li>

[] 배열안에 여러가지를 추가 할 수 있습니다. 이 방법을 사용한다면, 매우 쉽게 복잡한 경로를 추가할 수 있습니다.

 

routerLink 사용 시 주의 사항,

절대 경로 : "/"로 /로 시작.

상대 경로 : "./어딘가"이나, "../저긴가"와 같이 /로 시작하지 않습니다.

 

css클라스를 이용하지 않고,

routerLinkActive를 이용한다면, bootstrap없이 사용가능합니다.

 

각 리스트 아이템: <li>에 routerLinkActive를 추가해줍니다.

<li 
	role="presentation" 
	routerLinkActive="active"
	[routerLinkActiveOptions]="{exact: true}">
  <a routerLink="/">Home</a>
</li>

만약 [routerLinkActiveOptions]="{exact: true}"가 없다면,

bootstap은 "/" 가 앞에 있기 때문에 home의 네비게이션이 active인 상태로 만들어 버립니다.

 

exact는 정확히 / 만 있을 경우를 해당 주소로 인식하게 만드는 기능을 합니다.


3. ts파일로 작동하기

import { Router } from "@angular/router";

constructor(private router: Router) { }

onLoadServers() {
  // 복잡한 경로 계산이 가능해짐.
  this.router.navigate(['/servers'])
}

4. 상대주소

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

  onReload() {
    this.router.navigate(['somewhere'],{relativeTo: this.route})
  }

somewehere이라는 상대주소를 사용했습니다.

router.navigate의 2번째 파라미터에 {}자바스크립트 객체를 넣고,

relativeTo속성에 현재 위치가 어디인지를 나타냅니다.

현재 어떤 라우터가 작동하고있는지(ActivatedRoute) 알려줍니다.

 

*relativeTo가 없을 경우, root에서 추가하기 때문에, 원하지 않는 곳으로 이동할 수 있습니다.


5. 유저 불러오기

 

app.module.ts

const appRoutes: Routes = [
  { path: 'users/:id/:name', component: UsersComponent},// dynamic part of the path :'s meaning
]

path에 ":"는 다이나믹한 값을 받아올 수 있게 합니다.

 

ts

export class UserComponent implements OnInit {
  user: {id: number, name: string};

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.user = {
      id: this.route.snapshot.params['id'],
      name: this.route.snapshot.params['name']
    };
    this.route.params
      .subscribe(
        (params: Params) => {
          this.user.id = params['id'];
          this.user.name = params['name'];
        }
      );
  }
}

HTML

<p>User with ID {{user.id}} loaded.</p>
<p>User name is {{user.name}}</p>
<hr>
<a [routerLink]="['/users',10, 'Anna']">Load Anna (10)</a>

Snapshot: 일회성 사용시(the no-observable alternative)

특정 컴퍼넌트의 재사용이 필요 없다면, snapshot을 사용하세요.

router는 항상 새로운 인스턴스를 생성합니다.

 

router.snapshot은 router 최초 값(initial value)으로 작동합니다.

스냅샷을 이용한다면, subscribing이나 observable operator를 추가하지 않고도 쉽게 읽고 쓰기를 진행할 수 있습니다.

this.route.params
  .subscribe(
    (params: Params) => {
      this.user.id = params['id'];
      this.user.name = params['name'];
    }
  );

위 코드 없이 Load Anna (10) 링크를 클릭하면,

URL은 바뀌지만, HTML의 내용은 변하지 않음을 확인할 수 있습니다.

 

Angular는 UserComponent(동일 컴포넌트)에서 <a> link를 누를경우 re-rendering을 하지 않습니다. 

이는 Angular의 default 동작으로, Angular는 컴포넌트 전체를 destroy하고 recreate하지 않는 답니다.

그러기에 rendering을 다시 할 수 있도록, subscribe를 사용합니다.

 

*참고: this.route.params는 observables입니다.

기본적으로, observables는 third-party 패키지로 추가된 기능입니다.

Angular의 기능은 아니지만 어씽크(asynchronous) 작업을 쉽게 지원하기 때문에 많이 사용하고 있습니다.

link를 누르는 것은 어씽크 작업입니다.

불러온 route의 현재 패러미터값은 언제 변경될 모르고 얼마나 걸릴지도 모르기 때문입니다.

또한 링크를 누르지 않을 수 도 있기 때문에, 무한정 기다리거나 코드를 없애서도 안됩니다.

 

*참고: OnDestroy()

꼭 해야할 필요는 없지만, 알아두어야 할 기능이 있습니다. life cycle의 ngDestroy()입니다.

Angular는 background에서 해당 컴포넌트를 destroy합니다. 동시에 subscription을 제거해줍니다.

만약 하지 않는다면, subscription을 제거하지 않는다면, 해당 컴포넌트는 제거되지만, subscription은 계속 남아 있게 됩니다.

import { Subscription } from 'rxjs/Subscription';

export class UserComponent implements OnInit, OnDestroy {
  user: {id: number, name: string};
  paramsSubscription: Subscription;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.paramsSubscription = this.route.params
      .subscribe(
        (params: Params) => {
          this.user.id = params['id'];
          this.user.name = params['name'];
        }
      );
  }

  ngOnDestroy() {
    this.paramsSubscription.unsubscribe();
  }

}

6. Query Parameter & Fragment

 

?mode=editing#loading

을 추가하려면 어떻게 해야할까요?

 

1. app.module.ts / path 추가

const appRoutes: Routes = [
  { path: 'servers/:id/edit', component: EditServerComponent}
]

2. servers.component.html / routerLink, queryParams, fragment

<div class="row">
  <div class="col-xs-12 col-sm-4">
    <div class="list-group">
      <a
        [routerLink]="['/servers', 5, 'edit']"
        [queryParams]="{allowEdit: '1'}"
        fragment="loading"
        href="#"
        class="list-group-item"
        *ngFor="let server of servers">
        {{ server.name }}
      </a>
    </div>
  </div>
</div>

routerLink는 path에 추가된 조건과 동일하게 맞추어 줍니다.

 

queryParams는 routerLink의 'edit'뒤에 파라미터를 추가할 수 있지만, queryParams라는 directive를 사용해보겠습니다.

javascript 객체에 key-value 페어를 이용합니다.

fragment도 추가할 수 있습니다.

 

3. home.component.html & Home.component.ts / 코딩으로 routing

 

HTML

<button class="btn btn-primary" (click)="onLoadServers(1)">Load Server 1</button>

1 이라는 서버로 이동하는 매쏘드로 변경합니다.

 

onLoadServers(id: number) {
  this.router.navigate(['/servers', id, 'edit'], {queryParams: {allowEdit: '1'}, fragment: 'loading'})
}

2번과 같이 key-value로 추가합니다.

 

relativeTo를 사용했지만, 위와 같이도 가능합니다.


7. Child(Nested)Routes


app.module.ts

const appRoutes: Routes = [
  { path: '', component: HomeComponent},
  { path: 'users', component: UsersComponent},
	  { path: 'users/:id/:name', component: UsersComponent},
  
  { path: 'servers', component: ServersComponent},
	  { path: 'servers/:id', component: ServerComponent},
	  { path: 'servers/:id/edit', component: EditServerComponent}
]
const appRoutes: Routes = [
  { path: '', component: HomeComponent},
  { path: 'users', component: UsersComponent, children: [
    { path: ':id/:name', component: UserComponent},
  ]},
  { path: 'servers', component: ServersComponent, children: [
    { path: ':id', component: ServerComponent},
    { path: ':id/edit', component: EditServerComponent}
  ]}
]

children으로 중복되는 경로를 보기 쉽게 해줍니다.

component가 선언되어 있기 때문에, 해당 HTML도 변경되어야합니다.

 

servers.component.HTML

<div class="col-xs-12 col-sm-4">
  <router-outlet></router-outlet>
    <!-- <button class="btn btn-primary" (click)="onReload()">Reload Page</button> -->
    <!-- <app-edit-server></app-edit-server> -->
    <!-- <hr> -->
    <!-- <app-server></app-server> -->
</div>

users.componente.HTML

<div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
  <router-outlet></router-outlet>
</div>

8. Redirecting & Wildcard Routes

 

app.module.ts

const appRoutes: Routes = [
  { path: '', component: HomeComponent},
  { path: 'users', component: UsersComponent, children: [
    { path: ':id/:name', component: UserComponent},
  ]},
  { path: 'servers', component: ServersComponent, children: [
    { path: ':id', component: ServerComponent},
    { path: ':id/edit', component: EditServerComponent}
  ]},
  { path: 'not-found', component: PageNotFoundComponent},
  { path: '**', redirectTo: '/not-found'}
]

path: '**' 와일드 카드는 무조건 맨 아래에 있어야 합니다.

만약 맨 위에 위치하게 된다면, 모든 경로는 리다이랙트 됩니다.

 

*Tip:

{ path: '', redirectTo: '/somewhere-else', pathMatch: 'full' } 을 사용해야 합니다.

Angulart의 매칭 방법(matching strategy)은 "prefix"입니다.

Angular는 입력한 URL이 해당 path로 시작하는지 체크합니다. 물론 매번 ''으로 시작하기 때문에(중요: whitespace는 존재하지 않음으로, 언제나 "nothing"입니다.)

그러므로 매칭 방법을 "full"로 변경해야 합니다.


9. Outsourcing the Foute Configuration

 

9.1 module.ts파일을 만듭니다.

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

export class AppRoutingModule {

}

9.2 app.module.ts

imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule
  ]

imports에 추가합니다.

 

routing 정보를 파일로 만들면, 파일 정리 및 가독성을 높일 수 있습니다.


10. Guard

Authentication 을 Fake로 간단히 만들고, 어떤 페이지에서 이탈할 경우, 정말 나갈 것인지 묻도록 합니다.

 

10.1. auth.service | 로그인 기능

export class AuthService {
    loggedIn = false;

    isAuthenticated() {
        const promise = new Promise(
            (resolve, reject) => {
                setTimeout(()=> {
                    resolve(this.loggedIn);
                }, 800);
            }
        );
        return promise;
    }

    login() {
        this.loggedIn = true;
    }

    logout() {
        this.loggedIn = false;
    }
}

로그 in/out은 home.component를 통해 간단하게 조작합니다.

onLogin() {
    this.authService.login();
  }

  onLogout() {
    this.authService.logout();
  }

10.2 auth-guard.service | 접근 통제

import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, CanActivateChild } from "@angular/router";
import { Observable } from "rxjs";
import { Injectable } from "@angular/core";
import { AuthService} from "./auth.service";

@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {
    constructor(private authServlice: AuthService, private router: Router) {}
    canActivate(route: ActivatedRouteSnapshot,
                state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
        return this.authServlice.isAuthenticated()
            .then(
                (authenticated: boolean) => {
                    if (authenticated) {
                        return true;
                    } else {
                        this.router.navigate(['/']);
                    }
                }
            )
        }
    canActivateChild(route: ActivatedRouteSnapshot,
                    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    return this.canActivate(route, state);
    }
}

canActivateChild매쏘드는 일정 페이지 이상의 접근을 제한합니다.

app-routing.module.ts

{ path: 'servers',
      //   canActivate: [AuthGuard], 
      canActivateChild: [AuthGuard],
      component: ServersComponent, 
      children: [
      { path: ':id', component: ServerComponent},
      { path: ':id/edit', component: EditServerComponent, canDeactivate: [CanDeactivateGuard]}
    ]},

10.3 이탈 방지 코드

can-deactivate-guard.service.ts

import { Observable } from "rxjs/Observable";
import { ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router";

export interface CanComponentDeactivate {
    canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}

export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
    canDeactivate(component: CanComponentDeactivate,
        currentRoute: ActivatedRouteSnapshot,
        currentState: RouterStateSnapshot,
        nextState?: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
            return component.canDeactivate();
        }
}

ㅠㅠ 실력이 없어서... 위 코드를 따라하기만 하네요... 설명하고 싶은데 못하겠어요... 어플 만들고나서 이해가 필요한 부분이네요!

그 때쯤이면 어느정도 실력이 쌓여서 지금 풀어보는 것보다 더 이해도가 높겟쪄? ㅎㅎ

 

edit-server.component에서 실수로 뒤로가기를 누르거나, 업데이트 등 할 때 사용자에게 한번 더 묻습니다.

canDeactivate(): Observable<boolean> | Promise<boolean> | boolean {
    if (!this.allowEdit) {
      return true;
    }
    if ((this.serverName !== this.server.name || this.serverStatus !== this.server.status) && !this.chagesSaved) {
      return confirm('Do you want to discard the changes?');
    } else {
      return true;
    }
  }

물론 app-routing.module.ts에 추가하고, app.module에 provider로 등록해야합니다.

providers: [ServersService, AuthService, AuthGuard, CanDeactivateGuard],

11. Data with resolve Guard

11.1. static data를 route로 전달하기

 

11.1.1. routing.module.ts 에 코드 추가 data: {message: '원하는 문구'}

{ path: 'not-found', component: ErrorPageComponent, data: {message: 'Page not found'} },

11.1.2. route로 data 불러오기

export class ErrorPageComponent implements OnInit {

  errorMessage: string;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.data.subscribe(
      (data: Data) => {
        this.errorMessage = data['message'];
      }
    )
  }
}

 

11.2. Resolving 다이나믹 데이터

11.2.1. resolver 만들기

import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from "@angular/router";
import { Observable } from "rxjs";
import { ServersService } from "../servers.service";
import { Injectable } from "@angular/core";

interface Server {id: number, name: string, status: string}

@Injectable()
export class ServerResolver implements Resolve<Server> {
  constructor(private serversService: ServersService) {}
  resolve(route: ActivatedRouteSnapshot, status: RouterStateSnapshot): Observable<Server> | Promise<Server> | Server {
    return this.serversService.getServer(+route.params['id']);
  }
}

interface Resolve 를 구현 했기 대문에 route와 status의 스냅샷이 필요합니다.

+route.params['id']는 getServer가 number 타입을 원하기 때문에 꼼수로 앞에다 +를 추가하였습니다.

 

11.2.2. app.module.ts 에 providers 추가하기

providers: [ServersService, AuthService, AuthGuard, CanDeactivateGuard, ServerResolver],

 

11.2.3. routing.module.ts에 resolve추가

{ path: ':id', component: ServerComponent, resolve: {server: ServerResolver} },

 

11.2.4. 코드 적용하기

  ngOnInit() {
    this.route.data.subscribe(
      (data: Data) => {
        this.server = data['server'];
      }
    )
    // const id = +this.route.snapshot.params['id'];
    // this.server = this.serversService.getServer(id);
    // this.route.params
    //   .subscribe(
    //     (params: Params) => {
    //         this.server = this.serversService.getServer(+params['id']);
    //     }
    //   )
  }

기존의 코드를 주석처리 합니다.

route를 통해 data가 변경될 때 마다 받아 옵니다.

 

만약 /servers입력시,  index.html로 돌아간다면

@NgModule({
    imports: [
        RouterModule.forRoot(appRoutes, {useHash: true})
    ],
    exports: [RouterModule]
})

hash가 주소창 앞에 붙여

#이전의 주소와 #이후의 주소로 구별하여, 실제 server에 접근하지 않도록 방지해 줍니다.

 

 

 

 

 

 

 

 

 

 

 

출처: https://www.udemy.com/share/1000imAkEeeVhSQ34=/

Angular 8 (formerly Angular 2) - The Complete Guide

제제작자 Maximilian Schwarzmülle

routing-final.zip
0.06MB

 

반응형

'프로그래밍 > Angular' 카테고리의 다른 글

Observable & Subject  (0) 2019.06.26
Router 정리  (0) 2019.06.25
Providers - Hierarchical Injector  (0) 2019.06.16
custom attribute Directive  (0) 2019.06.09
style 스타일링  (0) 2018.09.05
반응형

providers의 메타 데이터는 객체를 지정합니다.

아래와 같은 방식으로 FooService와 BarService 객체를 지정합니다.

@Component({
  selector: 'app-active-users',
  templateUrl: './active-users.component.html',
  styleUrls: ['./active-users.component.css'],
  providers: [FooService, BarService]
})

 

provider를 어디에 지정하느냐에 따라 인스턴스 공유 범위가 달라집니다.

 

AppModule : Application에 같은 인스턴스를 사용할 수 있습니다.

 

AppComponent : 모든 컴포넌트와 같은 인스턴스를 사용할 수 있습니다.

 

하위 컴포넌트 : 해당 컴포넌트와 그 아래 모든 자식 컴포넌트와 같은 인스턴스를 사용할 수 있습니다.

 

 

 

개별적으로 인스턴스를 선언하여 사용할 경우, constructor에 선언하여 사용합니다.

export class Foo {
  constructor(private barService: BarService){}
}

 

Service 객체를 사용하면, 이벤트를 emit없이 손쉽게 객체간 communication이 가능합니다.

 

Logging과 같은 경우 common으로 주로 사용하기 때문에, 매우 유익하겠죠?

반응형

'프로그래밍 > Angular' 카테고리의 다른 글

Router 정리  (0) 2019.06.25
Routes  (0) 2019.06.17
custom attribute Directive  (0) 2019.06.09
style 스타일링  (0) 2018.09.05
FormsModule With ViewChild  (0) 2018.09.05
반응형

Attribute Directives (링크)

 

DOM element의 모습 및 행동을 변경합니다.

오른쪽은 예제 입니다. Attribute Directive example / download example.

 

Attribute directives는 element의 속성으로 사용됩니다.  Template Syntax guide의 built-in NgStyle directive입니다. 예제로써, 동시에 여러 element를 변경할 수 있습니다.

 

1. foo.directive.ts에 selector는 ['appFoo'] 와같이 []로 감쌉니다.

2. HTML에 <p appFoo class"example"> 예제 </p> 와같이 사용합니다.

Directives 기능

HostListener - style

constructor (private elRef: ElementRef, private renderer: Renderer2) { }



@HostListener('mouseenter') mouseover(eventData: Event) {

this.renderer.setStyle(this.elRef.nativeElement, 'background-color', 'blue', false, false);

}



@HostListener('mouseleave') mouseleave(eventData: Event) {

this.renderer.setStyle(this.elRef.nativeElement, 'background-color', 'transparent', false, false);

}

HostListener, HostBinding - style

@HostBinding('style.backgroundColor') backgroundColor: string = 'transparent';

constructor (private elRef: ElementRef, private renderer: Renderer2) { }



@HostListener('mouseenter') mouseover(eventData: Event) {

  this.backgroundColor = 'blue';

}



@HostListener('mouseleave') mouseleave(eventData: Event) {

  this.backgroundColor = 'transparent';

}

TS file

@Directive({
  selector: '[appHighlight]'
)}

@Input() defaultColor: string = 'transparent';
@Input() highlightColor: string = 'blue';
@HostBinding('style.backgroundColor') backgroundColor: string = 'transparent';

constructor (private elRef: ElementRef, private renderer: Renderer2) { }

ngOnInit() {
  this.backgroundColor = this.defaultColor;
}

@HostListener('mouseenter') mouseover(eventData: Event) {
  this.renderer.setStyle(this.elRef.nativeElement, 'background-color', 'blue', false, false);
  this.backgroundColor = this.highlightColor;
}

@HostListener('mouseleave') mouseleave(eventData: Event) {
  this.renderer.setStyle(this.elRef.nativeElement, 'background-color', 'transparent', false, false);
  this.backgroundColor = this.defaultColor;
}

HTML file

<p appHighlight [defaultColor]="yellow" [highlightColor]="red"> Style me with a better directive!</p>

 

유용한 드롭다운 Directive - class

class에 open을 삽입함으로써 작동하게 하는 bootstrap 코드입니다.


Closing the Dropdown From Anywhere

If you want that a dropdown can also be closed by a click anywhere outside (which also means that a click on one dropdown closes any other one, btw.), replace the code of dropdown.directive.ts by this one (placing the listener not on the dropdown, but on the document):

TS file

import {Directive, ElementRef, HostBinding, HostListener} from '@angular/core';
 
@Directive({
	selector: '[appDropdown]'
})

export class DropdownDirective {
@HostBinding('class.open') isOpen = false;
@HostListener('document:click', ['$event']) toggleOpen(event: Event) {
	this.isOpen = this.elRef.nativeElement.contains(event.target) ? !this.isOpen : false;
}
constructor(private elRef: ElementRef) {}

출처(102번 강좌) https://www.udemy.com/share/1000imAkEeeVhSQ34=/


반응형

'프로그래밍 > Angular' 카테고리의 다른 글

Routes  (0) 2019.06.17
Providers - Hierarchical Injector  (0) 2019.06.16
style 스타일링  (0) 2018.09.05
FormsModule With ViewChild  (0) 2018.09.05
Component- Event  (0) 2018.08.12
반응형

다이나믹하게 표현하는 방법 입니다.


클래스를 이용한 방법. (css, html)


[ngClass]를 이용하여 스타일을 적용 시킬 수 있습니다.


CSS

.even {
color: #0000FF;
}


HTML

<li
class="list-group-item"
[ngClass]="{even: number %2 == 0}"
*ngFor="let number of Numbers">
{{number}}
</li>


{className: operation}

연산된 값은 false | ture 입니다.


스타일을 이용한 방법. (html)


[ngStyle]을 사용한 방법


HTML

<li
class="list-group-item"
[ngClass]="{even: number %2 == 0}"
[ngStyle]="{backgroundColor: number %2 === 0 ? 'yellow' : 'transparent'}"
*ngFor="let number of Numbers">
{{number}}
</li>


{propertyName: operation}

연산된 값은 #FFFF00와 같이 입력되어야 합니다.

 .even-bg {

background-color: #FFFF00;
}


Attribute Directive 를 이용한 방법. (ts, html)


ts파일을 생성하여 간편하게 사용할 수 있습니다.


HTML 태그의 속성 처럼 사용이 가능합니다.


ts

import { Directive, ElementRef, OnInit } from '@angular/core';
@Directive({
selector: '[appBasicHighlight]'
})
export class BasicHighlightDirective implements OnInit {
constructor(private elementRef: ElementRef) {
}

ngOnInit() {
this.elementRef.nativeElement.style.backgroundColor = 'green';
}
}


HTML

<p appBasicHighlight>Style me with basic directive!</p>


하지만 이 방법은 native 엘레먼트에 직접적으로 상호작용(interact)할 수 없으므로 renderer2를 사용을 권장합니다.


import { Directive, Renderer2, OnInit, ElementRef } from '@angular/core';

@Directive({
selector: '[appBetterHighlight]'
})
export class BetterHighlightDirective implements OnInit {

constructor(private elRef: ElementRef, private renderer: Renderer2) { }

ngOnInit() {
this.renderer.setStyle(this.elRef.nativeElement, 'background-color', 'blue')
}
}


반응형

'프로그래밍 > Angular' 카테고리의 다른 글

Providers - Hierarchical Injector  (0) 2019.06.16
custom attribute Directive  (0) 2019.06.09
FormsModule With ViewChild  (0) 2018.09.05
Component- Event  (0) 2018.08.12
Input decoretor  (0) 2018.08.12
반응형

HTML에서 Input에 있는 값을 [(ngModel)]이 아닌 ViewChild데코레이터를 사용하는 방법입니다.


<HTML>

<label for="name">Name</label>
<input
type="text"
id="name"
class="form-control"
#nameInput>


<TS>

@ViewChild('nameInput') nameInputRef: ElementRef;

getInputName() {
return this.nameInputRef.nativeElement.value;
}


레퍼런스를 이용하여 해당 엘레먼트의 값을 가져오는 방식입니다.


HTML의 input 태그의 #nameInput이 @ViewChild('nameInput')을 함으로써 바인딩 됩니다.


nameInputRef의 타입은 엥귤러/코어 에 있는 ElementRef를 이용하여 접근합니다.

ElementRef를 이용함으로써 native값에 접근할 수 있습니다.

반응형

'프로그래밍 > Angular' 카테고리의 다른 글

custom attribute Directive  (0) 2019.06.09
style 스타일링  (0) 2018.09.05
Component- Event  (0) 2018.08.12
Input decoretor  (0) 2018.08.12
Component- DataBinding  (0) 2018.08.11
반응형

@Output - angular/core (컴포넌트 이벤트 바인딩)


-EventEmitter를 이용하여, emit을 실행함. 주의, 제네릭에 클래스를 넣어 진행.


Tip: @Output('customName') 을 사용하면, 외부로 노출되는 @Output() 변수명 : {}의 변수명을 노출하지 않음.


작동 원리 :

0. ts의 EventEmitter 및 onComponentEevet가 선언.

1. component.hmtl의 click이벤트 발생.

2. eventDirective에 EventEmitter가 주입되어 있으므로, eventDirective.emit을 통하여 event를 날림.

3. parent.html의 onParentEvent()로 연결된 이벤트를 실행함.




parent.component.ts

eventElements = [{type: 'customEvent', name: 'Testevent', content: 'Just a test!'}];

onEventAdded(eventData: {eventName: string, eventContent: string}) {
this.eventElements.push({
type: 'customEvent',
name: eventData.eventName,
content: eventData.eventContent
});
}


parent.component.html

<div class="container">
<app-cockpit (eventCreated)="onEventAdded($event)">
</app-cockpit>
</div>


cockpit.component.ts

@Output() eventCreated = new EventEmitter<{eventName: string, eventContent: string}>();
newEventName = '';
newEventContent = '';

onAddEvent() {
this.eventCreated.emit({
eventName: this.newEventName,
eventContent: this.newEventContent
});
}


cockpit.component.html

<div class="row">
<div class="col-xs-12">
<label>Event Name</label>
<input type="text" class="form-control" [(ngModel)]="newEventName">
<label>Event Content</label>
<input type="text" class="form-control" [(ngModel)]="newEventContent">
<br>
<button
class="btn btn-primary"
(click)="onAddEvent()">Add Event</button>
</div>
</div>



반응형

'프로그래밍 > Angular' 카테고리의 다른 글

style 스타일링  (0) 2018.09.05
FormsModule With ViewChild  (0) 2018.09.05
Input decoretor  (0) 2018.08.12
Component- DataBinding  (0) 2018.08.11
Pipe  (0) 2018.06.07
반응형

옵션

설명
bindingPropertyName

데코레이터는 클레스의 필드에 입력 속성으로 마킹하는 기능을 하며 메타데이터 구성을 제공합니다. 바인딩된 데이터의 입력 속성을 명시하면 앵귤러는 자동적으로 값을 업데이트 합니다.

옵션

데코레이터는 클레스의 필드에 입력 속성으로 마킹하는 기능을 하며 메타데이터 구성을 제공합니다. 바인딩된 데이터의 입력 속성을 명시하면 앵귤러는 자동적으로 값을 업데이트 합니다.

bindingPropertyName: string

컴포넌트가 인스턴스화 할 때, 템플릿에서 마음껏 다른 이름을 사용할 수 있도록 바인드 속성의 이름을 맵핑합니다.

기본적으로, 바인드된 속성의 원래 이름이 입력 바인딩을 위해서 사용됩니다.

아래 예제에서 2개의 입력 속성을 갖는 컴포넌트를 만들었습니다, 1개는 특별한 바인딩 이름을 갖습니다.

  1. @Component({
  2. selector: 'bank-account',
  3. template: `
  4. Bank Name: {{bankName}}
  5. Account Id: {{id}}
  6. `
  7. })
  8. class BankAccount {
  9. // 이 속성은 바운드된 본래의 이름을 사용합니다.
  10. @Input() bankName: string;
  11. // 템플릿에서 이 컴포넌트가 인스턴스화 될 때
  12. // 이 속성 값은 다른 속성 이름으로 바운드 됩니다.
  13. @Input('account-id') id: string;
  14.  
  15. // 이 속성은 바운드되지 않으며, 앵귤러가 자동적으로 업데이트할 수 없습니다.
  16. normalizedBankName: string;
  17. }
  18.  
  19. @Component({
  20. selector: 'app',
  21. template: `
  22. <bank-account bankName="RBC" account-id="4747"></bank-account>
  23. `
  24. })
  25.  
  26. class App {}


반응형

'프로그래밍 > Angular' 카테고리의 다른 글

FormsModule With ViewChild  (0) 2018.09.05
Component- Event  (0) 2018.08.12
Component- DataBinding  (0) 2018.08.11
Pipe  (0) 2018.06.07
NgModules  (0) 2018.06.07
반응형

@Input() - angular/core (컴포넌트 데이터 바인딩)


public으로 외부 컴포넌트에서 접근할 수 있도록 도와주는 데코레이터


parent component.ts

componentElements = [{type: 'example', name: 'Testexample', content: 'Just a test!'}];


parent component.html

<app-component-element
*ngFor="let componentElement of componentElements"
[customPropertyName]="componentElement"></app-component-element>

html에서는 []를 이용하여 속성으로 접근. 


component.ts
import { Input } from '@angular/core';

@Input('customPropertyName') element : {type: string, name: string, content: string};

(component.ts의 데코레이터 변수명(element)에 따라 데코레이터의 패러미터를 달리 할 수 있음.

속성명과 @Input 데코레이터의 변수명이 다를경우: @Input('customPropertyName') element: {}

속성명과 @Input 데코레이터의 변수명이 같은경우: @Input() element: {} 


component.html

<div class="panel panel-default">
<div class="panel-heading">{{ element.name }}</div>
<div class="panel-body">
<p>
<strong *ngIf="element.type === 'example'" style="color: red">{{ element.content }}</strong>
</p>
</div>
</div>



응용한 예제 보기


API 보기 (블로그, 원본)

반응형

'프로그래밍 > Angular' 카테고리의 다른 글

FormsModule With ViewChild  (0) 2018.09.05
Component- Event  (0) 2018.08.12
Input decoretor  (0) 2018.08.12
Pipe  (0) 2018.06.07
NgModules  (0) 2018.06.07

+ Recent posts