Como acionar em cada carregamento de rodapé (ou carregamento de página?)

Estou tentando carregar um formulário JavaScript no rodapé.

Tenho isso no cabeçalho:

<script type="text/discourse-plugin" version="0.8">
  let loadScript = require("discourse/lib/load-script").default;

  api.onPageChange(() => {
    loadScript("//js.hsforms.net/forms/current.js").then(() => {
      console.log("fazendo a coisa");
      hbspt.forms.create({
        portalId: "229276",
        formId: "a86ca9cc",
        submitButtonClass: "button orange-button hubspot-button",
        target: ".subscription-form"
      });
    });
  });
</script>

E isso no rodapé:

<div class="subscription-form clearfix">
  <h5>Inscreva-se para nosso Blog</h5>
</div>

Na primeira vez que a página carrega, funciona bem. Nas páginas subsequentes (a maioria delas, pelo menos), o formulário não carrega e aparece a seguinte mensagem de erro:

Não foi possível encontrar o container de destino .subscription-form para o Formulário HubSpot a86ca9cc. O formulário não será renderizado na página.

Acho que talvez o rodapé seja carregado depois que o script é executado?

Então, preciso atrasar a execução desse script até que o rodapé seja carregado.

O arquivo plugin-api.js diz:

// Escute um `AppEvent` disparado pelo Discourse.

api.onAppEvent("inserted-custom-html", () => {
  console.log("um rodapé personalizado foi renderizado");
});

Talvez eu só precise saber qual AppEvent observar?

Há um pouco sobre isso aqui.

Javascripts targeting the footer doesn't work after page transitions - #4 by Johani

Quando você usa isso

api.onAppEvent("inserted-custom-html", () => {
  // algum código
});

você precisa especificar qual HTML personalizado deseja mirar — diferentes eventos são acionados com base nisso.

Neste caso, você quer mirar no rodapé. Pode tentar isso e ver se funciona para você?

api.onAppEvent("inserted-custom-html:footer", () => {
  // algum código
});

Haha! Funcionou! (Claro, foi depois de eu convencê-los de que o que eles estavam tentando fazer no rodapé não valia a pena desde o início). Estou super animado por ter conseguido resolver isso e agradeço sua ajuda; estou lentamente chegando a um ponto em que essas coisas fazem sentido. (E acho que agora, eles vão simplesmente deletar o rodapé por completo.)

Aha! Não tenho certeza do motivo de eu não ter pensado em pesquisar por “footer”! :man_shrugging:

Então inserted-custom-html se refere a qualquer coisa em temas? E aí eu poderia adicionar :footer ou :header ou talvez :head_tag? É essa a mágica? Sou só eu, ou

https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/app/lib/plugin-api.js#L516

