Na versão 7 do Angular é possível utilizar um aspecto muito interessante para efeitos de interação: o drag and drop, famoso arrasta e solta.
Neste artigo eu mostro como utilizar esse recurso aplicado à uma lista com personagens de um jogo.
Requisitos
Criado o projeto, é necessário incluir as seguintes dependências. No terminal, informe:
$ ng add @angular/material
Aceite a instalação da biblioteca hammerjs e escolha um tema de sua preferência.
Com isso já podemos abrir o módulo principal e definir no array de import o módulo DragDropModule.
Para esse exemplo eu vou utilizar o HttpClientModule para carregar os dados de um arquivo local.
// Outros imports omitidos
'@angular/platform-browser/animations';
import { HttpClientModule } from '@angular/common/http';
import { DragDropModule } from '@angular/cdk/drag-drop';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
HttpClientModule,
DragDropModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Na pasta assets vamos criar um arquivo data.json com o seguinte conteúdo:
[
{
“name”: “Scorpion”,
“image”: “https://i.pinimg.com/564x/c4/b2/65/c4b2659a04c7cb1f99c1b2ed738f73d2.jpg",
“fatality”: “X + L2 + UP”
},
{
“name”: “Subzero”,
“image”: “https://i.pinimg.com/564x/80/b5/45/80b5458dd048c3457ca54a903de3f7ba.jpg",
“fatality”: “Up + Down + Up + Kick medium”
},
{
“name”: “Kitana”,
“image”: “https://i.pinimg.com/originals/4f/cc/cd/4fcccdf78814311d77be47d233db1699.png",
“fatality”: “X + L2 + UP”
},
{
“name”: “Aang”,
“image”: “http://rehatron-alpha.eu/images/6321365424_essay-help-forum.png",
“fatality”: “ L2 + UP”
}
]
Dessa forma, temos um array de objetos com as propriedades que representam nossos personagens.
Lógica do componente
Abrindo a classe app.component.ts vamos definir uma interface para mapear o personagem com os campos name, image e fatality.
export interface Character {
name: string;
image: string;
fatality: string;
}
Após a assinatura da classe vamos definir dois arrays que representarão nossas listas.
No método construtor fazemos a injeção do HttpClient e chamamos o método getMyList().
A implementação é basicamente um get tipando o resultado para nossa interface <Character[ ]>, informando o caminho da fonte de dados “assets/data.json”.
Ficamos ouvindo a emissão de eventos no subscribe, passando a lista para o mylist.
A próxima função, drop é responsável por duas ações.
Alterar a ordem dos elementos, utilizando a função movieItemInArray e/ou, mudar o item (no caso, os personagens) para outra lista. Para isso utilizamos a função transferArrayItem.
A implementação deve verificar antes, qual a origem (container) do item, através do comando:
if (event.previousContainer === event.container) {
Segue o código completo da classe.
import { Component } from '@angular/core';
import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
import { HttpClient } from '@angular/common/http';
export interface Character {
name: string;
image: string;
fatality: string;
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
myList: Character[]
confirmList: Character[] = [];
constructor(private httpClient: HttpClient) {
this.getMyList()
}
getMyList() {
this.httpClient.get<Character[]>("assets/data.json")
.subscribe(list =>{
this.myList = list;
})
}
drop(event: CdkDragDrop<Character[]>) {
if (event.previousContainer === event.container) {
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
transferArrayItem(event.previousContainer.data,
event.container.data,
event.previousIndex,
event.currentIndex);
}
}
}
Template do componente
Vamos codifcar o template. Edite o app.component.html e deixe assim:
<div class="container mx-auto p-2">
<div class="row p-2">
<div class="col-lg-12">
<h1>Mortal Kombat 11</h1>
</div>
</div>
<div class="row">
<div cdkDropList #minha="cdkDropList" [cdkDropListConnectedTo]="[confirmados]" [cdkDropListData]="myList"
class="col-lg-6" (cdkDropListDropped)="drop($event)">
<ul class="list-group">
<li class="list-group-item list-group-item-danger">
<h3>Minha Lista</h3>
</li>
<li class="list-group-item" *ngFor="let p of myList " cdkDrag>{{p.name}}</li>
</ul>
</div>
<div cdkDropList #confirmados="cdkDropList" [cdkDropListData]="confirmList" [cdkDropListConnectedTo]="[minha]"
(cdkDropListDropped)="drop($event)" class="col-lg-6">
<ul class="list-group">
<li class="list-group-item active">
<h3>Confirmados</h3>
</li>
<li class="list-group-item" *ngFor="let p of confirmList" cdkDrag>
<img [src]="p.image" class="img-person">
{{p.name}}</li>
</ul>
</div>
</div>
</div>
As linhas 8 e 17 são os locais onde definimos o container, utilizando a diretiva cdkDropList. Ainda na linha 8 criamos um atributo #minha e atribuímos a diretiva.
Com isso, podemos apontar para qual lista podemos arrastar os personagens, utilizando o atributo [cdkDropListConnectedTo].
Definimos a fonte de dados com [cdkDropListData] e finalmente a função (cdkDropListDropped)=”drop($event)”, responsável pelas ações definidas anteriormente.
Na linha 14 realizamos a iteração da lista com o *ngFor. Importante: aqui também declaramos a diretiva cdkDrag, indicando o elemento que pode ser arrastado.
Note que, a partir da linha 17, codificamos a outra lista, alterando o nome e a fonte.
No arquivo de estilos, codificamos duas animações para alterar a ordem e para soltar o personagem.
.cdk-drop-list-dragging .cdk-drag {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.cdk-drag-animating {
transition: transform 300ms cubic-bezier(0, 0, 0.2, 1);
}
.img-person {
border-radius: 35%;
width: 80px;
}
Fácil, né?
Até mais!