Criar um elemento de controle deslizante de imagem usando o Angular

Escrito pelo autor convidado Aayush Arora

Os elementos Angular (link em inglês) são componentes do Angular empacotados como elementos personalizados. Atualmente, eles são compatíveis com o Chrome, Opera e Safari e estão disponíveis em outros navegadores pelo uso de polyfills. Esses elementos podem usar toda a infraestrutura do Angular com uma interface Angular comum e uma estratégia de detecção de mudanças. Depois de registrados, esses elementos podem ser usados no navegador.

Este codelab orientará a criação do seu próprio componente Angular de controle deslizante de imagem, além de ajudar você a transformá-lo em um elemento Angular para que ele funcione fora do framework Angular.

O que você criará

Neste codelab, você criará um elemento de controle deslizante de imagem usando o Angular. Seu elemento:

  • funcionará como um elemento HTML no navegador;
  • conseguirá conectar qualquer framework que se comunique com o DOM.

O que você aprenderá

  • Como criar um componente personalizado de controle deslizante de imagem
  • Como transformar o componente personalizado de controle deslizante de imagem em um elemento personalizado
  • Como empacotar o componente para que ele funcione no navegador

O que é necessário

  • Uma versão recente do angular-cli (link em inglês).
  • O exemplo de código (link em inglês)
  • Um editor de texto
  • Conhecimentos básicos sobre os componentes do Angular

O foco deste codelab são os elementos Angular Conceitos e blocos de códigos sem relevância serão resumidos e apresentados para que você apenas os copie e cole.

Fazer o download do código

Clique no link abaixo para fazer o download de todo o código para este codelab:

Download do código-fonte

Descompacte o arquivo ZIP transferido por download. Isso descompactará uma pasta raiz (angular-element-codelab-master), que contém

duas pastas: (image-slider) e (image-slider-finished). Faremos todo o nosso trabalho de codificação em um diretório denominado image-slider (controle deslizante de imagem).

Como executar o projeto

Para executar o projeto, execute o comando ng-serve pelo diretório raiz image-slider.

Depois que o app for inicializado, você verá esta janela:

19ffd082e2f024a5.png

Como criar um controle deslizante de imagem?

Para esse controle deslizante de imagem, você associará botões usando a vinculação de clique do Angular. Criaremos uma matriz de objetos contendo imagens, tags Alt, links etc. Colocaremos essas imagens uma abaixo da outra em um contêiner e o transformaremos quando houver um clique.

Criaremos um componente de controle deslizante de imagem e depois o transformaremos em um elemento Angular.

  • Contêiner para imagens e títulos
  • Uma matriz contendo os dados
  • Modelo para vincular os dados

Há diversas maneiras de começar um projeto. Neste caso, para manter nosso projeto o mais simples possível e para nos concentrarmos nos elementos Angular, fornecemos um código básico com CSS.

Como criar uma matriz e um serviço de dados

Lembre-se de que a matriz sliderArray terá:

  • uma chave img para o URL da imagem no controle deslizante;
  • uma tag Alt para fornecer um texto alternativo para a imagem;
  • um texto para descrever a imagem.

O arquivo data.json que já está no seu diretório src/assets terá esta aparência:

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

Precisamos buscar esses dados no nosso componente usando um serviço. No arquivo data.service.ts, criaremos um método getData() usando o módulo httpClient de @angular/common/http, que buscará os dados da matriz criada acima.


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

Como buscar os dados no serviço de dados

Precisamos importar nosso serviço para dentro do componente e, em seguida, fazer a inscrição no observável para receber o objeto de data.json

Para isso, são necessárias três etapas:

  • Inicializar uma matriz de componente
  • Inscrever-se na classe observável retornada pela função getData()
  • Criar uma interface Result para conferir os tipos dos dados após a inscrição no observável
  • Atribuir os dados à matriz de componente

Como inicializar a matriz de componente

Vamos declarar e inicializar a matriz de componente dentro de slider.component.ts, que é uma matriz de objetos:

Para declarar:

sliderArray: object[];

Para inicializar:

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

Depois, precisamos importar e inicializar nosso serviço dentro do construtor

