สร้างองค์ประกอบแถบเลื่อนรูปภาพโดยใช้ Angular

1. บทนำ

โดยผู้เขียนรับเชิญ Aayush Arora

Angular Elements คือคอมโพเนนต์ Angular ที่จัดแพ็กเกจเป็นองค์ประกอบที่กำหนดเอง ปัจจุบัน Chrome, Opera และ Safari รองรับการใช้งานฟีเจอร์นี้ และเบราว์เซอร์อื่นๆ จะใช้งานได้ผ่าน Polyfill องค์ประกอบเหล่านี้สามารถใช้โครงสร้างพื้นฐานทั้งหมดของ Angular ด้วยอินเทอร์เฟซ Angular ทั่วไปและกลยุทธ์การตรวจหาการเปลี่ยนแปลง เมื่อลงทะเบียนแล้ว คุณจะใช้องค์ประกอบเหล่านี้ภายในเบราว์เซอร์ได้

Codelab นี้จะแนะนำวิธีสร้างคอมโพเนนต์ Angular แถบเลื่อนรูปภาพของคุณเอง จากนั้นจะช่วยคุณเปลี่ยนให้เป็นองค์ประกอบ Angular เพื่อให้ทำงานนอกเฟรมเวิร์ก Angular ได้

สิ่งที่คุณจะสร้าง

ใน Codelab นี้ คุณจะได้สร้างองค์ประกอบแถบเลื่อนรูปภาพโดยใช้ Angular องค์ประกอบของคุณ wi:

  • ทำงานเหมือนองค์ประกอบ HTML ในเบราว์เซอร์
  • เสียบเข้ากับเฟรมเวิร์กใดก็ได้ที่สื่อสารกับ DOM

สิ่งที่คุณจะได้เรียนรู้

  • วิธีสร้างคอมโพเนนต์ที่กำหนดเองของแถบเลื่อนรูปภาพ
  • วิธีเปลี่ยนคอมโพเนนต์ที่กำหนดเองของแถบเลื่อนรูปภาพเป็นองค์ประกอบที่กำหนดเอง
  • วิธีแพ็กเกจคอมโพเนนต์เพื่อให้ทำงานภายในเบราว์เซอร์ได้

สิ่งที่ต้องมี

  • angular-cli เวอร์ชันล่าสุด
  • โค้ดตัวอย่าง
  • โปรแกรมแก้ไขข้อความ
  • ความรู้พื้นฐานเกี่ยวกับคอมโพเนนต์ Angular

Codelab นี้มุ่งเน้นที่ Angular Elements เราจะข้ามแนวคิดและบล็อกโค้ดที่ไม่เกี่ยวข้องไป และจะให้คุณคัดลอกและวางได้ง่ายๆ

2. การเริ่มตั้งค่า

ดาวน์โหลดโค้ด

คลิกลิงก์ต่อไปนี้เพื่อดาวน์โหลดโค้ดทั้งหมดสำหรับ Codelab นี้

คลายไฟล์ ZIP ที่ดาวน์โหลด การดำเนินการนี้จะแตกโฟลเดอร์รูท (angular-element-codelab-master) ซึ่งมีรายการต่อไปนี้

โฟลเดอร์ 2 โฟลเดอร์ ได้แก่ (image-slider) และ (image-slider-finished) เราจะทำงานด้านการเขียนโค้ดทั้งหมดในไดเรกทอรีที่ชื่อว่า image-slider

การดำเนินโครงการ

หากต้องการเรียกใช้โปรเจ็กต์ คุณต้องเรียกใช้คำสั่ง ( ng-serve) จากไดเรกทอรีราก ( image-slider)

เมื่อแอปเริ่มต้นแล้ว คุณจะเห็นสิ่งต่อไปนี้

19ffd082e2f024a5.png

3. การสร้างคอมโพเนนต์ที่กำหนดเองของ Image-Slider

วิธีสร้างแถบเลื่อนรูปภาพ

สำหรับแถบเลื่อนรูปภาพนี้ ให้เชื่อมโยงปุ่มโดยใช้การเชื่อมโยงการคลิกเชิงมุม เราจะสร้างอาร์เรย์ของออบเจ็กต์ที่มีรูปภาพ แท็ก Alt ลิงก์ ฯลฯ เราจะวางรูปภาพเหล่านี้ต่อกันในคอนเทนเนอร์และแปลคอนเทนเนอร์เมื่อคลิก

เราจะสร้างคอมโพเนนต์แถบเลื่อนรูปภาพ แล้วแปลงเป็น Angular Element

  • คอนเทนเนอร์สำหรับรูปภาพและชื่อ
  • อาร์เรย์ที่มีข้อมูล
  • เทมเพลตเพื่อเชื่อมโยงข้อมูล

4. ใช้คอมโพเนนต์แถบเลื่อนรูปภาพ

คุณเริ่มโปรเจ็กต์ได้หลายวิธี ในกรณีนี้ เราได้ให้โค้ดพื้นฐานพร้อมกับ CSS เพื่อให้โปรเจ็กต์ของเราเรียบง่ายที่สุดและมุ่งเน้นไปที่ Angular Elements

การสร้างอาร์เรย์และบริการข้อมูล

อย่าลืมว่า sliderArray จะมีข้อมูลต่อไปนี้

  • คีย์ img สำหรับ URL ของรูปภาพในแถบเลื่อน
  • แท็ก alt เพื่อระบุข้อความแสดงแทนสำหรับรูปภาพ
  • ข้อความที่ใช้ระบุคำอธิบายเกี่ยวกับรูปภาพ

