Cómo disparar en cada carga de pie (o carga de página?)

Estoy intentando cargar un formulario de JavaScript en el pie de página.

Tengo esto en la cabecera:

<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("haciendo la cosa");
      hbspt.forms.create({
        portalId: "229276",
        formId: "a86ca9cc",
        submitButtonClass: "button orange-button hubspot-button",
        target: ".subscription-form"
      });
    });
  });
</script>

Y esto en el pie de página:

<div class="subscription-form clearfix">
  <h5>Regístrate en nuestro blog</h5>
</div>

La primera vez que se carga la página, funciona correctamente. Las páginas subsiguientes (la mayoría de ellas, al menos) no logran cargar el formulario y muestran el siguiente error:

No se pudo encontrar el contenedor de destino .subscription-form para el formulario de HubSpot a86ca9cc. No se está renderizando el formulario en la página.

Creo que quizás el pie de página se carga después de que se ejecuta el script.

Por lo tanto, necesito de alguna manera retrasar la ejecución hasta que el pie de página se haya cargado.

plugin-api.js dice:

// Escuchar un `AppEvent` activado por Discourse.

api.onAppEvent("inserted-custom-html", () => {
  console.log("se ha renderizado un pie de página personalizado");
});

¿Quizás solo necesito saber qué AppEvent buscar?

Hay un poco de información sobre esto aquí:

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

Cuando usas esto:

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

Debes especificar qué HTML personalizado quieres dirigir; se disparan diferentes eventos según eso.

En este caso, quieres dirigirte al pie de página. ¿Podrías probar esto y ver si funciona para ti?

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

¡Ja! ¡Eso funcionó! (Por supuesto, fue después de convencerlos de que lo que intentaban hacer en el pie de página no valía la pena desde el principio). Estoy encantado de haberlo resuelto y agradezco tu ayuda; poco a poco estoy entendiendo de qué va todo esto. (Y creo que ahora simplemente eliminarán el pie de página por completo.)

¡Ajá! No estoy seguro de por qué no se me ocurrió buscar “footer” (pie de página) antes. :man_shrugging:

Entonces, ¿inserted-custom-html se refiere a cualquier cosa en los temas? ¿Y luego podría añadir :footer o :header o quizás :head_tag? ¿Esa es la magia? ¿Soy solo yo, o sería más útil si en lugar de eso dijera

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

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

JavaScript y Ember son lo más difícil para mí de entender desde… quizás cuando aprendí Lisp a mediados de los años 80, así que todavía no puedo determinar con certeza qué se considera “obvio”.

No exactamente. Ese evento solo se dispara cuando se renderiza el componente ember custom-html.

Entonces, ¿dónde usamos ese componente ember y qué hace?

Lo usamos principalmente en la plantilla de aplicación principal para renderizar dos campos del tema.

Pestaña after_header del tema

Pestaña de pie de página del tema

Nota que lo usamos en otros lugares, pero no son relevantes en este contexto.

Fíjate que el pie de página también tiene triggerAppEvent="true".

Por eso puedes usar:

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

No puedes hacer lo mismo con la pestaña after_header. Discourse no dispara un evento para eso. Así que esto no funcionará.

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

Ahora, ¿por qué usamos ese componente ember para renderizar algunos campos del tema?

La respuesta simple es que nos da mucho más control sobre cuándo se renderiza.

Dos ejemplos de esto… La lista de temas y la página de administración.

No queremos renderizar el pie de página en la lista de temas mientras aún haya temas por cargar mediante desplazamiento infinito.

Tampoco queremos renderizar ningún banner o marcado de marca del sitio en la página de administración agregado en la pestaña after_header.

Así que, usar el componente ember custom-html nos da un control más granular sobre cosas como esas.

Ahora, volviendo a tu pregunta. Si hay algún trabajo que necesites hacer cuando se renderiza el pie de página, este es el contenedor que necesitas:

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

