javascriptangular

How to run an external js code in angular


Following this link I was able to load external js files as following:

import { Component, OnInit, Renderer2 } from "@angular/core";
import { ScriptService } from "@app/element-types/_services/script.service";

const scriptPath = 'https://apis.google.com/js/api.js';
declare let gapi: any;

@Component({
    selector: 'app',
    templateUrl: './app.component.html',
    standalone: true
})
export class AppComponent implements OnInit {
    constructor(
        private renderer: Renderer2,
        private scriptService: ScriptService
    ) {}

    ngOnInit() {
        const scriptElement = this.scriptService.loadJsScript(this.renderer, scriptPath);
        scriptElement.onload = () => {
            console.log('Google API Script loaded');
            gapi.load('client', 'init');
        }        
    }
}

And ScriptService.ts

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

@Injectable({
    providedIn: 'root'
})

export class ScriptService {
    constructor( @Inject(DOCUMENT) private document: Document) {}

    public loadJsScript(renderer: Renderer2, src: string): HTMLScriptElement {
        const script = renderer.createElement('script');
        script.type = 'text/javascript';
        script.src = src;
        renderer.appendChild(this.document.body, script);
        return script;
    }
}

So far so good, but I would like to change the code and instead of giving a url, I provide a script code and run it directly as follows:

const scriptPath = 'https://apis.google.com/js/api.js'; 
---> change to
const scriptPath = "alert('Hello world');";

The error that I get now is:

Uncaught SyntaxError: expected expression, got '<'

So I want to know if it is possible, to do so.

P.s. I don't want to define a js file in assets and then import it in the angular component and then call it.


Solution

  • If you want to create script element with custom code in it, you can change your service method like this:

    public loadJsScript(renderer: Renderer2, src: string): HTMLScriptElement {
        const script = renderer.createElement('script');
        script.type = 'text/javascript';
        script.text = 'code you want to insert into script element';
        renderer.appendChild(this.document.body, script);
        return script;
    }
    

    So, you can do it by removing setting src and instead set text attribute. Little about HTMLScriptElement.text attribute from docs:

    A string that joins and returns the contents of all Text nodes inside the <script> element (ignoring other nodes like comments) in tree order. On setting, it acts the same way as the Node.textContent property.

    Little note: as I will show in snippet, you can use text, textContent, innerText and innerHTML attributes to set code into script element but I would recommend to stick to the docs and use text attribute since that's it's use.

    Example:

    const script1 = document.createElement("script");
    const script2 = document.createElement("script");
    const script3 = document.createElement("script");
    const script4 = document.createElement("script");
    
    script1.text = "console.log(1);";
    script2.textContent = "console.log(2);";
    script3.innerText = "console.log(3);";
    script4.innerHTML = "console.log(4);";
    
    document.body.appendChild(script1);
    document.body.appendChild(script2);
    document.body.appendChild(script3);
    document.body.appendChild(script4);