Category Archives: Angular2

[Angular2] 다이나믹하게 페이지 타이틀 설정하기 (Dynamic Title)

페이지별로 타이틀을 다르게 설정하고 싶을 때, 어떻게 하면 좋을까?

routing module에 타이틀 정보를 넣어두고, 그 정보를 바탕으로 타이틀을 설정하는 방법이 있다.

 

1. Title Service 만들기

import {Injectable} from "@angular/core";
import {Title}     from "@angular/platform-browser";

@Injectable()
export class TitleService {
    static DEFAULT_TITLE = "기본 타이틀";
    constructor(
        private title: Title
    ) {}

    setTitle(str?: string): void {
        let titleString = str ? `${str} : ${TitleService.DEFAULT_TITLE}` : TitleService.DEFAULT_TITLE;
        this.title.setTitle(titleString);
    }
}

angular/platform-browser 의 타이틀 설정을 활용하자.

 

2. routing module에 타이틀 정보 넣어두기

import {NgModule} from "@angular/core";
import {RouterModule} from "@angular/router";
import {AuthGuard} from "../core/services/auth-guard.service";

@NgModule({
    imports: [
        RouterModule.forChild([
            {
                path: "",
                component: IndexComponent,
                children: [
                    {
                        path: "apply",
                        component: ApplyComponent,
                        data: {
                            title: "신청",
                        },
                    }
                ],
            },
        ]),
    ],
})

export class RoutingModule {
}

component하단에  data: { title: “신청”} 형식으로 타이틀을 넣어두었다. 말인 즉슨 타이틀 이외에도 다른 정보를 넣어서 다른 처리도 가능하다.

 

3. app.component.ts OnInit 에서의 처리

this.router.events

            .filter((event) => event instanceof NavigationEnd)

            .map(() => this.route)

            .map((route) => {

                while (route.firstChild) route = route.firstChild;

                return route;

            })

            .filter((route) => route.outlet === "primary")

            .subscribe((subject) => {

                if (subject.data) {

                    let event = subject.data["value"];
                    console.log("subscribe!", event)
                    this.titleService.setTitle(event["title"]); 

                    if (window['ga']) {

                        window['ga']('set', 'page', location.pathname);

                        window['ga']('send', 'pageview');

                    }

                }

        });

하나씩 살펴보자

  1. router.events 는 router 를 observable 형태로 볼수 있다. 자세한 것은 여기 문서 를 참조.
  2. event 에 필터를 걸어서 NavigationEnd, 즉 탐색이 완전히 끝나는 정보만 본다.
  3. this.route 는 constructor의 private router: Router 를 의미한다. 그리고 state tree의 마지막 정보를 보기 위해 while문을 추가하였다.
  4. .filter((route) => route.outlet === “primary”)
 로 필터링 하여 children정보도 볼 수 있도록 한다.
  5. 이제 가져온 정보에서 title을 세팅하면 된다.

기존에 Google링 한 자료에서는 .mergeMap(route => route.data) 까지 필요했었는데, 

이작업까지 할경우 같은 컴포넌트에서 라우팅을 할 경우 (같은 페이지로의 이동) subscribe가 중복으로 붙으면서 내부로직을 누적해서 호출하게 된다.(..)

 

[Angular2 + HammerJS] Swipe시 스크롤이 안되는 문제 (Scrolling disabled on Hammer.js Swipe element?)

Angular2에서 swipe를 구현하기 위해 hammerJS를 사용했다.

자세한 사용법은 하단에 나와있으니 참고하면 좋다.

근데 한가지 문제가 발견됐는데, Swipe를 사용하는 영역 위에서는 페이지 스크롤이 안될수도 있다는 것이다.

문제는 저 샘플에서도 스크롤이 안된다는 것(…)

export class MyHammerConfig extends HammerGestureConfig  {
  overrides = <any>{
      'swipe': {velocity: 0.4, threshold: 20} // override default settings
  }
}

이부분에서 HammerGestureConfig를 customize화 시킬 필요가 있다.

declare var Hammer: any;

import {Inject, Injectable, OpaqueToken} from "@angular/core";

export const HAMMER_GESTURE_CONFIG: OpaqueToken = new OpaqueToken("HammerGestureConfig");

export interface HammerInstance {
  on(eventName: string, callback: Function): void;
  off(eventName: string, callback: Function): void;
}

@Injectable()
export class HammerGestureCustomConfig {
  events: string[] = [];
  overrides: { [key: string]: Object } = {};

  buildHammer(element: HTMLElement): HammerInstance {
    let mc = new Hammer(element, {
      touchAction: "pan-y",
    });
    return mc;
  }
}

