No meu último artigo, Elm: Programação Funcional no Front-End do jeito certo, falei sobre as principais características e vantagens da linguagem mas (praticamente) sem mostrar código. Ou seja, dei ênfase no porquê e não no como. Hoje, vou focar no como e mostrar na prática o passo a passo para criar uma aplicação bem simples com Elm.
Introdução
Neste artigo, não vamos fazer um setup da linguagem, mas se você deseja ter o Elm instalado e poder criar suas próprias aplicações, sugiro seguir o guia oficial.
Vamos construir uma aplicação bem simples, mas que demonstra diversos aspectos da linguagem: um contador.
Modularização
Para importarmos módulos com Elm, basta adicionarmos import seguido do nome do módulo que desejamos. Como vamos utilizar HTML na aplicação, vamos importar o módulo Html, um dos módulos oficiais da linguagem, assim:
import Html
Dessa forma podemos invocar funções do módulo Html:
Html.text 'Olá'
Mas repetir o nome do módulo toda vez que formos invocar uma função dele pode ser cansativo. Podemos evitar essa repetição alterando o import, expondo as funções que estamos usando:
import Html exposing (text) text 'Olá'
Podemos também expor todas as funções de um módulo, sem precisar nomear cada uma delas:
import Html exposing (..) text 'Olá'
Com isso, já temos a primeira linha da nossa aplicação:
import Html exposing (..)
Model
O nosso model será extremamente simples. Como temos um contador, o modelo será um valor inteiro simples, inicializado com zero:
model = 0
Opcionalmente, podemos deixar explícita a tipagem do modelo, fazendo uma anotação de tipo (ou Type Annotation), desta forma:
model: Int model = 0
Se não fizermos a anotação de tipo, o compilador irá automaticamente inferir os tipos.
Com isso, nossa aplicação está assim:
import Html exposing (..) model: Int model = 0
View
Não faz sentido construir um contador sem que seja possível visualizá-lo. Precisamos então da nossa view, que será simplesmente uma função que converterá código Elm em HTML.
Para refrescar a memória, é disto que precisamos:
Ou seja, dois botões: um para incrementar e um para decrementar, e um elemento de texto com o valor do contador.
Podemos então implementar a função que irá representar a view desta forma:
view model = div [] [ button [] [ text "-" ] , div [] [ text (toString model) ] , button [] [ text "+" ] ]
O código acima pode parecer complicado, mas vamos desmitificá-lo passo a passo.
1. view model = 2. div [] 3. [ button [] [ text "-" ] 4. , div [] [ text (toString model) ] 5. , button [] [ text "+" ] 6. ]
Na linha 1, estamos criando uma função chamada view, que recebe como parâmetro o nosso model.
Essa nova função view receberá uma div, que também é uma função (linha 2):
1. view model = 2. div [] 3. [ button [] [ text "-" ] 4. , div [] [ text (toString model) ] 5. , button [] [ text "+" ] 6. ]
- A função div recebe dois parâmetros:
O primeiro é uma lista de atributos (id, class, style etc). No nosso caso, ela vai ficar vazia porque não precisamos de nenhum atributo. - E o segundo é uma lista de elementos filhos dessa div.
1. view model = 2. div [] 3. [ button [] [ text "-" ] 4. , div [] [ text (toString model) ] 5. , button [] [ text "+" ] 6. ]
Os filhos da div (da linha 2) serão:
- Um botão para decrementar o contador (linha 3)
- Uma outra div com o valor do contador (linha 4)
- Um botão para incrementar o contador (linha 5)
Como o nosso model é um Int, não podemos simplesmente usá-lo na função text, porque ela espera uma String como parâmetro. Por isso, convertemos o valor do model de um Int para uma String:
1. view model = 2. div [] 3. [ button [] [ text "-" ] 4. , div [] [ text (toString model) ] 5. , button [] [ text "+" ] 6. ]
Agora basta explicitarmos as anotações de tipo da view. Se analisarmos a função view, verificamos que ela espera um model como parâmetro (que é simplesmente um Int) e retorna um Html como resultado. Isso resultaria em:
view: Int -> Html view model = div [] [ button [] [ text "-" ] , div [] [ text (toString model) ] , button [] [ text "+" ] ]
Porém, isso está incorreto por um pequeno detalhe:
view: Int -> Html Msg
Mais pra frente, vamos entender esse Msg obscuro, mas quero que neste momento você entenda apenas que a view recebe o model e retorna um Html.
Portanto, nossa aplicação completa se encontra desta maneira:
import Html exposing (..) model: Int model = 0 view: Int -> Html Msg view model = div [] [ button [] [ text "-" ] , div [] [ text (toString model) ] , button [] [ text "+" ] ]
Type Aliases
Uma boa convenção da comunidade Elm é criar Type Aliases para facilitar a leitura das anotações de tipo criadas.
No nosso caso, podemos criar um Alias para o modelo desta forma:
type alias Model = Int
Ou seja, em todos os lugares da nossa aplicação que estamos usando o Int para nos referir ao model, podemos agora usar o próprio Model. Facilita muito a leitura, principalmente em aplicações grandes.
Atualizando então a aplicação, temos:
import Html exposing (..) type alias Model = Int model: Model model = 0 view: Model -> Html Msg view model = div [] [ button [] [ text "-" ] , div [] [ text (toString model) ] , button [] [ text "+" ] ]
Ficou alguma dúvida? Calma que ainda temos a parte 2! Na próxima semana, eu volto com o final do artigo sobre como criar uma aplicação em Elm. Se você tiver alguma sugestão ou comentário, aproveite os campos abaixo.