2017-12-27 3 views
1

Angular2 데코레이터를 래핑하는 일부 기능을 만들려고합니다.TypeScript의 데코레이터 내에서 데코레이터를 재사용하는 방법

경고 : 그래서 나는 다음을 만들어 호스트에 CSS 클래스를 추가하는 과정을 단순화 할

type Constructor = {new(...args: any[]): {}}; 

export function AddCssClassToHost<T extends Constructor>(cssClass: string) { 
    return function (constructor: T) { 
     class Decorated extends constructor { 
      @HostBinding("class") cssClass = cssClass; 
     } 
     // Can't return an inline class, so we name it. 
     return Decorated; 
    }; 
} 

내가 또 다른를 만들 수 있도록하려면 AOT 컴파일 작동하지 않는다 특정 CSS 클래스를 추가하는 데코레이터.

/** 
* Decorator to be used for components that are top level routes. Automatically adds the content-container class that is 
* required so that the main content scrollbar plays nice with the header and the header won't scroll away. 
*/ 
export function TopLevelRoutedComponent<T extends Constructor>(constructor: T) { 
    // This causes an error when called as a decorator 
    return AddCssClassToHost("content-container"); 
    // Code below works, I'd like to avoid the duplication 
    // class Decorated extends constructor { 
    //  @HostBinding("class") cssClass = "content-container"; 
    // } 
    // return Decorated; 
} 

// Called like 
@TopLevelRoutedComponent 
@Component({ 
    selector: "vcd-administration-navigation", 
    template: ` 
     <div class="content-area"> 
      <router-outlet></router-outlet> 
     </div> 
     <vcd-side-nav [navMenu]="navItems"></vcd-side-nav>` 
}) 
export class AdminNavigationComponent { 
    navItems: NavItem[] = [{nameKey: "Multisite", routerLink: "multisite"}]; 
} 

내가 오류 메시지가

TS1238: Unable to resolve signature of class decorator when called as an expression. 
Type '(constructor: Constructor) => { new (...args: any[]): AddCssClassToHost<Constructor>.Decorated; p...' is not assignable to type 'typeof AdminNavigationComponent'. 
Type '(constructor: Constructor) => { new (...args: any[]): AddCssClassToHost<Constructor>.Decorated; p...' provides no match for the signature 'new(): AdminNavigationComponent' 

내가 모두

function wrapWithHostBindingClass<T extends Constructor>(constructor: T, cssClass: string) { 
    class Decorated extends constructor { 
     @HostBinding("class") cssClass = cssClass; 
    } 
    return Decorated; // Can't return an inline decorated class, name it. 
} 
export function AddCssClassToHost(cssClass: string) { 
    return function(constructor) { 
     return wrapWithHostBindingClass(constructor, cssClass); 
    }; 
} 
export function TopLevelRoutedComponent(constructor) { 
    return wrapWithHostBindingClass(constructor, "content-container"); 
} 

에 의해 호출되는 함수를 작성하여 해결 할 수 있었다입니다 거기 도우미 기능을 필요로하지 않거나 코드를 복제하지 않고 첫 번째 스타일 작업을 수행하는 방법은 무엇입니까? 나는 내 시도가 훌륭하지 않다는 것을 깨닫고 이해하지 못한다. 그러나 나는 오류 메시지 주위에 머리를 맞출 수가 없었다. (가지)이다

버전 AOT 호환 다음이 훨씬 간단하기 때문에, 그것은 AOT 컴파일러 충돌이 발생하지 않습니다. 그러나 webpack 컴파일러를 사용하는 경우 사용자 정의 데코레이터가 제거되므로 ngc로 컴파일 할 때만 작동합니다.

export function TopLevelRoutedComponent(constructor: Function) { 
    const propertyKey = "cssClass"; 
    const className = "content-container"; 
    const descriptor = { 
     enumerable: false, 
     configurable: false, 
     writable: false, 
     value: className 
    }; 
    HostBinding("class")(constructor.prototype, propertyKey, descriptor); 
    Object.defineProperty(constructor.prototype, propertyKey, descriptor); 
} 

답변

1

장식 검사기를 통과하지 못하게하는 장식 자의 유형 서명에 약간의 오류가 있습니다.

를 해결하기 위해, 당신이 장식장식 공장 사이의 차이를 이해하는 것이 필수적이다.

데코레이터 팩토리는 예상대로 알 수 있듯이 데코레이터를 반환하는 함수입니다.

매개 변수화하여 적용된 데코레이터를 사용자 정의하려면 데코레이터 팩토리를 사용하십시오.

예로 들어 코드를 사용하려면 다음은 장식 공장에서 사용하는 CSS를 사용자 정의 매개 변수를 사용

export type Constructor<T = {}> = new (...args: any[]) => T; 

export function AddCssClassToHost<T extends Constructor>(cssClass: string) { 
    return function (C: T) { 

     // TypeScript does not allow decorators on class expressions so we create a local 
     class Decorated extends C { 
      @HostBinding("class") cssClass = cssClass; 
     } 
     return Decorated; 
    }; 
} 

장식 공장은 반환 장식이 대상을 대체하는 와 (그것은 한 입이었다).

하지만 이제는 특정 CSS를 추가하기 위해 데코레이터 팩토리를 다시 사용하고 싶습니다. 즉 우리는 공장이 아닌 일반 오래된 장식자를 필요로한다는 것을 의미합니다.

데코레이터 팩토리는 우리가 필요로하는 것을 반환하기 때문에 호출 할 수 있습니다.

export const TopLevelRoutedComponent = AddCssClassToHost("content-container"); 

이제 우리는 응용 프로그램

@TopLevelRoutedComponent 
@Component({...}) 
export class AdminNavigationComponent { 
    navItems = [{nameKey: "Multisite", routerLink: "multisite"}]; 
} 

에 오류가 발생하고 우리는 이유를 고려해야합니다, 두 번째 예제를 사용합니다.

팩토리 함수를 호출하면 형식 매개 변수가 인스턴스화됩니다.

제네릭 데코레이터를 반환하는 제네릭 데코레이터 팩터 리 대신 제네릭 데코레이터를 만드는 팩토리가 필요합니다!

즉, TopLevelRoutedComponent은 제네릭이어야합니다.

우리는 단순히 여기에

export function AddCssClassToHost(cssClass: string) { 
    return function <T extends Constructor>(C: T) { 

     // TypeScript does not allow decorators on class expressions so we create a local 
     class Decorated extends C { 
      @HostBinding("class") cssClass = cssClass; 
     } 
     return Decorated; 
    }; 
} 

live example

+0

가 큰 작품이다 리턴 장식하는 형식 매개 변수를 이동, 우리의 원래 장식 공장을 다시 작성하여, 나는하지 않았다 거의 부끄러워 것을 할 수 이걸 생각해 보라 –

+0

조금 미묘하다. 대상을 제자리에서 수정하는 대신 데코레이터가 새로운 클래스를 반환하므로 새 클래스를 원본에 할당 할 수 있어야합니다. 대부분의 데코레이터는 타겟을 변경하고'void '를 반환합니다. –

+0

@JuanMendes는 머리를 위로하고 데코레이터를 추상화하는 본능은 합리적이고 합리적입니다 (DRY를 유지하고 재사용을 촉진하며 앱 수준의 패턴을 코드화하는 데 도움이됩니다). TypeScript 또는 JavaScript로 작성하는 경우에만 작동합니다. 바벨 - 플러그인 - 변형 - 데코레이터 - 유산). Angular의 AOT 언어로 작성하는 경우 데코레이터를 사용할 수 없습니다. –

관련 문제