constructor(private data: DataService) {}

Agora, estamos prontos para usar nosso serviço e chamar os métodos dele.

Como buscar dados do serviço de dados

Para buscar os dados do serviço, chamaremos o método getData() e faremos a inscrição no observável que ele retornará. Também criaremos uma interface Result, para que possamos conferir se estamos recebendo o tipo correto de dados.

Faremos isso dentro do método ngOnInit:

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

Como atribuir dados à matriz de componente

No final, atribuiremos os dados à matriz de componente:

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

Depois de colocarmos os dados dentro da matriz do nosso componente, podemos vincular nosso modelo a esses dados.

Em slider.component.html,, já temos um modelo de HTML. A próxima etapa é vincular esse modelo à sliderArray.

Vamos vincular os dados ao modelo usando a diretiva *ngFor e, por fim, adicionar transformações no modelo para que o controle deslizante funcione.

Isso ocorre em três etapas:

  • Vincular sliderArray ao modelo
  • Adicionar a vinculação de eventos para botões de controle deslizante
  • Adicionar transformações CSS usando ngStyle e ngClass

Como vincular a slideArray ao componente

Temos um contêiner com um img-container, um text-container e um slider.

Vincularemos os dados nos três contêineres usando a diretiva *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>

Como vincular eventos à slideArray

Assim que os dados forem vinculados, nós vincularemos o evento de clique a cada botão deslizante usando o click binding do Angular. Criaremos uma função chamada selected(x), em que x é o índice da matriz.

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

Pontos importantes:

  • A função downselected diminui o valor da propriedade de transformação 50 vezes o valor do índice transmitido com o clique na função selected.
  • Essa lógica converte o contêiner de texto em 100%, 50%, -50% e -100%, resultando em quatro estados diferentes.

Como adicionar transformações CSS usando ngStyle e ngClass

Inicialmente, definimos todas as imagens com opacidade zero e adicionamos uma classe selected usando a ngClass directive quando o índice selecionado se torna igual ao índice da imagem. Essa classe selected acrescenta uma opacidade de valor um à imagem, tornando-a visível para o usuário.

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

Após isso, transformaremos o contêiner de texto de acordo com o valor de transform calculado usando a função select().

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

Depois de seguir todas essas etapas, você terá um código final semelhante ao mostrado abaixo:

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

Esse procedimento consiste em cinco etapas:

  • Usar o Shadow DOM para um elemento Angular
  • Usar entryComponents
  • Importar e usar o módulo CreateCustomElement de @angular/elements
  • Definir nosso custom-element
  • Executar o método ngDoBootstrap

Como usar o shadow DOM para elementos Angular

Agora que temos o controle deslizante de imagem funcionando, só precisamos transformá-lo em um Angular Element.

A parte boa é que apenas uma pequena mudança é necessária para transformar o DOM do componente em um shadow DOM.

Precisamos importar o módulo ViewEncapsulation e usar o método ShadowDom dele.

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

Como usar entryComponents

O componente de entrada é essencial no Angular. Para especificar um componente de entrada, inicialize-o em um NgModule.

Aqui, vamos especificar nosso SliderComponent na matriz entryComponents dentro de @NgModule.

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

Como importar e usar o módulo createCustomElement

Aqui, precisamos usar o módulo createCustomElement de @angular/elements. Você terá que usar o SliderComponent, como um parâmetro para a função createCustomElement. Depois, precisamos registrar o slider no DOM.

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

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

Para registrar o controle deslizante como um elemento DOM, nós o definiremos usando o método customElements.define.

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

Por fim, precisamos inicializar esse elemento personalizado usando o método ngDoBootstrap(). O código completo terá esta aparência:

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

}

Como empacotar o elemento Angular

Será necessário modificar o package.json com nossos novos comandos. Modificaremos o objeto script no arquivo package.json.

Vamos conferir nosso objeto script modificado:

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

Agora, podemos executar o comando ng build & ng package e, por fim, executar ng-serve para exibir a pasta dist/ gerada usando o comando de compilação. Além disso, podemos usar o gzip recebido com o comando ng package, extraí-lo e publicá-lo como um npm module.