Angular를 사용하여 image-slider 요소 빌드

게스트 작성자: 아유시 아로라

Angular 요소는 맞춤 요소로 패키징된 Angular 구성요소입니다. 현재 Chrome, Opera 및 Safari에서 지원되며 다른 브라우저에서는 폴리필을 통해 사용할 수 있습니다. Angular 요소에서는 공통 Angular 인터페이스 및 Change Detection Strategy와 함께 전체 Angular 인프라를 활용할 수 있습니다. 이러한 요소는 한 번 등록되면 브라우저 내에서 사용할 수 있습니다.

이 Codelab에서는 자체 image-slider Angular 구성요소를 만드는 과정을 안내한 다음, 이를 Angular 프레임워크 외부에서 작동할 수 있도록 Angular 요소로 변환하는 방법을 설명합니다.

빌드할 항목

이 Codelab에서는 Angular를 사용하여 image-slider 요소를 빌드해 보겠습니다. 이 요소의 특징은 다음과 같습니다.

  • 브라우저에서 HTML 요소처럼 작동합니다.
  • DOM과 통신하는 모든 프레임워크에 삽입할 수 있습니다.

과정 내용

  • image-slider 맞춤 구성요소를 만드는 방법
  • image-slider 맞춤 구성요소를 맞춤 요소로 변환하는 방법
  • 브라우저 내에서 작동하도록 구성요소를 패키징하는 방법

필요한 사항

이 Codelab에서는 Angular 요소를 중점적으로 설명합니다. 관련 없는 개념과 코드 블록은 자세히 언급되지 않으며 복사하여 붙여넣기만 하도록 제공됩니다.

코드 다운로드하기

다음 링크를 클릭하면 이 Codelab의 모든 코드를 다운로드할 수 있습니다.

소스 코드 다운로드

다운로드한 ZIP 파일의 압축을 해제합니다. 그러면 루트 폴더 (angular-element-codelab-master)의 압축이 해제됩니다.

루트 폴더에는 2개의 폴더 (image-slider)(image-slider-finished)가 들어 있습니다. 모든 코딩 작업은 image-slider라는 디렉터리에서 하게 됩니다.

프로젝트 실행

프로젝트를 실행하려면 루트 디렉터리(image-slider)에서 명령어(ng-serve)를 실행해야 합니다.

앱이 부트스트랩되면 다음이 표시됩니다.

19ffd082e2f024a5.png

이미지 슬라이더를 만드는 방법

이 이미지 슬라이더에서는 Angular 클릭 결합을 사용하여 버튼을 결합합니다. 이미지, 대체 태그, 링크 등을 포함하는 객체 배열을 만들겠습니다. 이러한 이미지를 컨테이너에서 아래로 하나씩 쌓고 클릭 시 컨테이너를 변환하게 됩니다.

image-slider 구성요소를 만든 다음 이 구성요소를 angular-element로 변환해 보겠습니다.

  • 이미지 및 제목을 위한 컨테이너
  • 데이터를 포함하는 배열
  • 데이터를 결합하기 위한 템플릿

프로젝트를 시작하는 방법에는 여러 가지가 있습니다. 이 경우 프로젝트를 최대한 단순하게 유지하고 Angular 요소에 집중하기 위해 CSS와 함께 기본 코드를 제공했습니다.

배열 및 데이터 서비스 만들기

sliderArray에는 다음이 포함된다는 사실을 기억하세요.

  • 슬라이더에서 이미지 URL의 img 키
  • 이미지 대체 항목을 제공하는 alt 태그
  • 이미지에 관한 설명을 제공하는 텍스트

이미 src/assets 디렉터리에 있는 data.json 파일은 다음과 같습니다.

sliderArray = [
 {img: 'http://bloquo.cc/img/works/1.jpg', alt: '', text: '365 Days Of weddings a year'},
 {img: 'http://bloquo.cc/img/works/2.jpg', alt: '',  text: '365 Days Of weddings a year'},
 {img: 'http://bloquo.cc/img/works/3.jpg', alt: '', text: '365 Days Of weddings a year'},
 {img: 'http://bloquo.cc/img/works/4.jpg', alt: '',  text: '365 Days Of weddings a year'},
 {img: 'http://bloquo.cc/img/works/5.jpg', alt: '', text: '365 Days Of weddings a year'}
];

서비스를 사용하여 구성요소에서 이 데이터를 가져와야 합니다. data.service.ts 파일에서 @angular/common/httphttpClient 모듈을 사용하여 getData() 메서드를 작성합니다. 그러면 이 메서드가 위에서 만든 배열에서 데이터를 가져옵니다.


import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'

const URL = '../assets/data.json';

@Injectable({
 providedIn: 'root'
})
export class DataService {

 constructor(private http: HttpClient) {}

