Adding script tags in Angular component template

JavascriptAngularJson LdAngular2 Universal

Javascript Problem Overview


Angular2 removes <script> tags automatically from templates to stop people using this functionality as a "poor's man" loader.

The issue here is that script tags currently have more uses than just loading code or other script files. There is the possibility that further functionality around <script> tags will be introduced in future as well.

One current use is JSON-LD which takes the format

<script type="application/ld+json">
{
    "@context":"http://schema.org",
    "@type":"HealthClub",
    ...
}
</script>

A commonly suggested work around is to dynamically add script tags to the document via the ngAfterViewInit hook, but this is obviously not proper ng2 practice and will not work server side, which JSON-LD obviously needs to be able to do.

Are there any other workarounds that we can use to include <script> tags in angular2 templates (even if the tag is inert within the browser) or is this a case of the framework being too opinionated? What other solutions might exist if this situation can't be solved in angular2?

Javascript Solutions


Solution 1 - Javascript

Maybe a little late to the party here, but since the above answers do not work well with Angular SSR (e.g. document is not defined server-side or document.createElement is not a function), I decided to write a version that works for Angular 4+, in both server and browser context:

Component Implementation

import { Renderer2, OnInit, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

class MyComponent implements OnInit {

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

    public ngOnInit() {

		let script = this._renderer2.createElement('script');
		script.type = `application/ld+json`;
		script.text = `
			{
				"@context": "https://schema.org"
				/* your schema.org microdata goes here */
			}
		`;

		this._renderer2.appendChild(this._document.body, script);
	}
}

Service Implementation

NOTE: Services cannot use Renderer2 directly. In fact, rendering an element is supposed to be done by a Component. However, you might find yourself in situation where you want to automate the creation of JSON-LD script tags on a page. As an example, a situation could be to invoke such function on route navigation change events. Hence I decided to add a version that works in a Service context.

import { Renderer2, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

/**
 * Use a Service to automate creation of JSON-LD Microdata.
 */
class MyService {

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

	/**
	 * Set JSON-LD Microdata on the Document Body.
	 *
	 * @param renderer2 			The Angular Renderer
	 * @param data 					The data for the JSON-LD script
	 * @returns                     Void
	 */
    public setJsonLd(renderer2: Renderer2, data: any): void {

		let script = renderer2.createElement('script');
		script.type = 'application/ld+json';
		script.text = `${JSON.stringify(data)}`;

		renderer2.appendChild(this._document.body, script);
	}
}

Solution 2 - Javascript

There is no Angular2 way of adding a script tag to a template.

Using require(...) to load external scripts from the components class was mentioned as a workaround (haven't tried it myself)

To dynamically add a script tag use

constructor(private elementRef:ElementRef) {};

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

See also https://stackoverflow.com/questions/35570746/angular2-including-thirdparty-js-scripts-in-component/35570783#35570783

Solution 3 - Javascript

The following works with Angular 5.2.7:

The required imports are:

import { Inject, AfterViewInit, ElementRef } from '@angular/core';
import { DOCUMENT } from '@angular/common';

Implement AfterViewInit:

export class HeroesComponent implements AfterViewInit {

If your component is implementing more that one interfaces, separate them by comma; for example:

export class HeroesComponent implements OnInit, AfterViewInit {

Pass the below arguments to constructor:

constructor(@Inject(DOCUMENT) private document, private elementRef: ElementRef) { }

Add ngAfterViewInit method of view life-cycle:

ngAfterViewInit() {
    const s = this.document.createElement('script');
    s.type = 'text/javascript';
    s.src = '//external.script.com/script.js';
    const __this = this; //to store the current instance to call 
                         //afterScriptAdded function on onload event of 
                         //script.
    s.onload = function () { __this.afterScriptAdded(); };
    this.elementRef.nativeElement.appendChild(s);
  }

Add afterScriptAdded member function.

This function will be called after the external script is loaded successfully. So the properties or functions you want to use from external js will be accessed in the body of this function.

 afterScriptAdded() {
    const params= {
      width: '350px',
      height: '420px',
    };
    if (typeof (window['functionFromExternalScript']) === 'function') {
      window['functionFromExternalScript'](params);
    }
  }

Solution 4 - Javascript

Actually There is no Angular2 way of adding a script tag to a template. but you can do some trick first of all you will import AfterViewInit and ElementRef from angular2 like this :

import {Component,AfterViewInit,ElementRef} from 'Angular2/core';

then you will you will implement them in your class like that :

export class example1 implements AfterViewInit{}

and here is a very simple javascript dom trick you gonna do

 export class example1 implements AfterViewInit{
 ngAfterViewInit()
 {
  var s=document.createElement("script");
  s.type="text/javascript";
  s.innerHTML="console.log('done');"; //inline script
  s.src="path/test.js"; //external script
 }
}

Solution 5 - Javascript

I added js script loading dynamically with condition inside of component init life hook in next way:

    private loadChatWithScript() {
    let chatScript = document.createElement("script");
    chatScript.type = "text/javascript";
    chatScript.async = true;
    chatScript.src = "https://chat-assets.frontapp.com/v1/chat.bundle.js";
    document.body.appendChild(chatScript);

    let chatScriptConfiguration = document.createElement("script");
    chatScriptConfiguration.type = "text/javascript";
    chatScriptConfiguration.async = true;
    chatScriptConfiguration.innerHTML = "window.FCSP = 'some-key-123'";
    document.body.appendChild(chatScriptConfiguration);
}

Solution 6 - Javascript

const head = document.getElementsByTagName('head')[0];
const _js = document.createElement('script');
_js.type = 'text/javascript';
_js.appendChild(document.createTextNode('{your js code here}'));
head.appendChild(_js);

this can be placed anywhere one wishes and it's a good approach

Solution 7 - Javascript

Gentleman/Gentlewoman, please take a look.

For adding a Pixel Scripts this may save you time, This solution is easier than any elaboration:

- Only Works on First Loaded Page -

Find index.html inside your Angular project, adjust it to something like this (put your script in the script tag):

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>My Angular Application</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root></app-root>
<script>
  // Put your script here
  alert('Test me, Angular hero!');
</script>
</body>
</html>

For using a script inside an angular component template, I suggest @Nicky's answer.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIan BelcherView Question on Stackoverflow
Solution 1 - JavascriptNickyView Answer on Stackoverflow
Solution 2 - JavascriptGünter ZöchbauerView Answer on Stackoverflow
Solution 3 - JavascriptKetan YekaleView Answer on Stackoverflow
Solution 4 - JavascriptMohsen M. GalalView Answer on Stackoverflow
Solution 5 - JavascriptOleksandr YefymovView Answer on Stackoverflow
Solution 6 - JavascriptGiggsView Answer on Stackoverflow
Solution 7 - JavascriptStas SorokinView Answer on Stackoverflow