Como injetar um serviço no Discourse?

Quero usar um serviço em um componente de tema que estou construindo. Se os serviços estiverem disponíveis apenas em plugins, também posso criar um plugin. O objetivo é conectar dois “plugin outlets” diferentes - pressionar um botão em um outlet e ter esse clique de botão capturado pelo outro outlet.

Como posso registrar corretamente um serviço no Discourse? Segui o tópico aqui e o código relacionado do plugin no github, mas não estou conseguindo fazer funcionar.

Acho que a peça que falta é que meu componente de tema não está reconhecendo o serviço. (Tentei em um plugin também, com o mesmo resultado).


Aqui está um código que tentei:

javascripts/discourse/services/great-stuff.js:

import Service, { inject as service } from "@ember/service";

export default class GreatStuffService extends Service {
    call() {
        console.log('você chamou')
    }
}

connectors/topic-list-after-title/sample-outlet.js.es6:

import { inject as service } from "@ember/service";

export default {
  greatStuff: service(),
  actions: {
      buttonPressInOutlet(){
         this.greatStuff.call()
     }
  }
}

Quando chamo a ação “buttonPressInOutlet()”, recebo o erro: Uncaught TypeError: Cannot read properties of undefined (reading 'call').

O que mais é necessário?

Talvez você queira ver como outros serviços são escritos. Não acho que a sintaxe x extends y funcione ainda na versão do Ember que o Discourse usa.

Além disso, talvez você queira ver a API appEvents para se comunicar entre dois componentes.

2 curtidas

O problema aqui é que o ‘connector’ não é um EmberObject, e por isso você não pode injetar coisas nele. Em vez disso, você precisará criar um Ember Component e injetá-lo lá.

Seu template ‘connector’ ficaria então parecido com

{{my-component-name}}

Aqui está um exemplo de como isso é feito no plugin whos-online:

Temos um serviço no core:

Um template connector no plugin

A definição do componente:
https://github.com/discourse/discourse-whos-online/blob/main/assets/javascripts/discourse/components/whos-online.js#L6-L7

(ou você pode usar export default Component.extend({ whosOnline: service() }) se preferir)

E o template do componente:
https://github.com/discourse/discourse-whos-online/blob/main/assets/javascripts/discourse/templates/components/whos-online.hbs

3 curtidas

Eu não tinha pensado nisso. Obrigado pela ideia!


@david: muito obrigado por esta explicação e exemplos de código. Com esses exemplos, consegui fazer funcionar clicar em um botão em um componente e fazer com que ele chame uma ação no serviço. Esse é um grande passo à frente.

Agora estou tentando resolver a outra metade - uma vez que uma ação é chamada em um serviço, fazer com que isso acione uma ação em um componente. Acho que é algo como, no serviço, importar o componente e chamar uma função nesse componente (e/ou assinar a ação no componente). Mas ainda não entendi bem a sintaxe.

Assumindo que isso esteja certo, você tem algum exemplo de sintaxe?

Chamar um método de componente de um serviço não é realmente uma boa prática, e o Ember não oferece uma maneira fácil de fazer isso. Pode haver várias instâncias de um componente, então como o serviço saberia em qual delas acionar a ação?

Dito isso, às vezes pode ser necessário fazer algo funcionar dentro das limitações do sistema de “plugin outlet” do Discourse.

Eu recomendaria o mesmo que @fzngagan - dê uma olhada em appEvents. Provavelmente é a maneira mais limpa de acionar a lógica do componente a partir de um serviço.

2 curtidas

Eu pensei que era para isso que evented servia, mas nunca o usei antes. O objetivo é apenas ter uma configuração semelhante a ipc onde

  • o usuário clica no botão no componente A
  • esse clique faz com que os dados sejam carregados no componente B

Estou mais familiarizado com o Angular, onde criar e depois assinar um serviço seria a maneira geral de fazer isso. Mas no Discourse, a melhor maneira é appEvents?

Exatamente! appEvents é um wrapper para Evented. Se você preferir usar o Evented diretamente, também está tudo bem :+1:

https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/services/app-events.js#L4

1 curtida

:slight_smile: Interessante! Obrigado pelo acompanhamento. Não tenho preferência por evented - nunca o usei antes e estava com um pouco de dificuldade com a sintaxe em um serviço do Discourse:
export default class GreatStuffService extends Service.extends(Evented, {...})
não está exatamente certo.

Também nunca usei appEvents. Consigo criar um novo appEvent em um plugin/componente de tema que eu possa então assinar? A maioria dos exemplos que encontro é sobre assinar appEvents que já estão definidos no núcleo do Discourse.

1 curtida

Sim, você consegue. Procure por appEvents.trigger.

2 curtidas