seria mais útil se dissesse

   api.onAppEvent('inserted-custom-html:footer', () => {

Javascript e Ember são as coisas mais difíceis para mim de entender desde… talvez quando aprendi Lisp no meio dos anos 1980, então ainda não consigo dizer com certeza o que conta como “óbvio”.

Não exatamente. Esse evento só é disparado quando o componente ember custom-html é renderizado.

Então, onde usamos esse componente ember e o que ele faz?

Usamos isso principalmente no modelo principal da aplicação para renderizar dois campos de tema.

Aba after_header do tema

Aba footer do tema

Note que usamos isso em outros lugares, mas eles não importam neste contexto.

Perceba que o footer também tem triggerAppEvent="true".

É por isso que você pode usar:

api.onAppEvent("inserted-custom-html:footer", () => {
  // algum código
});

Você não pode fazer o mesmo com a aba after_header. O Discourse não dispara um evento para isso. Então, isso não funcionará.

api.onAppEvent("inserted-custom-html:top", () => {
  // algum código
});

Agora, por que usamos esse componente ember para renderizar alguns campos de tema?

A resposta simples é que isso nos dá muito mais controle sobre quando ele é renderizado.

Dois exemplos disso… A lista de tópicos e a página de administração.

Não queremos renderizar o footer na lista de tópicos enquanto ainda houver tópicos para serem carregados via rolagem infinita.

Também não queremos renderizar nenhum banner ou marcação de branding do site na página de administração adicionada na aba after_header.

Portanto, usar o componente ember custom-html nos dá um controle mais granular sobre coisas assim.

Agora, de volta à sua pergunta. Se há algum trabalho que você precisa fazer quando o footer é renderizado, este é o wrapper que você precisa:

api.onAppEvent("inserted-custom-html:footer", () => {
  // algum código
});

Esse método da API funciona para todos os eventos de aplicativo. Então, embora o exemplo lá não esteja realmente completo, é apenas um exemplo. Existem muitos outros AppEvents que você pode usar nesse método. Por exemplo, você pode verificar aqui.

para ver os nomes de alguns eventos que são disparados.

this.appEvents.trigger("EVENT_NAME")

e como nos conectamos a eles para fazer outras alterações:

this.appEvents.on("EVENT_NAME")

Você pode se conectar a qualquer evento que o núcleo do Discourse dispare no seu tema. Então, se eu quiser executar algum código logo antes do compositor abrir.

discourse/app/assets/javascripts/discourse/app/components/composer-editor.js at 1c38b4abf1fab8d67aaaa4b9f2810add64b709c4 · discourse/discourse · GitHub.

Posso adicionar algo assim ao meu tema:

api.onAppEvent("composer:will-open", () => {
  console.log("isso é disparado logo antes do compositor abrir");
});

Dito isso, concordo com você. Deveríamos usar um exemplo diferente para esse método no arquivo da API. Fiz uma anotação para melhorar esse exemplo.

Esta é uma ótima resposta e tão clara que até eu consegui entendê-la.

Esse problema de “como fazer algo disparar” surge com bastante frequência. Talvez as pessoas o encontrem aqui, mas sua última postagem poderia ser um tópico próprio e talvez ser adicionada ou vinculada nas guias de temas e/ou plugins.

E agora que “tema” significa principalmente “Ember” e “plugin” significa principalmente “Rails”, pode fazer sentido que alguém pense em refatorar um pouco essas seções. Parece que, quando tudo estava começando, havia uma série de coisas do Ember que exigiam um plugin e que agora podem ser feitas em um componente de tema, certo? E agora você pode fazer as coisas do Ember em um plugin ou em um componente de tema.

Ei @Johani, então, outra versão desse problema de “como acionar ou ser acionado” é que estou usando uma versão dos Custom Header Links em um plugin. Ele cria links para alguns itens (“servidores”) criados em um modelo separado que meu plugin adiciona. Quando um server é criado, quero reconstruir os links do cabeçalho para apontar para os dois servidores mais recentemente criados. Estou fazendo isso agora em um inicializador e funciona bem, exceto que, para atualizar após a adição de um servidor, você precisa recarregar a página.

Você me ensinou como adicionar e monitorar gatilhos, então pensei que poderia resolver isso, mas a página que faz o trabalho está no discourse-subscriptions. Talvez o que eu queira fazer seja enviar um PR para o discourse-subscriptions que adicione:

 this.appEvents.trigger("purchase-complete")

após a conclusão da compra (e a compra aciona a adição a um grupo, o que aciona a criação de um servidor e a remoção do usuário do grupo). Ou, se eu pudesse apenas acionar um recarregamento após a conclusão da compra ou quando o usuário clicasse em “OK” no modal de “compra concluída”, também funcionaria, mas não sei como fazer isso (o que tentei apenas recarregou a página infinitamente…)

Então, talvez aqui:

https://github.com/discourse/discourse-subscriptions/blob/main/assets/javascripts/discourse/controllers/s-show.js.es6#L71-L81

Eu queira adicionar:

 this.appEvents.trigger("successful-transaction")

após o carregamento ser definido como falso, e então eu poderia fazer com que meu inicializador adicione:

 this.appEvents.on("successful-transaction")

para fazer a manipulação do cabeçalho?

Acho que, assim que fizer isso, precisarei fazer algo diferente, pois tenho medo de que:

      api.decorateWidget("header-buttons:before", (helper) => {
        return helper.h("ul.pfaffmanager-header-links", headerLinks);
      });

adicione a header-buttons:before em vez de substituí-los, então eu teria mais links toda vez que fosse acionado?

Isso me ajudou novamente! O que eu precisava fazer era carregar um script quando o fluxo de posts fosse atualizado. Todos os exemplos que encontrei estavam em componentes que tinham acesso a appEvent através de this. Finalmente encontrei isso e agora em um apiInitializer eu posso

  api.onAppEvent('post-stream:refresh', args => {
   // do some stuff!!!
  });

E agora esses anúncios carregam quando cada lote de posts carrega. Eu já dei :heart: na sua postagem, então tive que escrever um agradecimento mais extenso. :wink:

Este é um dos meus tópicos favoritos :heart: Volto aqui de tempos em tempos :sweat_smile: Agradeço muito por isso! :hugs: