0.0.27 • Published 3 years ago

@jj-viewer/skinnable v0.0.27

Weekly downloads
4
License
MIT
Repository
github
Last release
3 years ago

@jj-viewer/skinnable

This library was generated with Angular CLI version 9.1.0.

Features

  • Angular 프로젝트에서 다음 사항을 컴파일 이후에도 동적으로 로드하여 설정할 수 있도록 한다.

    • HTML (template)
    • Css (style)
    • Json (Component의 속성 설정)
  • 컴포넌트간 API를 호출할 수 있는 Mediator를 제공한다.

(주의) Jit 컴파일 일때만 정상 동작합니다.

  • AOT 컴파일을 지원되지 않습니다. (--aot=false)
  • <router-outlet></router-outlet> 구문은 테스트되지 않았습니다.

Installation

npm install @jj-viewer/skinnable --save

Typescript 환경 설정

tsconfig.json

{
  "compilerOptions": {
    ...
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "useDefineForClassFields": true, // 생성자에서 초기화하지 않은 member도 Object.keys 목록에 추가함
    }
  },
  
  "angularCompilerOptions": {
    ...
    "enableIvy": false
  },
  ...
}

proxy 설정

개발환경에서 런타임에 사용할 bundle 폴더에 접근하기 위해 proxy 서버를 설정합니다.
bundle 폴더에는 skin(template, css) 파일 및 이미지등의 asset 파일을 넣어둡니다.

workspace (root) 위치가 다음 주소로 서비스된다고 가정할때

http://localhost/dev/workspace
  1. proxy.conf.json 파일 생성

    {
      "/bundle/*": {
        "target": "http://localhost/dev/workspace",
        "secure": false,
        "logLevel": "debug"
      }
    }
  2. angular.json 파일 수정

    "serve": {
      "options": {
        "proxyConfig": "proxy.conf.json",
      },
    }

    또는 실행시 옵션 설정

    ng serve --proxy-config=proxy.conf.json
  3. ng serve 실행 http://localhost/dev/workspace/bundle 서비스를 http://localhost:4200/bundle로 접근 가능해짐.

Usage

testApp 프로젝트에서 보다 자세한 예시를 참고하실 수 있습니다.

동적으로 Skin (template, css) 이 설정되는 과정

  1. 설정 파일을 정의합니다. (bundle 폴더)
  2. SkinApplicationComponent에서 설정 파일이 로드됩니다. (json 파일)
  3. 설정 파일 내용이 각 SkinComponent에 동적으로 적용됩니다. (template, css)

AppModule 설정

@NgModule({

    // 중요: Skinnable Component는 AppModule에 export 되어 있어야 함
    // AppModule에 export 되어있는 module은 추가할 필요 없음
    providers: [
        {provide: SKIN_INJECT_MODULES, useValue: [AppModule]}
    ],

    schemas: [
        NO_ERRORS_SCHEMA,
        CUSTOM_ELEMENTS_SCHEMA
    ],

    imports: [
        ...imports,
        SkinnableModule
    ],

    exports: [
        ...exports
    ],
    ...
})
export class AppModule {}

SKIN_INJECT_MODULES에 값 지정

컴파일된 이후에 사용되는 (다른 모듈에서 정의된) 컴포넌트를 사용하려면

  1. 컴포넌트는 AppModule에서 export 되어 있어야 하고,
  2. exporte된 컴포넌트를 런타임에 사용하는 SkinnableModuleimport 되어야 하므로,
  3. 필요한 module을 배열로 지정하는 것 보다는 모든 기능(요소)을 포함하는 AppModule을 대표로 import 시킵니다..
  4. AppModule 대신 필요한 모듈만 import 시켜도 됩니다.
    {provide: SKIN_INJECT_MODULES, useValue: [...modules]}

(주의) AOT 컴파일을 지원되지 않습니다. (--aot=false) SkinnableModule에 런타임에 import되는 Module을 동적으로 인식할 수없습니다.

