Tạo phần tử thanh trượt hình ảnh bằng Angular

1. Giới thiệu

Do tác giả khách Aayush Arora

Angular Elements là các thành phần Angular được đóng gói dưới dạng các phần tử tuỳ chỉnh. Các API này hiện được Chrome, Opera và Safari hỗ trợ, đồng thời có sẵn trong các trình duyệt khác thông qua polyfill. Các phần tử này có thể sử dụng toàn bộ Cơ sở hạ tầng Angular với Giao diện Angular chung và Chiến lược phát hiện thay đổi. Sau khi đăng ký, bạn có thể sử dụng các phần tử này trong trình duyệt.

Lớp học lập trình này sẽ hướng dẫn bạn tạo thành phần Angular của riêng thanh trượt hình ảnh, sau đó giúp bạn chuyển đổi thành phần đó thành một phần tử Angular để có thể hoạt động bên ngoài Khung Angular.

Sản phẩm bạn sẽ tạo ra

Trong lớp học lập trình này, bạn sẽ tạo một phần tử trình chiếu hình ảnh bằng Angular. Your element wi:

  • hoạt động như một phần tử HTML trong trình duyệt
  • Có thể cắm vào bất kỳ khung nào tương tác với DOM.

Kiến thức bạn sẽ học được

  • Cách tạo thành phần tuỳ chỉnh cho trình chiếu hình ảnh
  • Cách chuyển đổi thành phần tuỳ chỉnh image-slider thành phần tử tuỳ chỉnh
  • Cách đóng gói thành phần để thành phần hoạt động trong trình duyệt

Bạn cần có

  • Một phiên bản gần đây của angular-cli.
  • Mã mẫu
  • Trình chỉnh sửa văn bản
  • Kiến thức cơ bản về Thành phần Angular

Lớp học lập trình này tập trung vào Angular Elements. Các khái niệm và khối mã không liên quan được tinh chỉnh và cung cấp cho bạn, chỉ cần sao chép và dán.

2. Chuẩn bị

Tải mã nguồn xuống

Nhấp vào đường liên kết sau đây để tải toàn bộ mã nguồn cho lớp học lập trình này:

Giải nén tệp zip đã tải xuống. Thao tác này sẽ giải nén một thư mục gốc (angular-element-codelab-master) chứa

hai thư mục (image-slider)(image-slider-finished). Chúng ta sẽ thực hiện tất cả công việc lập trình trong một thư mục có tên là image-slider.

Chạy dự án

Để chạy dự án, bạn cần chạy lệnh ( ng-serve ) từ thư mục gốc ( image-slider ).

Sau khi ứng dụng được khởi động, bạn sẽ thấy:

19ffd082e2f024a5.png

3. Bạn muốn tạo một thành phần tuỳ chỉnh Image-Slider (Thanh trượt hình ảnh)?

Làm cách nào để tạo một thanh trượt hình ảnh?

Đối với trình chiếu hình ảnh này, hãy liên kết các nút bằng cách sử dụng tính năng liên kết lượt nhấp của Angular. Chúng ta sẽ tạo một mảng các đối tượng chứa hình ảnh, thẻ alt, đường liên kết, v.v. Chúng ta sẽ đặt các hình ảnh này bên dưới nhau trong một vùng chứa và dịch vùng chứa khi nhấp vào.

Chúng ta sẽ tạo một thành phần thanh trượt hình ảnh rồi chuyển đổi thành phần đó thành angular-element.

  • Vùng chứa hình ảnh và tiêu đề.
  • Một mảng chứa dữ liệu
  • Mẫu để liên kết dữ liệu

4. Triển khai thành phần thanh trượt hình ảnh

Có nhiều cách để bắt đầu với bất kỳ dự án nào. Trong trường hợp này, để dự án của chúng ta đơn giản nhất có thể và tập trung vào Angular Elements, chúng tôi đã cung cấp cho bạn mã cơ bản cùng với css.

Tạo mảng và dịch vụ dữ liệu

Hãy nhớ rằng sliderArray sẽ chứa:

  • Khoá img cho URL hình ảnh trong thanh trượt
  • Thẻ alt để cung cấp văn bản thay thế cho hình ảnh
  • Văn bản mô tả về hình ảnh

Tệp data.json đã có trong thư mục src/assets sẽ có dạng như sau.

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'}
];

Chúng ta cần tìm nạp dữ liệu này trong thành phần bằng cách sử dụng một dịch vụ. Trong tệp data.service.ts, chúng ta sẽ viết một phương thức getData() bằng cách sử dụng mô-đun httpClient từ @angular/common/http. Phương thức này sẽ tìm nạp dữ liệu từ mảng mà chúng ta đã tạo ở trên.

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

Tìm nạp dữ liệu từ dịch vụ dữ liệu

Chúng ta cần nhập dịch vụ vào trong thành phần, sau đó có thể đăng ký theo dõi đối tượng có thể quan sát để lấy đối tượng từ data.json

Chúng ta cần thực hiện 3 bước cho việc này:

  • Khởi tạo một mảng thành phần
  • Đăng ký đối tượng có thể quan sát do hàm getData() trả về
  • Tạo một giao diện Result để kiểm tra loại dữ liệu sau khi đăng ký observable.
  • Chỉ định dữ liệu cho mảng thành phần.

Khởi chạy mảng thành phần

Chúng ta sẽ khai báo và khởi tạo mảng thành phần bên trong slider.component.ts. Đây là một mảng gồm các đối tượng:

Cách khai báo:

sliderArray: object[];

Cách khởi chạy:

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

Tiếp theo, chúng ta cần nhập và khởi động dịch vụ trong hàm khởi tạo