 getData() {
   return this.http.get(URL)
 }
}

데이터 서비스에서 데이터 가져오기

구성요소 내에서 서비스를 가져와야 합니다. 그러면 observable을 구독하여 data.json에서 객체를 가져올 수 있습니다.

이를 위해 다음 3단계를 진행해야 합니다.

  • 구성요소 배열 초기화
  • getData() 함수에서 반환된 Observable 구독
  • observable을 구독한 후 데이터 유형 확인을 위한 인터페이스 Result 생성
  • 데이터를 구성요소 배열에 할당

구성요소 배열 초기화하기

객체의 배열인 slider.component.ts 내에서 구성요소 배열을 선언하고 초기화하겠습니다.

선언하려면 다음과 같이 합니다.

sliderArray: object[];

초기화하려면 다음과 같이 합니다.

constructor(private data: DataService) {
 this.sliderArray = [];
}

다음으로 생성자 내에서 서비스를 가져와 초기화해야 합니다.

constructor(private data: DataService) {}

이제 서비스를 사용하고 서비스 메서드를 호출할 준비가 되었습니다.

데이터 서비스에서 데이터 가져오기

서비스에서 데이터를 가져오기 위해 getData() 메서드를 호출하고 그 메서드에서 반환하는 observable을 구독하겠습니다. 또한 인터페이스 Result,를 생성하여 올바른 코드 유형을 가져왔는지 확인할 수 있도록 합니다.

이 작업은 ngOnInit 메서드 내에서 진행합니다.

this.data.getData().subscribe((result: Result)=>{
})

구성요소 배열에 데이터 할당하기

마지막으로 구성요소 배열에 데이터를 할당하겠습니다.

this.data.getData().subscribe((result: Result)=>{
  this.sliderArray = result.sliderArray;
})

구성요소 배열 내에서 데이터를 가져오면 이 데이터에 템플릿을 결합할 수 있습니다.

slider.component.html,에는 이미 HTML 템플릿이 있습니다. 다음 단계는 이 템플릿을 sliderArray에 결합하는 것입니다.

*ngFor 지시어를 사용하여 데이터를 템플릿에 결합하고, 마지막으로 템플릿에 변환을 추가하여 슬라이더가 작동하도록 합니다.

여기는 다음 3단계로 이루어집니다.

  • 템플릿에 sliderArray 결합
  • 슬라이더 버튼에 이벤트 결합 추가
  • ngStylengClass을 사용하여 CSS 변환 추가

구성요소에 slideArray 결합

img-container, a text-containera slider.를 포함하는 컨테이너가 있습니다.

*ngFor 지시어를 사용하여 세 컨테이너 모두의 데이터를 결합합니다.

<div class="container">
 <div class="img-container" *ngFor="let i of sliderArray; let select = index;">
   <img src="{{i.img}}" alt="{{i.alt}}" >
 </div>

 <div>
   <div class="text-container">
     <div class="page-text" *ngFor="let i of sliderArray;let select = index;">
       <h3>{{i.text}}</h3>
     </div>
   </div>
 </div>

</div>

<div class="slider">
 <div class="slide-button-parent-container" *ngFor="let i of sliderArray; let x =index">
    <div class="select-box">
     <div class="slide-button">
     </div>
    </div>
 </div>
</div>

slideArray에 이벤트 결합하기

데이터가 결합되면 Angular click binding을 사용하여 클릭 이벤트를 모든 slide-button에 결합합니다. selected(x)라는 함수를 만듭니다. 여기서 x는 배열의 색인입니다.

selected(x) {
 this.downSelected(x);
 this.selectedIndex = x;
}

downSelected(i) {
  this.transform =  100 - (i) * 50;
  this.selectedIndex = this.selectedIndex + 1;
  if(this.selectedIndex > 4) {
    this.selectedIndex = 0;
  }
}

여기서 유념할 사항은 다음과 같습니다.

  • 세부 선택된 함수는 selected 함수 클릭 시 색인에서 전달된 변환 속성의 값을 1/50로 줄입니다.
  • 이 로직은 텍스트 컨테이너를 100%, 50%, -50%, -100%로 변환하고 그 결과 네 가지 서로 다른 상태가 생성됩니다.

ngStyle 및 ngClass를 사용하여 CSS 변환 추가하기

처음에는 모든 이미지를 불투명도 0으로 설정하고 선택한 색인이 이미지 색인과 같아지면 ngClass directive를 사용하여 selected 클래스를 추가합니다. 이 selected 클래스는 이미지가 사용자에게 보이도록 이미지에 불투명도 1을 추가합니다.

<div class="img-container"  *ngFor="let i of sliderArray; let select = index;"
      [ngClass]="{'selected': select == selectedIndex}">
</div>

