07 de julho de 2017 • 7 min de leitura
Criando componentes usando só ES6
Muita gente usa React, outros usam Vue e outros Angular. Por que não só ES6?
Introdução
Faaaaala pessoal, hoje eu venho trazendo um post bem legal, que sempre acabo me perguntando. Será que precisamos usar React/Vue/Angular para criar componentes sempre? Se for algo simples, por que não usar ES6 puro?
Nesse post irei falar sobre uma propriedade do ES6 que curto muito que é a Template Strings.
E durante esse post eu vou ouvindo a trilha sonora de um dos mais belos jogos desse ano Horizon Zero Dawn, o jogo é incrível e a trilha sonora é ainda melhor, bota para ouvir, garanto que não vai se decepcionar!
Trabalhando com Strings no passado
As strings
são representadas no JavaScript utilizando as aspas simples 'minha string'
ou aspas duplas "minha string"
. Mas para algumas tarefas em JS, trabalhar com strings era bem chatinho, como, por exemplo, para trabalhar com múltiplas linhas, o código precisava ser assim:
const text = 'Minha primeira linha\n' +
'Minha segunda linha\n' +
'Minha terceira linha.';
console.log(text);
// > Minha primeira linha
// Minha segunda linha
// Minha terceira linha
Repare que precisávamos utilizar o operador +
para concatenar as strings e não só isso, precisávamos também utilizar o \n
para indicar uma quebra de linha.
Outro caso em que era bem chatinho trabalhar com strings era essa parte da concatenação, onde precisávamos quebrar a string e usar o +
para juntar com nossa variável. Dessa forma:
const name = 'Willian';
console.log('Hello ' + name + '!');
// > Hello Willian!
Precisávamos inclusive prestar atenção para não esquecer o espaço depois da palavra, porque senão juntava com a variável e ficava tudo horrível.
Trabalhando com Strings no ES6
Com a chegada do ES6, veio essa coisa maravilhosa que é o Template String. Agora basta utilizarmos o símbolo de crase ` na string e conseguimos criar interpolações e concatenar muito mais facilmente.
Para trabalhar com múltiplas linhas ficaria assim:
const text = `Minha primeira linha
Minha segunda linha
Minha terceira linha.`;
console.log(text);
// > Minha primeira linha
// Minha segunda linha
// Minha terceira linha
E para trabalhar com interpolação, podemos utilizar a sintaxe ${variavel}
dentro da string, desse forma:
const name = 'Willian';
console.log(`Hello ${name}!`);
// > Hello Willian!
Outra coisa legal é que podemos adicionar funções dentro dessas strings para rodar, um exemplo:
function sum(a, b) {
return a + b;
}
console.log(`2 + 2 = ${sum(2, 2)}`);
// > 2 + 2 = 4
Isso abre diversas possibilidades e facilita demais na escrita e leitura do nosso código.
Criando templates
Aí você vem e pergunta: "Tá Willian, mas o que isso tem a ver com componentes?". E eu respondo, tem tudo! Antigamente nós utilizávamos coisas como Handlebars e outros mais para poder criar templates e assim formar componentes. Mas agora com o Template String, nós podemos usar esse poder para criar nossos próprios componentes! =D
Se você quiser já ver o que vamos criar direto, só acessar esse link do codepen.
Criando método para montar o markup
O primeiro componente que iremos criar será o componente onde mostra as informações do album, conforme a imagem abaixo:
Para isso teríamos um Markup da seguinte forma:
<img class="album-image" src="..." alt="Ten">
<p class="album-title">Ten</p>
<p class="album-artist">Pearl Jam</p>
<p class="album-counter">11 Músicas</p>
E transformando isso para o nosso lindo ES6, ficaria assim então:
function createMarkupAlbum(data) {
return (`
<img class="album-image" src="${data.images}" alt="${data.name}">
<p class="album-title">${data.name}</p>
<p class="album-artist">${data.artists}</p>
<p class="album-counter">${data.tracks.length} Músicas</p>
`);
}
Onde o data
é a informação que pode vir de uma API
, json
ou até mesmo uma variável
, como utilizada no exemplo.
Criando método para renderizar o markup
Depois de criada o método de montar o markup, precisamos renderizar na tela e para isso é bastante simples. Precisamos basicamente selecionar um elemento do DOM e usar o innerHTML
para apendar o nosso markup criado, que nada mais é que uma string =D
function renderAlbumInfo(data, element) {
const markup = createMarkupAlbum(data);
element.innerHTML = markup;
};
Repare que o meu método chama internamente o createMarkupAlbum
que vai montar o nosso markup recebendo a informação do nosso data
passado e depois disso, apenderemos o markup no element
definido.
Chamando o método e pintando na tela
Com os nossos métodos criados, precisamos só então criar um elemento no html para apendar nosso componente na tela e chamar o método para fazê-lo.
const album = document.getElementById('album');
renderAlbumInfo(data, album);
Criando markups mais complexos e utilizando funções
Com o código feito acima, já temos as informações do album, mas ainda faltam as músicas ao lado. Conforme a imagem abaixo:
Se vocês repararem, dentro dessa lista temos um padrão básico, que é sempre: Número da Música - Nome da Música - Tempo
. O que podemos fazer então é criar um loop para criar música depois de música e então apendar a lista inteira.
Nosso conjunto de dados para as tracks
é um array da seguinte forma:
"tracks" : [
{
"duration_ms" : 231367,
"spotify" : "https://open.spotify.com/track/4nRyBgsqXEP2oPfzaMeZr7",
"name" : "Once",
"track_number" : 1
}, {
"duration_ms" : 292580,
"spotify" : "https://open.spotify.com/track/6QewNVIDKdSl8Y3ycuHIei",
"name" : "Even Flow",
"track_number" : 2
}...
]
Para isso podemos utilizar o método map
que vai iterar cada item dessa lista e vai criar o markup para nós. Ao final de cada iteração vamos usar o método join('')
para unir as strings criando a lista completa. O código fica assim:
function createMarkupTracks(tracks) {
return tracks.map(track => `
<div class="music">
<p class="music-number">${track.track_number}</p>
<p class="music-title">${track.name}</p>
<p class="music-duration">${convertToHumanTime(track.duration_ms)}</p>
</div>`).join('');
}
Repare que ali eu ainda tenho uma função convertToHumanTime
, ela é necessária pois no meu array eu estou recebendo o tempo em ms
, que não é tão legível para nós. Então como eu posso utilizar funções dentro dos meus templates, eu crio essa função do lado de fora, que fica desse jeito:
function convertToHumanTime(duration) {
let s = parseInt((duration / 1000) % 60, 10);
const m = parseInt((duration / (1000 * 60)) % 60, 10);
s = (s < 10) ? `0${s}` : s;
return `${m}:${s}`;
}
Depois disso é só criar a função para apendar esse markup igual feito anteriormente:
function renderAlbumTracks(data, element) {
const markup = createMarkupTracks(data);
element.innerHTML = markup;
}
const list = document.getElementById('list');
renderAlbumTracks(data.tracks, list);
E é isso gente! Nosso componente está prontinho e renderizado na tela! =D
Para ver o código completo, só ir lá no link do codepen.
Separando em diferentes arquivos e usando import/export
Só para complementar o código, podemos separar esses métodos e importá-los num arquivo main
que será responsável por fazer tudo isso. Um exemplo seria:
function createMarkup(data) {
return (`
<img class="album-image" src="${data.images[0].url}" alt="${data.name}">
<p class="album-title">${data.name}</p>
<p class="album-artist">${data.artists[0].name}</p>
<p class="album-counter">${data.tracks.total} Músicas</p>
`);
}
export default function renderAlbumInfo(data, element) {
const markup = createMarkup(data);
element.innerHTML = markup;
return data;
};
Repare que eu estou exportando somente a função renderAlbumInfo
, deixando assim o método createMarkup
como privado, assim não corro o risco da criação do meu markup ser poluído por outro código.
E aí no meu arquivo main
eu teria algo como:
import renderAlbumInfo from './AlbumInfo';
const albumInfo = document.getElementById('album-info');
spotify.album.getAlbum(albumId)
.then(data => renderAlbumInfo(data, albumInfo))
Onde o meu data
está sendo extraído da própria API do Spotify e alimentando o meu método renderAlbumInfo
.
Conclusão
É isso galera, sei que o React/Vue/Angular fazem muito mais do que isso, mas as vezes nem precisamos deles se quisermos criar componentes básicos e as vezes até complexos xD