Component에 적용

SkinComponent를 상속 받아 사용합니다.

import {
    SkinApplicationComponent,
    SkinComponent,
    SKINNABLE_TEMPLATE,
    SkinnableParameter,
    skinnableParameterProvider
} from '@jj-viewer/skinnable';

@Component({
    selector: 'jj-sample',
    providers: [skinnableParameterProvider],

    // 설정파일 또는 Attribute에 설정된 경로의 템플릿이 동적 로딩됨
    template: SKINNABLE_TEMPLATE

    // 아래 설정도 여전히 사용 가능함 (이곳 템플릿은 컴파일 됨)
    // templateUrl: 'sample.html',
    // styleUrls: ['sample.css'],
})
export class SampleComponent extends SkinComponent {
    constructor(protected param: SkinnableParameter, protected viewContainerRef: ViewContainerRef) {
        super(param, viewContainerRef);
    }
}

Application에 적용

SkinApplicationComponent를 상속 받아 사용합니다.

import {
    SkinApplicationComponent,
    SKINNABLE_TEMPLATE,
    SkinnableParameter,
    skinnableParameterProvider
} from '@jj-viewer/skinnable';

@Component({
    selector: 'jj-application',
    providers: [skinnableParameterProvider],
    template: SKINNABLE_TEMPLATE
})
export class ApplicationComponent extends SkinApplicationComponent {
    constructor(protected param: SkinnableParameter, protected viewContainerRef: ViewContainerRef) {
        super(param, viewContainerRef);
    }
}

컴포넌트간 통신

testApp 프로젝트에서 SampleComponent에서 발송한 이벤트를 DefineComponent에서 받아 처리하는 예제 입니다.

컴포넌트간 서로 통신을 하려면 반드시 id가 (attribute or class value) 설정되어 있어야 합니다.

  • SampleComponent에서 이벤트 발송
// SampleComponent
bookLoadedEvent = new EventEmitter<any>();

// @오버라이딩
// 모든 컴포넌트 설정이 완료된 applicationCompleteEvent 발생 후 emit 한다.
// 오버라이딩해서 사용하도록 applicationCompleteEvent 청취가 구현되어 있다.
protected onApplicationComplete() {

    setTimeout(() => {
        // 예 : bookLoaded 생성 완료를 알림
        this.bookLoadedEvent.emit(this.id);
    }, 1000);

}
  • template에 id 설정
<jj-sample id="3.app_skin_sample" ...>
  • DefineComponent에서 이벤트 청취
// @오버라이딩
// 모든 컴포넌트 설정이 완료된 applicationCompleteEvent 발생 후 청취 한다.
protected onApplicationComplete() {

    // Mediator를 통해 컴포넌트 instance에 접근
    const book = this.param.mediatorService.get('3.app_skin_sample') as SampleComponent;

    // 테스트 : bookLoaded 생성 완료 청취
    const subscription = book.bookLoadedEvent.subscribe((id: string) => {
        console.error('bookLoadedEvent : ', book.nodeName, id);
        subscription.unsubscribe();

        // book API 사용
        // book.goPage(10);
    });

}

설정 파일 정의

컴포넌트에 설정값을 전달하는 방법은 다음과 같습니다.
다음 순서대로 (우선순위) 설정값이 적용됩니다.

  1. Attribute 설정값이 있으면 우선적으로 적용됩니다.
  2. 설정 파일에서 [태그(selector)] 항목에서 정의된 설정값
  3. 설정 파일에서 [global] 항목에서 정의된 설정값

설정 파일에 대해 설명합니다.