이런식으로 touchAction을 pan-y를 주면된다. swipe에 대한 기본 값은 pan-x다. touchAction에 대한 자세한 내용은 여기를 참고하면 된다.

auto 로 주는 것도 가능하지만, auto로 설정할 경우,  swipe를 대각선으로 하면 페이지 스크롤도 동시에 일어나서 보기에 매끄럽지가 않다.

 

이 문제 때문에 anuglar2, hammerjs github 등에 이슈로 올라오거나 stackoverflow에 질문이 많았다.

그러나 변변한 답을 못찾아 고생을 했는데, 정답은 역시 document에 있다. 늘 그런건 아니지만.

[Angular2] DOMSanitizer by Pipe

innertHTML등으로 string의 내용을 html DOM 에 바인딩 할때,  스크립트 공격 등을 방어하기 위해 Angular2는 DOMSanitizer를  제공한다.

이를 pipe형태로 간단하게 템플릿에서 사용하기 위해 아래와 같이 처리했다.

import {Pipe, PipeTransform} from "@angular/core";
import {DomSanitizer} from "@angular/platform-browser";

@Pipe({name: "safeHtml"})
export class SafeHtmlPipe implements PipeTransform {
    constructor(private sanitized: DomSanitizer) {
    }

    transform(value: string) {
        return this.sanitized.bypassSecurityTrustHtml(value);
    }
}

@Pipe({name: "safeCss"})
export class SafeCssPipe implements PipeTransform {
    constructor(private sanitized: DomSanitizer) {
    }

    transform(value: string) {
        return this.sanitized.bypassSecurityTrustStyle(value);
    }
}

@Pipe({name: "safeScript"})
export class SafeScriptPipe implements PipeTransform {
    constructor(private sanitized: DomSanitizer) {
    }

    transform(value: string) {
        return this.sanitized.bypassSecurityTrustScript(value);
    }
}

@Pipe({name: "safeResourceUrl"})
export class SafeResourceUrlPipe implements PipeTransform {
    constructor(private sanitized: DomSanitizer) {
    }

    transform(value: string) {
        return this.sanitized.bypassSecurityTrustResourceUrl(value);
    }
}

 

[Angular2] RouterOutlet 의 부모 정보 가져오기

는 바로 Host 데코레이터를 사용하면 됩니다.

constructor (
        @Host() private parent: ParentComponent) { }
    
    
    ngOnInit() {
        console.log(this.parent.category)
    }

Host 데코레이터는 현재 컴포넌트에서 host를 선언한 엘리먼트가 닿을 때까지 의존성 트리를 뒤지면서,

이와 일치하는 의존성을 가진 인젝터를 가져옵니다.

router-outlet을 썼을 경우, 부모 – 자식 관계를 갖기 때문에 이런식으로 부모의 값을 가져올 수 있습니당

 

 

[Angular2, Typescript…] json결과를 Model 객체로 Serialize하기

Angular2, Typescript 와만 관련이 있다고 볼순 없지만..

보통 api서버를 통해서 결과를 json객체로 가져돈다.

그 json객체를 typescipt에서 선언한 모델 객체로 변환하는 방법을 적어본다.

 

일단 이렇게  api결과가 왔고

{
    "id": 1,
    "name": "안녕",
    "created_at": "2015-10-07T11:42:17.000+09:00",
    "updated_at": "2016-04-29T11:15:28.000+09:00",
    "blocked": false,
    "show": true,
    "order": null
}

이 Test 객체에 담아 보자.

export class Test {
  id: number;
  name: string;
  blocked: boolean;
  show: boolean;
  order: number;
  created_at: Date;
  updated_at: Date;
}

모델에 이런 메소드를 만들자.

이 메소드는 object assign을 이용하여 any객체를 test 모델에 할당할 것이다.

그리고 created_at, updated_at이 있다면 이를 Date로 변환할 것이다.

참고로 http 는 import {Http } from “@angular/http”;

요거다.

static fromJSON(obj: any) {
        return Object.assign(new Test(), obj, {
            created_at: obj.created_at && new Date(obj.created_at as string),
            updated_at: obj.updated_at && new Date(obj.updated_at as string)
        });
    }

이제 웹 Api에서  serialize 하자

test() {
        return this.http.post(`/this/is/test/url`)
            .toPromise()
            .then(a => Test.fromJSON(a.json()))
    }

요렇게 하자.

리스트 형태로 날라오면 아래와 같이 하자.

test() {
        return this.http.post(`/this/is/test/url`)
            .toPromise()
            .then(r => (<any[]>r.json()).map(json => Test.fromJSON(json)))
    }