Ese método de API funciona para todos los eventos de la aplicación. Así que, aunque el ejemplo allí no esté realmente completo, es solo un ejemplo. Hay muchos otros AppEvents que puedes usar en ese método. Por ejemplo, puedes revisar aquí:

para ver los nombres de algunos eventos que se disparan.

this.appEvents.trigger("EVENT_NAME")

y cómo nos conectamos a ellos para hacer otros cambios:

this.appEvents.on("EVENT_NAME")

Puedes conectarte a cualquier evento que el núcleo de Discourse dispare en tu tema. Así que, si quiero ejecutar algún código justo antes de que se abra el editor de respuesta.

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

Puedo agregar algo como esto a mi tema:

api.onAppEvent("composer:will-open", () => {
  console.log("esto se dispara justo antes de que se abra el editor de respuesta");
});

Dicho esto, estoy de acuerdo contigo. Deberíamos usar un ejemplo diferente para ese método en el archivo de la API. He tomado nota para mejorar ese ejemplo.

Esta es una excelente respuesta y está tan clara que incluso yo pude entenderla.

Este tema de “cómo hacer que algo se active” surge con bastante frecuencia. Quizás la gente lo encuentre aquí, pero tu último post podría ser un tema independiente y quizás añadirse o enlazarse en las guías de temas y/o complementos.

Y ahora que “tema” significa mayormente “Ember” y “complemento” significa mayormente “Rails”, podría tener sentido que alguien piense en refactorizarlos un poco. Parece que cuando todo apenas estaba comenzando, había un montón de cosas de Ember que requerían un complemento, pero que ahora se pueden hacer en un componente de tema, ¿verdad? Y ahora puedes hacer las cosas de Ember en un complemento o en un componente de tema.

¡Hola @Johani! Otra variante de este problema de “cómo activar o ser activado” es que estoy usando una versión de Custom Header Links en un plugin. Este enlaza a algunos elementos (“servidores”) creados en un modelo separado que agrega mi plugin. Cuando se crea un server, quiero reconstruir los enlaces del encabezado para que apunten a los dos servidores creados más recientemente. Lo estoy haciendo ahora en un inicializador y funciona bien, excepto que para que se actualice después de agregar un servidor, tienes que recargar la página.

Me dijiste cómo agregar y observar triggers, así que pensé que podría resolverlo, pero la página que realiza el trabajo está en discourse-subscriptions. Quizás lo que quiero hacer es enviar un PR a discourse-subscriptions que agregue un:

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

después de que se complete una compra (y la compra active la adición a un grupo, lo que active la creación de un servidor y la eliminación del usuario del grupo). O, si pudiera simplemente activar una recarga después de que se complete la compra, o cuando hagan clic en “Aceptar” en el modal de “compra completada”, también me serviría, pero no sé cómo hacerlo (lo que intenté recargó la página infinitamente…).

Así que tal vez aquí:

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

Quisiera agregar un:

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

después de que loading se establezca en false, y luego mi inicializador podría agregar un:

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

para manipular el encabezado.

Creo que una vez que haga eso, necesitaré hacer algo diferente porque temo que:

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

agregue a header-buttons:before en lugar de reemplazarlos, por lo que obtendré más enlaces cada vez que se ejecute.

¡Esto me ha ayudado de nuevo! Lo que necesitaba hacer era cargar un script cuando se actualizaba el flujo de publicaciones. Todos los ejemplos que encontré estaban en componentes que tenían acceso a appEvent a través de this. Finalmente encontré esto y ahora en un apiInitializer puedo

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

Y ahora esos anuncios se cargan cuando se carga cada lote de publicaciones. Ya le di :heart: a tu publicación, así que tuve que escribir un agradecimiento más extenso. :wink:

¡Este es uno de mis temas favoritos :heart: Vuelvo aquí de vez en cuando :sweat_smile: ¡Realmente lo aprecio! :hugs: