# Convertendo modais de controladores legados para a nova API do componente DModal

**URL:** https://meta.discourse.org/t/converting-modals-from-legacy-controllers-to-new-dmodal-component-api/268057
**Category:** Developer Guides
**Tags:** code
**Created:** [3 Julho , 2023 09:52 UTC](https://meta.discourse.org/t/converting-modals-from-legacy-controllers-to-new-dmodal-component-api/268057 "2023-07-03T09:52:14Z")
**Posts on this page:** 1
**Showing post:** 1

<div class="post-metadata">

### Author: ![Discourse](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/discourse/32/148734_2.png) [@Discourse](https://meta.discourse.org/u/Discourse)
#### Post date: [3 Julho , 2023 09:52 UTC](https://meta.discourse.org/t/converting-modals-from-legacy-controllers-to-new-dmodal-component-api/268057/1 "2023-07-03T09:52:14Z")

</div>

> ℹ Se você estiver implementando um novo Modal, confira a documentação principal [aqui](https://meta.discourse.org/t/268304). Este tópico descreve como migrar um Modal baseado em controlador existente para a nova API baseada em Componentes.

No passado, o Discourse utilizava uma API baseada em Controladores do Ember para renderizar modais. Para invocar o modal, você passava uma string com o nome do controlador para `showModal()`. Internamente, isso fazia uso da API `Route#renderTemplate` do Ember, que está obsoleta no Ember 3.x e será removida no Ember 4.x.

Para permitir que o Discourse faça o upgrade para o Ember 4.x e versões posteriores, introduzimos uma nova API baseada em componentes para modais. Esta nova API abraça os padrões de design ‘declarativos’ do Ember e visa fornecer semânticas limpas de DDAU (data down actions up).

## Etapa 1: Mover Arquivos

Mova o arquivo JS do controlador e o arquivo de template para o diretório `/components/modal`. Isso os torna um ‘componente co-localizado’, que pode ser importado da mesma forma que qualquer outro módulo JS.

## Etapa 2: Atualizar o arquivo JS

Em seguida, atualize a definição do componente JS para estender `@ember/component` em vez de `@ember/controller` \[1\]. Remova o mixin `ModalFunctionality` e atualize qualquer uso de suas funções de acordo com a tabela abaixo:

| Antes | Depois |
| --- | --- |
| `flash()` and `clearFlash()` | Crie uma propriedade `flash` no seu componente e passe-a para o argumento `@flash` de `<DModal>`. Por padrão, o alerta será estilizado com a classe `alert`, que é uma cópia da classe ‘`error`’, mas pode ser sobrescrito usando o argumento `@flashType`. |
| `showModal()` | Importe a função `showModal` de `discourse/lib/show-modal` |
| ação `closeModal` | Invocar o argumento `closeModal`, que é automaticamente passado para o seu componente |

Os Controladores de Modal do estilo antigo viviam ‘para sempre’, o que significava que tínhamos que limpar o estado manualmente. Com a nova API baseada em Componentes, o componente será criado e destruído quando o modal for mostrado/oculto. Em muitos casos, isso significa que seus antigos hooks de ciclo de vida não são mais necessários.

Se você ainda precisar de alguma lógica baseada em ciclo de vida, use esta tabela:

| Antes | Depois |
| --- | --- |
| `onShow()` | Use o ciclo de vida padrão de componentes Ember (`init()` ou modificador Ember) |
| `afterRender` | Use o ciclo de vida padrão de componentes Ember (`init()` ou modificador Ember) |
| `beforeClose()` | Crie um wrapper ao redor do argumento `@closeModal` que é passado para o seu componente. Passe uma referência para o seu wrapper de fechamento para o `DModal` como `<DModal @closeModal={{this.myCloseModalWrapper}}`\> |
| `onClose()` | Use o ciclo de vida padrão de componentes Ember (`willDestroy()` ou modificador Ember) |

## Etapa 3: Atualizar o Template

Substitua o wrapper `<DModalBody>` por `<DModal>`. Adicione alguns novos atributos:

- Passe o novo argumento `@closeModal`
- Adicione uma classe explícita. Para corresponder ao comportamento antigo, pegue o nome do arquivo do seu controlador e adicione `-modal`.

Por exemplo, se o seu controlador de modal fosse chamado `close-topic.js`, a nova invocação de `<DModal>` seria algo assim:

```hbs
<DModal @closeModal={{@closeModal}} class="close-topic-modal">

```

Se a invocação de `DModalBody` incluir quaisquer outros argumentos, atualize-os com base na tabela abaixo:

| Antes | Depois |
| --- | --- |
| `@title="title_key"` | `@title={{i18n "title_key"}}` |
| `@rawTitle="translated title"` | `@title="translated title"` |
| `@subtitle="subtitle_key"` | `@subtitle={{i18n "subtitle_key"}}` |
| `@rawSubtitle="translated subtitle"` | `@subtitle="translated subtitle"` |
| `@class` | `@bodyClass` |
| `@modalClass` | Use a sintaxe de parênteses angulares com atributo html regular: `<DModal class="blah">` |
| `@titleAriaElementId` | Use a sintaxe de parênteses angulares com atributo html regular: `<DModal aria-labelledby="blah">` |
| `@dismissable`, `@submitOnEnter`, `@headerClass` | Inalterado |

Se houvesse qualquer conteúdo de rodapé renderizado após o antigo componente `<DModalBody>`, use o novo bloco nomeado `<:footer>` para introduzi-lo dentro de `<DModal>`. Ao usar qualquer bloco nomeado, o conteúdo do corpo deve ser envolto em `<:body></:body>`. Por exemplo:

```hbs
<DModal @closeModal={{@closeModal}}>
  <:body>
    Hello world, this is the content of the modal
  </:body>
  <:footer>
    This is the footer content. A `.modal-footer` wrapper will be added
    automatically
  </:footer>
</DModal>

```

### Etapa 4: Atualizar os pontos de chamada do showModal

Anteriormente, os modais eram renderizados usando a API `showModal`, que aceitava uma string (o nome do controlador) e uma série de opções. Ela retornava uma instância do controlador que podia ser manipulada:

```js
import showModal from "discourse/lib/show-modal";

export default class extends Component {
  showMyModal() {
    const controller = showModal("my-modal", {
      title: "My Modal Title",
      modalClass: "my-modal-class",
      model: { topic: this.topic },
    });

    controller.set("updateTopic", this.updateTopic);
  });
}

```

Para renderizar novos Modais baseados em componentes, você deve injetar o serviço ‘modal’ (ou acessá-lo usando algo como `getOwner(this).lookup("service:modal")`) e chamar a função `show()`.

`show()` aceita uma referência para a nova classe do Componente como primeiro argumento. A única opção ainda suportada é ‘model’, que pode ser usada para passar todos os dados/ações necessários para o seu Modal.

Nenhuma referência para a instância do componente será retornada. Em vez disso, `show()` retorna uma promise que será resolvida quando o modal for fechado. A promise será resolvida com quaisquer dados que foram passados para `@closeModal`.

```js
import MyModal from "discourse/components/my-modal";
import { service } from "@ember/service";

export default class extends Component {
  @service modal;

  showMyModal() {
    this.modal.show(MyModal, {
      model: { topic: this.topic, updateTopic: this.updateTopic },
    });
  });
}

```

Alternativamente, migre para a API declarativa descrita na [documentação principal do DModal](https://meta.discourse.org/t/using-the-dmodal-api-to-render-modal-windows-aka-popups-dialogs-in-discourse/268304).

A funcionalidade das antigas opções pode ser replicada da seguinte forma:

| Opção antiga `showModal` | Solução |
| --- | --- |
| `admin` | n/a para componente - remova-o |
| `templateName` | n/a para componentes - remova-o |
| `title` | mova para `<DModal @title={{i18n "blah"}}>` |
| `titleTranslated` | mova para `<DModal @title="blah">`. Isso pode ser calculado com base em dados de `model` se necessário |
| `modalClass` | mova para `<DModal class="blah">` |
| `titleAriaElementId` | mova para `<DModal aria-labelledby="blah">` |
| `panels` | Use o bloco nomeado `<:headerBelowTitle>` para implementar abas no seu componente ([exemplo](https://github.com/discourse/discourse/pull/22164)) |
| `model` | inalterado |

## Etapa 5: Testes

Quaisquer testes devem permanecer em grande parte os mesmos. O problema mais comum é:

- Os Modais não têm mais uma classe padrão baseada em seus nomes. As classes devem ser especificadas explicitamente no template (veja o início da Etapa 3)

- O wrapper `d-modal` não persiste mais no DOM quando o modal é fechado. Para verificar se todos os modais estão fechados, use uma verificação como `assert.dom('.d-modal').doesNotExist()`

## Lucro!

Seu modal deve agora funcionar como funcionava antes. Para aproveitar ainda mais a nova API, você pode considerar substituir as chamadas `showModal` por uma estratégia declarativa, e converter seu Modal para ser um componente Glimmer.

## Exemplos

Aqui estão alguns exemplos de commits que demonstram a conversão de alguns dos modais do núcleo do Discourse para a nova API:

- [DEV: Convert share-topic modal to new component-based API - Pull Request #22154 - discourse/discourse - GitHub](https://github.com/discourse/discourse/pull/22154)

- [DEV: Convert poll modals to new component-based API - Pull Request #22164 - discourse/discourse - GitHub](https://github.com/discourse/discourse/pull/22164)

* * *

Este documento é controlado por versão - sugira alterações [no github](https://github.com/discourse/discourse/blob/main/docs/developer-guides/docs/03-code-internals/11-converting-modals.md).

* * *

1. Os Componentes Clássicos do Ember são recomendados neste guia porque forneceram o caminho de migração mais fácil a partir dos Controladores do Ember. Mas para modais simples, ou se você estiver disposto a gastar algum tempo refatorando, os componentes Glimmer modernos são a melhor escolha.

---

_[View the full topic](https://meta.discourse.org/t/converting-modals-from-legacy-controllers-to-new-dmodal-component-api/268057)._
