@jj-viewer/skinnable v0.0.27
@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
proxy.conf.json
파일 생성{ "/bundle/*": { "target": "http://localhost/dev/workspace", "secure": false, "logLevel": "debug" } }
angular.json
파일 수정"serve": { "options": { "proxyConfig": "proxy.conf.json", }, }
또는 실행시 옵션 설정
ng serve --proxy-config=proxy.conf.json
ng serve
실행http://localhost/dev/workspace/bundle
서비스를http://localhost:4200/bundle
로 접근 가능해짐.
Usage
testApp
프로젝트에서 보다 자세한 예시를 참고하실 수 있습니다.
동적으로 Skin (template, css) 이 설정되는 과정
- 설정 파일을 정의합니다. (
bundle
폴더) SkinApplicationComponent
에서 설정 파일이 로드됩니다. (json 파일)- 설정 파일 내용이 각
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
에 값 지정
컴파일된 이후에 사용되는 (다른 모듈에서 정의된) 컴포넌트를 사용하려면
- 컴포넌트는
AppModule
에서export
되어 있어야 하고, exporte
된 컴포넌트를 런타임에 사용하는SkinnableModule
에import
되어야 하므로,- 필요한 module을 배열로 지정하는 것 보다는 모든 기능(요소)을 포함하는
AppModule
을 대표로import
시킵니다.. 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);
});
}
설정 파일 정의
컴포넌트에 설정값을 전달하는 방법은 다음과 같습니다.
다음 순서대로 (우선순위) 설정값이 적용됩니다.
- Attribute 설정값이 있으면 우선적으로 적용됩니다.
- 설정 파일에서
[태그(selector)]
항목에서 정의된 설정값 - 설정 파일에서
[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에 작성하면 작업이 너무 번거로워 진다.)