Tutorial do tutorial Ember.js. Aplicativo Super Rentals. Parte 1.2

Continuamos a publicar a tradução do tutorial no guia oficial do Ember.js. O tutorial consiste em duas partes e esta é a segunda metade da primeira parte do tutorial. Lembramos que você pode ler a primeira metade neste link


A lista de tópicos abordados no tutorial sugere:


  • Usando a CLI Ember
  • Navegação de arquivos e pastas do aplicativo Ember
  • Criar e vincular entre páginas
  • Modelos e componentes
  • Teste automatizado
  • Trabalhar com dados do servidor
  • Segmentos dinâmicos em rotas
  • Serviços na Ember
  • Biblioteca de dados Ember
  • Adaptadores e serializadores
  • Padrão de componente do provedor

Sente-se, abra os terminais, encontre o projeto no seu computador e vamos seguir em frente. E lembre-se de que, se você tiver alguma dificuldade, sempre pode pedir ajuda no canal da comunidade Discord (no canal russo # lang-russian ), bem como no canal de telegrama no idioma russo ember_js


Detalhes do componente


É hora de finalmente trabalhar na lista de aluguel:



Ao compilar esta lista de propriedades de aluguel, você aprenderá sobre:


  • Geração de componentes
  • Organizando código usando componentes de namespace
  • Encaminhando atributos HTML com ...attributes
  • Determinando a quantidade apropriada de cobertura de teste

Geração de componentes


Vamos começar criando o componente <Rental> . Desta vez, usaremos o gerador de componentes para criar um modelo e um arquivo de teste para nós:


 $ ember generate component rental installing component create app/components/rental.hbs skip app/components/rental.js tip to add a class, run `ember generate component-class rental` installing component-test create tests/integration/components/rental-test.js 

O gerador criou dois novos arquivos para nós: o modelo de componente em app/components/rental.hbs e o arquivo de teste do componente em tests/integration/components/rental-test.js .


Começaremos editando o modelo. Vamos codificar os detalhes de um objeto alugado e substituí-lo por dados reais do servidor.



Em seguida, escreveremos um teste para garantir que todos os detalhes estejam presentes. Substituiremos o teste de modelo gerado por nós por nossas próprias equipes, como fizemos anteriormente no componente <Jumbo> :



Os testes devem passar.



Por fim, vamos adicionar um novo componente ao modelo de índice para preencher a página.



Nesse caso, devemos ver o componente <Rental> mostrando nossa Grand Old Mansion três vezes na página:



Tudo parece muito bom para um pouco de trabalho!


Organizando código usando namespaces


Em seguida, adicione uma imagem para a propriedade de aluguel. Usaremos o gerador de componentes para isso novamente:


 $ ember generate component rental/image installing component create app/components/rental/image.hbs skip app/components/rental/image.js tip to add a class, run `ember generate component-class rental/image` installing component-test create tests/integration/components/rental/image-test.js 

Desta vez, tivemos / no nome do componente. Isso levou à criação de um componente em app/components/rental/image.hbs , que pode ser chamado como <Rental::Image> .


Componentes semelhantes são conhecidos como componentes de espaço para nome . O espaço para nome nos permite organizar nossos componentes em pastas de acordo com sua finalidade. Isso é totalmente opcional, mas conveniente, especialmente quando você está desenvolvendo um aplicativo grande em uma equipe.


Encaminhando atributos HTML com ...attributes


Vamos editar o modelo do componente:



Em vez de definir valores específicos para os atributos src e alt em <img> , escolhemos a palavra-chave ...attributes , que também é chamada de "splattributes". Isso permite que você transmita atributos HTML arbitrários ao chamar este componente, por exemplo:



Especificamos aqui o atributo HTML src e alt , que serão passados ​​para o componente e anexados ao elemento, onde ...attributes serão aplicados no modelo do componente. Você pode pensar que isso é semelhante a {{yield}} , mas apenas para atributos HTML e não para conteúdo de exibição. De fato, já usamos essa função anteriormente quando passamos o atributo de class para <LinkTo> .



Portanto, nosso componente <Rental::Image> não está associado a nenhuma propriedade de aluguel específica no site. Claro, tudo também está codificado conosco, mas trataremos disso em breve. Enquanto isso, restringiremos todo o código rígido a um componente, para que seja mais fácil limpá-lo quando prosseguirmos na extração de dados reais.

Em geral, é uma boa idéia adicionar ...attributes ao elemento raiz no seu componente. Isso fornecerá flexibilidade máxima, pois o iniciador pode precisar passar nas classes de estilo ou nos atributos ARIA para melhorar a acessibilidade.


Agora vamos escrever um teste para o nosso novo componente!



Determinando a quantidade apropriada de cobertura de teste


Por fim, também precisamos atualizar os testes para o componente <Rental> para garantir que chamemos com êxito <Rental::Image> .



Como já escrevemos testes relacionados a <Rental::Image> , aqui podemos omitir os detalhes e minimizar a verificação. Portanto, também não precisamos atualizar o teste <Rental> sempre que fizermos alterações em <Rental::Image> .



Componentes interativos


Neste capítulo, você adiciona interatividade à página, o que permite ao usuário clicar na imagem para ampliá-la ou reduzi-la:



Nesse caso, você aprenderá sobre:


  • Adicionando comportamento a componentes com classes
  • Acessar estados da instância a partir de modelos
  • Gerenciamento de estado com propriedades rastreadas
  • Usando sintaxes condicionais em modelos
  • Respondendo à interação do usuário com ações
  • Como chamar modificadores de elemento
  • Teste de interação do usuário

Adicionando comportamento a componentes com classes


Até agora, todos os componentes que escrevemos são puramente de apresentação - são apenas fragmentos de marcação reutilizável. É claro que isso é maravilhoso, mas os componentes do Ember podem fazer muito mais!


Às vezes, você deseja associar algum comportamento aos seus componentes para que eles possam fazer coisas mais interessantes. Por exemplo, <LinkTo> pode responder a cliques alterando o URL e movendo-se para outra página.


Aqui vamos fazer algo semelhante! Vamos implementar as funcionalidades de «View Larger» e «View Smaller» , que permitirão que nossos usuários cliquem na imagem em casa, na versão maior e clique nela novamente para retornar à versão menor.


Em outras palavras, precisamos de uma maneira de alternar a imagem entre um dos dois estados . Para fazer isso, precisamos de uma maneira do componente armazenar dois estados possíveis e saber em que estado ele está atualmente.


Além disso, o Ember nos permite associar o código JavaScript a um componente especificamente para esse fim. Podemos adicionar um arquivo JavaScript para nosso componente <Rental::Image> executando o gerador de componentes:


 $ ember generate component-class rental/image installing component-class create app/components/rental/image.js 

Este comando gerou um arquivo JavaScript com o mesmo nome que o nosso modelo de componente em app/components/rentals/image.js . Ele contém uma classe JavaScript herdada de @glimmer/component .


Zoe explica ...



@glimmer/component ou Glimmer component é uma das várias classes de componentes disponíveis para uso. Eles são um excelente ponto de partida quando você deseja adicionar comportamento aos seus componentes. Neste tutorial, usaremos apenas os componentes Glimmer.

Em geral, os componentes Glimmer devem ser usados ​​sempre que possível. No entanto, você também pode ver @ember/components (componentes clássicos) usados ​​em aplicativos mais antigos. Você pode diferenciá-los olhando o caminho para importá-los (o que é útil ao procurar a documentação apropriada, pois eles têm APIs diferentes e incompatíveis ).

O Ember instancia a classe sempre que nosso componente é chamado. Podemos usar esta instância para armazenar nosso estado:



Aqui, no construtor do componente , inicializamos a variável de instância this.isLarge com o valor false , pois esse é o estado padrão que queremos para o nosso componente.


Acessar estados da instância a partir de modelos


Vamos atualizar nosso modelo para usar este estado que acabamos de adicionar:



No modelo, temos acesso às variáveis ​​de instância do componente. A sintaxe condicional {{#if ...}}...{{else}}...{{/if}} permite exibir um conteúdo diferente dependendo da condição (nesse caso, o valor da this.isLarge instância this.isLarge ). Combinando essas duas funções, podemos visualizar, respectivamente, uma versão pequena e uma grande da imagem.


Podemos verificar isso alterando temporariamente o valor inicial em nosso arquivo JavaScript. Se app/components/rental/image.js para inicializar this.isLarge = true ; no construtor, devemos ver uma versão grande da imagem da propriedade no navegador. Uau!



Depois de checar, podemos mudar this.isLarge volta para false .


Como esse padrão de inicialização para variáveis ​​de instância no construtor é bastante comum, há uma sintaxe muito mais curta para ele:



Mesma funcionalidade, mas muito mais curta!


Obviamente, nossos usuários não podem editar nosso código fonte, por isso precisamos de uma maneira de mudar o tamanho da imagem do navegador. Em particular, queremos alternar o valor this.isLarge sempre que um usuário clica em nosso componente.


Gerenciamento de estado usando propriedades rastreadas


Vamos mudar nossa classe adicionando um método para mudar o tamanho:



Fizemos algumas coisas aqui, então vamos descobrir.


Primeiro, adicionamos o decorador @tracked à isLarge instância @tracked . Essa anotação informa ao Ember para acompanhar essa variável para atualizações. Sempre que o valor dessa variável é alterado, o Ember redesenha automaticamente todos os modelos que dependem de seu valor.


No nosso caso, sempre que atribuirmos um novo valor a this.isLarge , a anotação this.isLarge forçará o Ember a reavaliar a condição {{#if this.isLarge}} em nosso modelo e alternar entre os dois blocos, respectivamente.


Zoe explica ...



Não se preocupe! Se você se referir a uma variável no modelo, mas esqueceu de adicionar o decorador @tracked , no modo de desenvolvimento, você receberá um erro claro ao alterar seu valor!

Processamos ações do usuário


Em seguida, adicionamos o método toggleSize à nossa classe, que alterna this.isLarge em oposição ao seu estado atual ( false se torna true ou true se torna false ).


Por fim, adicionamos o decorador @action ao nosso método. Isso indica a Ember que pretendemos usar esse método em nosso modelo. Sem isso, o método não funcionaria corretamente como uma função de manipulação de eventos (nesse caso, um manipulador de cliques).


Zoe explica ...



Se você esquecer de adicionar o decorador @action , também receberá um erro ao clicar no botão no modo de desenvolvimento!

Agora é hora de usar isso no modelo:



Nós mudamos duas coisas.


Primeiro, como queríamos tornar nosso componente interativo, trocamos a tag que contém de <div> para <button> (isso é importante por razões de acessibilidade). Usando a tag semântica correta, também obteremos foco "gratuito" e interação do teclado.


Em seguida, usamos o modificador {{on}} para anexar this.toggleSize como um manipulador de cliques de botão.


Assim, criamos nosso primeiro componente interativo. Experimente como ele funciona no navegador!



Teste de interação do usuário


Por fim, vamos escrever um teste para esse novo comportamento:




Resultado do teste


Vamos limpar nosso modelo antes de prosseguir. Adicionamos muitas duplicatas quando inserimos expressões condicionais no modelo. Se olharmos de perto, as únicas coisas que diferem entre esses dois blocos são:
1) A presença de uma classe CSS "large" em <button> .
2) O texto «» e «» .


Essas alterações estão ocultas em muitos códigos duplicados. Podemos reduzir a duplicação usando a expressão {{if}} :



A versão da expressão {{if}} recebe dois argumentos. O primeiro argumento é a condição . O segundo argumento é uma expressão, que deve ser executada se a condição for verdadeira.


Opcionalmente, {{if}} pode assumir como terceiro argumento uma expressão que deve ser executada se a condição for falsa. Isso significa que poderíamos reescrever o rótulo do botão da seguinte maneira:



Se isso é uma melhoria na clareza do nosso código, é uma questão de gosto. De qualquer forma, reduzimos significativamente a duplicação em nosso código e tornamos mais visíveis partes importantes da lógica.


Execute os testes pela última vez para garantir que nossa refatoração não quebrou nada e estaremos prontos para o próximo desafio!



Reutilizando componentes


A parte restante não realizada no componente é um mapa mostrando a localização da casa, na qual continuaremos trabalhando:


Ao adicionar um mapa, você aprenderá sobre:


  • Gerenciamento de configuração no nível do aplicativo
  • Parametrização de componentes com argumentos
  • Acesso aos argumentos do componente
  • Interpolando valores em modelos
  • Substituindo atributos HTML em ... atributos
  • Refatoração com getters e rastreamento automático (rastreamento automático)
  • Recuperando valores JavaScript em um contexto de teste

Gerenciamento de configuração no nível do aplicativo


Usaremos a API do Mapbox para criar mapas para nossas propriedades de aluguel. Você pode se cadastrar gratuitamente e sem cartão de crédito.


O Mapbox fornece uma API de imagem de mapa estática que serve imagens de mapa no formato PNG. Isso significa que podemos gerar a URL apropriada para os parâmetros desejados e exibir o mapa usando o padrão <img> . Classe!


Se você estiver interessado, poderá explorar as opções disponíveis no Mapbox usando uma sandbox interativa .


Depois de se registrar no serviço, pegue seu token público (token público padrão) e cole-o em config/environment.js :




Como o nome sugere, config/environment.js usado para configurar nosso aplicativo e armazenar chaves de API como estas. Esses valores podem ser acessados ​​de outras partes de nosso aplicativo e podem ter valores diferentes, dependendo do ambiente atual (que pode ser desenvolvimento, teste (teste) ou produção (produção)).


Zoe explica ...



Se desejar, você pode criar tokens de acesso diferentes da Mapbox para uso em diferentes ambientes. No mínimo, cada token deve ter um escopo "styles: tile" para usar a API de imagem estática da Mapbox.

Após salvar as alterações em nosso arquivo de configuração, precisaremos reiniciar nosso servidor de desenvolvimento para receber essas alterações. Diferente dos arquivos que editamos, o config/environment.js não é reiniciado automaticamente.


Você pode parar o servidor localizando a janela do terminal em que o ember server execução e pressione Ctrl + C Ou seja, pressionando a tecla “C” no teclado enquanto mantém pressionada a tecla “Ctrl”. Depois de parado, você pode iniciá-lo novamente usando o mesmo comando do ember server .


 $ ember server building... Build successful (13286ms) – Serving on http://localhost:4200/ 

Criando um componente contendo uma classe de componente


Depois de adicionar a chave da API do Mapbox ao aplicativo, vamos gerar um novo componente para o nosso mapa.


 $ ember generate component map --with-component-class installing component create app/components/map.js create app/components/map.hbs installing component-test create tests/integration/components/map-test.js 

Como nem todo componente necessariamente terá um comportamento específico associado a ele, o gerador de componentes por padrão não cria um arquivo JavaScript para nós. Como vimos anteriormente, sempre podemos usar o gerador de componentes para adicioná-lo mais tarde.


No entanto, no caso do nosso componente <Map> , temos quase certeza de que precisaremos de um arquivo JavaScript para algum comportamento que ainda precisamos definir! Portanto, podemos passar o sinalizador --with-component-class gerador de --with-component-class para que tenhamos tudo o que precisamos desde o início.


Zoe aconselha ...



Demais para digitar? Use o ember g component map -gc . O sinalizador -gc denota o componente Glimmer ( g limmer c omponent) e também gera a classe ( g enerate c lass)

Parametrização de componentes usando argumentos


Vamos começar com o nosso arquivo JavaScript:



Aqui importamos o token de acesso do arquivo de configuração e o devolvemos do token getter. Isso nos permite acessar nosso token como this.token dentro da classe MapComponent e no modelo de componente. Também é importante codificar o token, caso ele contenha caracteres especiais que não sejam seguros para o URL.


Interpolação de valores em padrões


Agora vamos passar do arquivo JavaScript para o modelo:



Primeiro, temos um elemento de contêiner para estilizar.


Em seguida, temos <img> para solicitar e renderizar uma imagem estática do mapa no Mapbox.


Nosso modelo contém vários valores que ainda não existem - @lat , @lng , @zoom , @width e @height . Estes são os argumentos do componente <Map> que forneceremos a ele quando chamado.


Parametrizando nosso componente com argumentos, criamos um componente reutilizável que pode ser chamado de diferentes partes do aplicativo e customizado para atender às necessidades desses contextos específicos. Já vimos isso em ação ao usar o componente <LinkTo> anteriormente; precisávamos especificar o argumento @route saber para qual página ir.


Fornecemos um valor padrão razoável para o atributo alt base nos valores dos @lng e @lng . Você pode perceber que interpolamos diretamente os valores no valor do atributo alt . O Ember combinará automaticamente esses valores interpolados em um valor de sequência, incluindo a execução de qualquer HTML de escape necessário.


Substituindo atributos HTML para ...attributes


Em seguida, usamos ...attributes para permitir que o chamador personalize ainda mais <img> , por exemplo, passe atributos adicionais, como class , e também substitua nosso atributo alt padrão por um atributo mais específico ou amigável ao ser humano.


A ordem é importante! O Ember aplica atributos na ordem em que aparecem. Atribuindo ao atributo o valor alt padrão primeiro (antes de aplicar ...attributes ), fornecemos explicitamente ao chamador a oportunidade de fornecer um atributo alt mais especializado de acordo com o caso de uso necessário.


Como o atributo alt passado (se houver) aparecerá após o nosso, ele substituirá o valor especificado. Por outro lado, é importante atribuirmos src , width e height após ... atributos para que eles não sejam substituídos acidentalmente pelo invocador.


O atributo src interpola todos os parâmetros necessários no formato de URL da API de imagem estática do Mapbox, incluindo o token seguro de URL this.token .


Finalmente, como estamos usando a imagem "retina" @2x , precisamos especificar os atributos de width e height . Caso contrário, o <img> será exibido duas vezes mais do que esperávamos!


Adicionamos bastante código a um componente, então vamos escrever alguns testes! Em particular, precisamos garantir que tenhamos cobertura de teste para o comportamento de atributos HTML substituídos, que discutimos acima.





Observe que o hasAttribute auxiliar hasAttribute do qunit-dom suporta o uso de expressões regulares . , , src https://api.mapbox.com/ ( , ). , , .


… .



, ! , ? , <Map> <Rental> :



!



...



, , config/environment.js MAPBOX_ACCESS_TOKEN . ! , Ember-.

<Rental> , <Map> .



- (auto-track)


<Map> src <img> , . — JavaScript .


JavaScript API this.args.* . , URL .


...



this.args — API, Glimmer. (, «» ) (legacy) , API JavaScript .



! !



, @tracked . , , Ember .


, , , . , src lat , lng , width , height zoom this.args . , , , {{this.src}} .


Ember , . @tracked , Ember , (invalidate) , «» . (auto-track). , this.args ( , this.args.* ), @tracked Glimmer. , (Just works).


JavaScript


, :





API this.setProperties , .


, . ( !).


this , render . «» . .


!




<Rental> . , :



:


  • (model hook)
  • (mocking) JSON
  • (remote)
  • {{#each}}


<Rental> . , , , , . .


, , . , .


, Ember — . , , .


. app/routes/index.js :



, , . Route . , .


Route IndexRoute, , .



. ? model() . .


, . Ember , . , , ( ).


, . , async . await . ( : , Promise Javascript )


. , , JavaScript ( POJO (Plain Old Javascript Object)).


, , , . @model . POJO, .


, , title :



, .



Ótimo!


, , , , , ! <Rental> , .


.


-, <Rental> @rental . <h1> , , :



@model <Rental> @rental , «Grand Old Mansion» <Rental> ! , @rental .




, «Grand Old Mansion», , .



, : , .


, , , , .


<Rental> . , setProperties , .



, <Rental> render @rental . , !



(mocking) JSON


, , , , , !


, , , API. , API . JSON . , JSON HTTP- — , API, — - . Legal!


? , JSON .zip. public .


, public :


 public ├── api │ ├── rentals │ │ ├── downtown-charm.json │ │ ├── grand-old-mansion.json │ │ └── urban-living.json │ └── rentals.json ├── assets │ └── images │ └── teaching-tomster.png └── robots.txt 4 directories, 6 files 

, , URL http://localhost:4200/api/rentals.json .



«» JSON. !


(remote)


. , .



?


, Fetch API JSON API /api/rentals.json , URL, .


, . Fetch API , fetch async , . , await .


Fetch API . , ; , JSON, json() . , await .



, , .




JSON:API , , .


-, JSON:API , "data" , . ; , , , — , .


, , . type id , (!). , , attributes .


, , , , : , , type , - . type "Standalone" "Community", , <Rental> .


JSON:API. .


:



(parsing) JSON attributes , type , . , , - .


! .


(helper) {{#each}}


, , — index.hbs , <Rental> . @rental @model . , @model — ()! , , .


.



{{#each}}...{{/each}} , . — — . — <Rental> , <li> .


{{rental}} . rental ? , ! as |rental| each . - , as |property| , {{property}} .


, .



Viva! , . fetch . , ?


, !



( 1.1 1.2)


Ember!


Ember, .


#Emberjs. Discord , ember_js


Quando você voltar, contaremos com o que aprendemos na primeira parte e passaremos para um novo nível!


ATUALIZAÇÃO: E demolição, agradeça ao usuário MK1301 a correção de erros.

Source: https://habr.com/ru/post/pt482390/


All Articles