Angular: ¿Cómo añadir un script a un componente?

por Pauli

Un poco de contexto: Hace poco comencé un trabajo nuevo, con responsabilidades variadas, ayer me dieron una tarea para Angular que me tomó un poco de tiempo resolver. Quizás era algo realmente sencillo, pero siempre debemos recordar una máxima importante:

Solo porque algo sea fácil para algunos, no significa que lo sea para todos.

Hace un tiempo, los tags <script> fueron eliminados de Angular, si intentas agregar a un componente, después de la compilación no estarán allí. Esto puede ser realmente frustrante si no estás al tanto de esa información.

El problema que tenía es que era NECESARIO agregar un tag <script> ¿Por qué? por la razón más vieja del manual del programador, el cliente así lo quería/requería, así qué, buscando un rato encontré 2 posibles soluciones.

Agregar dinámicamente un tag script

Ante la duda, StackOverflow:

constructor(private elementRef:ElementRef) {};

public ngOnInit() {
  var s = document.createElement("script");
  s.type = "text/javascript";
  s.src = "http://somedomain.com/somescript";
  this.elementRef.nativeElement.appendChild(s);
}

Vamos a explicarlo linea a linea:

  • Se declara en el constructor el ElementRef, este es un wrapper que nos va a permitir manipular elementos del DOM
  • Se crea el elemento script (acá llamado s)
  • Le indicamos el tipo, en este caso "text/javascript" y luego el src del script
  • Y finalmente le insertamos este elemento (s) al elemento nativo del DOM.

Teóricamente, todo es muy simple, pero hay 2 problemas con esta implementación. En primer lugar Angular lo considera una mala práctica. En segundo lugar, no funciona cuando estamos usando SSR con Angular Universal.

La solución “ideal”.

Hace poco mi tutora me decía que no existe una solución “óptima”, pero sí una solución ideal. ¿Qué pasa si nuestra aplicación se renderiza en el servidor? ¿ y qué pasa si se debe insertar ese script a un elemento específico de toda la vista del componente? (como fue mi caso).

Es aquí donde entra en juego la clase Renderer2. Esta clase permite renderizar de forma segura elementos en el DOM que no estan permitidos en la plataforma (como en el SSR).

    constructor(
        private _renderer2: Renderer2, 
        @Inject(DOCUMENT) private _document: Document) { }

    public ngOnInit() {
        let elemento = this._document.getElementById('elementId');
        let script = this._renderer2.createElement('script');
        script.type = 'application/javascript';
        script.src = 'http://somedomain.com/somescript';
        this._renderer2.appendChild(elemento, script);
    }
  • Declaramos el Renderer2 y le “inyectamos” el documento, este sería, el DOM
  • Buscamos el elemento en donde agregaremos el script (en caso que no sea a un elemento específico si no a la vista, obviar este paso).
  • Creamos el script, esta vez usando el renderer2.
  • De igual manera definimos el type y el src.
  • Y finalmente le hacemos el appendChild del script creado al elemento.

Et voilá, esta sería nuestra solución. Si lo analizamos bien, la lección aquí es NO usar ElementRef y, en su lugar, utilizar Renderer2.

Espero que esta información sea útil para alguien. Debo confesar que me llevó unas horas comprenderlo por completo y estuve un largo rato dando vueltas 😂😂🤣

También puedes leer