constructor(private data: DataService) {}

Giờ đây, chúng ta đã sẵn sàng sử dụng dịch vụ và gọi các phương thức dịch vụ.

Lấy dữ liệu từ Dịch vụ dữ liệu

Để lấy dữ liệu từ dịch vụ, chúng ta sẽ gọi phương thức getData() và đăng ký với đối tượng có thể quan sát mà phương thức này sẽ trả về. Chúng ta cũng sẽ tạo một giao diện Result, để có thể kiểm tra loại mà chúng ta đang nhận được dữ liệu chính xác.

Chúng ta sẽ thực hiện việc này trong phương thức ngOnInit:

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

Chỉ định dữ liệu cho Mảng thành phần

Cuối cùng, chúng ta sẽ chỉ định dữ liệu cho mảng thành phần:

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

Sau khi nhận được dữ liệu trong mảng của thành phần, chúng ta có thể liên kết mẫu với dữ liệu này.

Trong slider.component.html,, chúng ta đã có một mẫu HTML. Bước tiếp theo là liên kết mẫu này với sliderArray.

5. Liên kết dữ liệu với mẫu

Chúng ta sẽ liên kết dữ liệu với mẫu bằng cách sử dụng Chỉ thị *ngFor và cuối cùng, chúng ta sẽ thêm các phép biến đổi vào mẫu để thanh trượt hoạt động.

Quy trình này gồm 3 bước:

  • Liên kết sliderArray với mẫu
  • Thêm tính năng Liên kết sự kiện cho các nút thanh trượt
  • Thêm các phép biến đổi CSS bằng cách sử dụng ngStylengClass

Liên kết slideArray với Thành phần

Chúng ta có một vùng chứa chứa img-container, a text-containera slider.

Chúng ta sẽ liên kết dữ liệu trong cả 3 vùng chứa bằng chỉ thị *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>

Liên kết sự kiện với slideArray

Sau khi dữ liệu được liên kết, chúng ta sẽ liên kết sự kiện nhấp chuột với mọi nút trượt bằng cách sử dụng click binding. Chúng ta sẽ tạo một hàm có tên là selected(x), trong đó x là chỉ mục của mảng.

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

Những điểm cần nhớ:

  • Hàm được chọn giảm giá trị của thuộc tính biến đổi 50 lần chỉ mục được truyền khi nhấp vào hàm selected.
  • Logic này dịch vùng chứa văn bản thành 100%, 50%, -50%, -100%, dẫn đến 4 trạng thái khác nhau.

Thêm các phép biến đổi CSS bằng ngStyle và ngClass

Ban đầu, chúng ta đặt tất cả hình ảnh ở độ mờ bằng 0, chúng ta thêm một lớp selected bằng cách sử dụng ngClass directive khi chỉ mục đã chọn bằng với chỉ mục hình ảnh. Lớp selected này thêm độ mờ bằng 1 vào hình ảnh, giúp người dùng nhìn thấy hình ảnh.

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

Sau đó, chúng ta sẽ dịch vùng chứa văn bản theo giá trị transform được tính bằng hàm select().

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

Sau khi thực hiện tất cả các bước này, bạn có thể tìm thấy mã cuối cùng như được cung cấp bên dưới:

<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. Chuyển đổi thành phần thành phần tử góc

Quy trình này bao gồm 5 bước:

  • Sử dụng Shadow DOM cho phần tử Angular
  • Tận dụng entryComponents
  • Nhập và sử dụng mô-đun CreateCustomElement từ @angular/elements
  • Xác định custom-element của chúng ta
  • Phương thức ngDoBootstrap đang chạy

Sử dụng DOM bóng cho phần tử góc

Giờ đây, chúng ta đã có trình chiếu hình ảnh đang chạy, chúng ta chỉ cần biến trình chiếu này thành một Angular Element.

Điều thú vị là chỉ có một thay đổi nhỏ để tạo DOM thành phần, một DOM tối.

Chúng ta cần nhập mô-đun ViewEncapsulation và phải sử dụng phương thức ShadowDom từ mô-đun đó.

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

Sử dụng entryComponents

Thành phần nhập là thành phần mà angular tải một cách bắt buộc. Bạn chỉ định một thành phần nhập bằng cách khởi động thành phần đó trong NgModule.

Tại đây, chúng ta sẽ chỉ định SliderComponent trong mảng entryComponents bên trong @NgModule

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

Nhập và sử dụng mô-đun createCustomElement

Ở đây, chúng ta cần sử dụng Mô-đun createCustomElement từ @angular/elements. Bạn cần sử dụng SliderComponent, làm tham số cho hàm createCustomElement. Sau đó, chúng ta cần đăng ký slider trong DOM.

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

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

Để đăng ký thanh trượt dưới dạng một phần tử DOM, chúng ta sẽ xác định thanh trượt đó bằng phương thức customElements.define.

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

Cuối cùng, chúng ta phải khởi động phần tử tuỳ chỉnh này bằng phương thức ngDoBootstrap(). Mã hoàn chỉnh sẽ có dạng như sau:

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() {}

}

Đóng gói Phần tử Angular

Chúng ta cần sửa đổi package.json bằng các lệnh mới, chúng ta sẽ sửa đổi đối tượng tập lệnh trong tệp package.json.

Hãy kiểm tra đối tượng tập lệnh đã sửa đổi:

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

Bây giờ, chúng ta có thể chạy lệnh ng build & ng package và cuối cùng, chúng ta sẽ chạy ng serve để phân phát thư mục dist/ được tạo bằng lệnh build. Ngoài ra, chúng ta có thể sử dụng gzip thu được từ lệnh ng package, trích xuất và xuất bản dưới dạng npm module.