그런 다음 select() 함수를 사용하여 계산된 transform 값에 따라 text-container를 변환합니다.

<div [ngStyle]="{'transform': 'translateY('+ transform + '%' +')', 'transition': '.8s'}">
</div>

이 모든 단계를 진행하면 아래와 같은 최종 코드가 표시됩니다.

<div class="container">
 <div class="img-container"  *ngFor="let i of sliderArray; let select = index;"
      [ngClass]="{'selected': select == selectedIndex}">
   <img src="{{i.img}}" alt="{{i.alt}}" >
 </div>

 <!--</div>-->
 <div [ngStyle]="{'transform': 'translateY('+ transform + '%' +')', 'transition': '.8s'}">
   <div class="text-container">
     <div class="page-text" *ngFor="let i of sliderArray;let select = index;" [ngClass]="{'selected': select == selectedIndex}">
       <h3>{{i.text}}</h3>
     </div>
   </div>
 </div>

</div>

<div class="slider">
 <div class="slide-button-parent-container"  *ngFor="let i of sliderArray; let x =index" (click)="selected(x)" >
    <div class="select-box">
     <div   class="slide-button" [ngClass]="{'slide-button-select': x == selectedIndex}" >
     </div>
    </div>
 </div>
</div>

이 절차는 다음 5단계로 구성됩니다.

  • Angular 요소에 Shadow DOM 사용
  • entryComponents 활용
  • @angular/elements에서 CreateCustomElement 모듈 가져오기 및 사용
  • custom-element 정의
  • ngDoBootstrap 메서드 실행

Angular 요소에 Shadow DOM 사용하기

이제 image-slider가 실행되고 있으므로 이를 Angular Element로 만들기만 하면 됩니다.

흥미로운 사실은 구성요소 DOM 즉, Shadow DOM을 만드는 데 약간의 변경만 하면 된다는 점입니다.

ViewEncapsulation 모듈을 가져와서 ShadowDom 메서드를 사용해야 합니다.

@Component({
 selector: 'app-slider',
 templateUrl: './slider.component.html',
 styleUrls: ['./slider.component.css'],
 encapsulation: ViewEncapsulation.ShadowDom
})

entryComponent 활용

entry 구성요소는 Angular에서 필수적으로 로드되는 구성요소입니다. entry 구성요소는 NgModule에서 부트스트랩하여 지정합니다.

여기서는 @NgModule 내의 entryComponents 배열에 SliderComponent를 지정합니다.

@NgModule({
 declarations: [
   SliderComponent
 ],
 imports: [
   BrowserModule,
   HttpClientModule
 ],
})

createCustomElement 모듈 가져오기 및 사용

여기에서는 @angular/elements.createCustomElement 모듈을 사용해야 합니다. SliderComponent,createCustomElement 함수의 매개변수로 사용해야 합니다. 그런 다음 DOM에 slider를 등록해야 합니다.

import { createCustomElement } from '@angular/elements';

export class AppModule {
 constructor(private injector: Injector) {
   const slider = createCustomElement(SliderComponent, { injector });
    }
}

슬라이더를 DOM 요소로 등록하려면 customElements.define 메서드를 사용하여 슬라이더를 정의합니다.

customElements.define('motley-slider', slider);

마지막으로 ngDoBootstrap() 메서드를 사용하여 이 custom-element를 부트스트랩해야 합니다. 전체 코드는 다음과 같습니다.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { SliderComponent } from './slider/slider.component';
import { HttpClientModule} from "@angular/common/http";

@NgModule({
 declarations: [
   SliderComponent
 ],
 imports: [
   BrowserModule,
   HttpClientModule
 ],
})
export class AppModule {
 constructor(private injector: Injector) {
   const slider = createCustomElement(SliderComponent, { injector });
   customElements.define('motley-slider', slider);
 }

 ngDoBootstrap() {}

}

Angular 요소 패키징

새 명령어로 package.json을 수정해야 합니다. package.json 파일 내의 스크립트 객체를 수정합니다.

수정된 스크립트 객체를 확인해 보겠습니다.

"scripts": {
 "ng": "ng",
 "start": "ng serve",
 "build": "ng build --prod --output-hashing=none",
 "package": "cat dist/my-app/{runtime,polyfills,scripts,main}.js | gzip > elements.js.gz",
 "serve": "http-server",
 "test": "ng test",
 "lint": "ng lint",
 "e2e": "ng e2e"
}

이제 ng build & ng package 명령어를 실행할 수 있습니다. 마지막으로 ng serve를 실행하여 build 명령을 사용하여 생성된 dist/ 폴더를 제공합니다. 또한 ng package 명령어에서 얻은 gzip을 사용하고 압축을 푼 다음 npm module로 게시할 수 있습니다.