{
  "global": {
    /*
    컴포넌트들의 공통 속성중 기본값으로 설정할 값을 전역 설정합니다.
    이곳에서는 properties, skins 외 다른 속성은 정의할 수 없습니다.
    새로운 속성을 추가하려면 properties 항목을 이용합니다.
    */
    
    // 이곳에 속성을 추가해도 컴포넌트에 전달되지 않습니다.
    "can't add propertise": "전달되지 않는 속성값",

    "properties": {
      "can add propertise": "추가할 수 있는 속성 영역",
      // 예) a, b 속성을 모든 컴포넌트에 전달 
      "a": 1, "b": 2
    },

    "skins": {
      "useLoader": true,
      "urlTemplate": "",
      "urlCss": ""
    }
  },

  // 'jj-application' selector를 가진 컴포넌트에 전달되는 속성 설정
  "jj-application": {
    "properties": {
      // 예) jj-application 컴포넌트에 global 설정값까지 함께 전달됨 
      // "a": 1, "b": 10, "c": 20
      "b": 10, "c": 20
    },
    "skins": {
      "useLoader": false,
      "urlTemplate": "bundle/skins/application.html",
      "urlCss": "bundle/skins/application.css"
    }
  },

  // 'jj-sample' selector를 가진 컴포넌트에 전달되는 속성 설정
  "jj-sample": {
    // attribute에 값이 지정된 경우 값 지정 생략해도 됨.
    "skins": {}
  }
  ...
}

Skin 파일 지정 방법

  • url-configuration에 지정된 설정파일에 정의
  • attribute에서 직접 지정하는 방법

Template (HTML) 작성 방법

attribute에 다음 속성을 활용합니다.

  • url-configuration
    • 설정파일 경로를 지정 합니다.
    • SkinApplicationComponent에만 설정할 수 있습니다.
  • url-template
    • 동적 로드되는 템플릿(HTML) 파일 경로입니다.
    • 파일 내부에 @import url() 구문 사용할 수 없습니다.
  • url-css
    • 동적 로드되는 CSS 파일 경로입니다.
  • use-loader
    • 파일 로드 방식을 설정
    • (false) angular 로더를 사용하여 파일 내용을 로드
    • (true) 새로 작성된 http 로더로 파일 내용을 로드
    • true 이면 같은 url의 파일은 요청을 한번만 호출함 (cache 사용과는 다름)

Attribute 속성으로 사용

<!--문자열은 "'"로 감싸야 함.-->
<jj-book [use-loader]="false"
         [url-template]="'bundle/skins/book.component.html'"
         [url-css]="'bundle/skins/book.component.css'">

Attribute 값으로 사용

<!--type을 유지하려면 속성 바인딩 구문 사용해야 함.-->
<!--그렇지 않으면 아래 use-loader 속성은 문자열 "false"로 인식됨.-->
<jj-sample use-loader="false"
         url-template="bundle/skins/book.component.html"
         url-css="bundle/skins/book.component.css">

Attribute 바인딩 사용

<!--interpoliate 구문 사용-->
<jj-application url-configuration="{{urlConfiguration}}">

경로 설정

Template, Css에 사용되는 URL은 publishing 후 index.html을 기준으로한 상대경로로 작성해야 합니다.

  • Template 내부에서 경로 지정

    <jj-sample urlTemplate="./bundle/skins/sample.html"
               urlCss="./bundle/skins/sample.css">
    </jj-sample>
  • Css 내부에서 경로 지정

    @import url("./bundle/skins/test_import.css");
    /* styleUrl로 지정된 경우 파일 내부에 @import url() 구문 사용 불가함. */

디렉티브로 기능을 구현하지 않은 이유

  • Directive Attribute으로 로드 기능을 구현하는 경우 클래스 상속을 활용할 수없다.
  • 기능이 필요한 모든 노드에 직접 디렉티브를 명시해 주어야 한다.
    (설정값까지 attribute에 작성하면 작업이 너무 번거로워 진다.)
0.0.27

3 years ago

0.0.26

4 years ago

0.0.24

4 years ago

0.0.25

4 years ago

0.0.20

4 years ago

0.0.21

4 years ago

0.0.22

4 years ago

0.0.23

4 years ago

0.0.19

4 years ago

0.0.17

4 years ago