ไฟล์ data.json ที่อยู่ในไดเรกทอรี src/assets อยู่แล้วควรมีลักษณะดังนี้

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 เราจะเขียนเมธอด getData() โดยใช้โมดูล httpClient จาก @angular/common/http ซึ่งจะดึงข้อมูลจากอาร์เรย์ที่เราสร้างไว้ข้างต้น

 
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 ขั้นตอนต่อไปนี้

  • การเริ่มต้นอาร์เรย์คอมโพเนนต์
  • การติดตาม Observable ที่ฟังก์ชัน getData() แสดงผล
  • สร้างอินเทอร์เฟซ Result สำหรับการตรวจสอบประเภทของข้อมูลหลังจากสมัครใช้บริการ Observable
  • กำหนดข้อมูลให้กับอาร์เรย์คอมโพเนนต์

กำลังเริ่มต้นอาร์เรย์คอมโพเนนต์

เราจะประกาศและเริ่มต้นอาร์เรย์คอมโพเนนต์ภายใน 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

5. การเชื่อมโยงข้อมูลกับเทมเพลต

เราจะเชื่อมโยงข้อมูลกับเทมเพลตโดยใช้*ngFor Directive และสุดท้ายจะเพิ่มการเปลี่ยนรูปแบบในเทมเพลตเพื่อให้แถบเลื่อนทำงานได้

ซึ่งมี 3 ขั้นตอน ได้แก่

  • การเชื่อมโยง sliderArray กับเทมเพลต
  • การเพิ่มการเชื่อมโยงเหตุการณ์สำหรับปุ่มแถบเลื่อน
  • การเพิ่มการเปลี่ยนรูปแบบ CSS โดยใช้ ngStyle และ ngClass

การเชื่อมโยง slideArray กับคอมโพเนนต์

เรามีคอนเทนเนอร์ที่มี img-container, a text-container และ a slider.

เราจะเชื่อมโยงข้อมูลในคอนเทนเนอร์ทั้ง 3 รายการโดยใช้คำสั่ง *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

เมื่อผูกข้อมูลแล้ว เราจะผูกเหตุการณ์คลิกกับปุ่มสไลด์ทุกปุ่มโดยใช้ click binding ของ Angular เราจะสร้างฟังก์ชันที่ชื่อ 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;
  }
}

ข้อควรทราบมีดังนี้

  • ฟังก์ชันที่เลือกจะลดค่าของพร็อพเพอร์ตี้การเปลี่ยนรูป 50 เท่าของดัชนีที่ส่งผ่านเมื่อคลิกฟังก์ชัน selected
  • ตรรกะนี้จะแปลคอนเทนเนอร์ข้อความเป็น 100%, 50%, -50%, -100% ซึ่งส่งผลให้เกิดสถานะที่แตกต่างกัน 4 สถานะ

การเพิ่มการเปลี่ยนรูปแบบ CSS โดยใช้ ngStyle และ ngClass

ในตอนแรก เราตั้งค่ารูปภาพทั้งหมดให้มีความทึบแสงเป็น 0 จากนั้นเพิ่มคลาส selected โดยใช้ ngClass directive เมื่อดัชนีที่เลือกเท่ากับดัชนีรูปภาพ selected คลาสนี้จะเพิ่มความทึบแสงเป็น 1 ให้กับรูปภาพเพื่อให้ผู้ใช้เห็นรูปภาพ

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

หลังจากนี้ เราจะแปลคอนเทนเนอร์ข้อความตามค่า transform ที่คำนวณโดยใช้ฟังก์ชัน select()

<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>

6. การแปลงคอมโพเนนต์เป็นองค์ประกอบ Angular

กระบวนการนี้มี 5 ขั้นตอนดังนี้

  • การใช้ Shadow DOM สำหรับ Angular Element
  • การใช้ประโยชน์จาก entryComponents
  • การนำเข้าและใช้โมดูล CreateCustomElement จาก @angular/elements
  • การกำหนด custom-element
  • วิธีการngDoBootstrapเรียกใช้

การใช้ Shadow DOM สำหรับองค์ประกอบ Angular

ตอนนี้เรามีแถบเลื่อนรูปภาพที่ทำงานอยู่แล้ว สิ่งที่เราต้องทำก็คือเปลี่ยนให้เป็น Angular Element

ส่วนที่น่าสนใจคือการเปลี่ยนแปลงเล็กๆ น้อยๆ ในการสร้าง DOM ของคอมโพเนนต์ ซึ่งก็คือ Shadow DOM

เราต้องนำเข้าโมดูล ViewEncapsulation และต้องใช้วิธี ShadowDom จากโมดูลดังกล่าว

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

การใช้ประโยชน์จาก entryComponents

Entry Component คือคอมโพเนนต์ที่ Angular โหลดโดยใช้คำสั่ง คุณระบุคอมโพเนนต์รายการโดยการเริ่มต้นใน NgModule

ในที่นี้ เราจะระบุ SliderComponent ในอาร์เรย์ entryComponents ภายใน @NgModule

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

การนำเข้าและใช้โมดูล createCustomElement

ในที่นี้ เราต้องใช้โมดูล createCustomElement จาก @angular/elements. คุณต้องใช้ SliderComponent, เป็นพารามิเตอร์ของฟังก์ชัน createCustomElement หลังจากนั้น เราต้องลงทะเบียน slider ใน DOM

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() โค้ดที่สมบูรณ์จะมีลักษณะดังนี้

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 Element

เราต้องแก้ไข 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 เพื่อแสดงโฟลเดอร์ dist/ ที่สร้างขึ้นโดยใช้คำสั่ง build นอกจากนี้ เรายังใช้ gzip ที่ได้จากคำสั่ง ng package แยกข้อมูล และเผยแพร่เป็น npm module ได้ด้วย