When using Angular 2 (Typescript), dynamically binding HTML does not apply script formatting correctly

Within my index.html file, I have the following script:

<script type="text/x-mathjax-config>
  MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}}});
</script>
<script type="text/javascript"
  src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>

When I manually add a line of HTML statically in component.html, it correctly applies the formatting from the script (see image 1).

However, when I let Angular bind the HTML dynamically (see images 2 and 3), the script within the <script> tag is not applied, resulting in plain HTML being rendered. I've tried binding through [innerHTML] and setting an #ID.

How can I achieve the nice formatting seen in image 1 with a dynamic string?

import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
import { Request } from "./Request";

@Component({
  selector: 'html-component',
  templateUrl: './html.component.html',
  styleUrls: ['./html.component.css'],
  providers: [Request]
})
export class HtmlComponent implements OnInit {  
  questionString = '';
  answerString = '';
  currentQuestion;
  type;
  @ViewChild('questionId') questionId: ElementRef;

I update the inner HTML content here:

this.request.postHtml("openLink", result)
      .then(response => {   
          this.questionId.nativeElement.innerHTML =
              response._body.replace(/{/g, '{{\'{\'}}');

      console.log(this.questionId.nativeElement.innerHTML);
      this.questionString = response._body.replace(/{/g, '{{\'{\'}}');

I don't use a real template, as most of the HTML is contained in one large file.

HTML (again):

HTML Output:

I've also attempted placing the <script> before the [innerHTML] binding, but it doesn't change anything since the <script> is already included in the index.html (the parent of this HTML page).

Answer №1

One issue arises from the fact that MathJax processes the page before Angular can complete building the template and databind properties. The solution lies in processing the math after Angular finishes its job. To achieve this, you must utilize MathJax.Hub to explicitly instruct MathJax to process and display the math string when necessary (i.e., after Angular has placed it on the page).

A recommended approach is to utilize the @ViewChild() questionId as shown below:

this.request.postHtml().then(response => {

   // select the element where the math will be displayed
   let div = this.questionId.nativeElement;

   // insert the math input string into the div. Consider using innerText before resorting to innerHTML
   div.innerText = ...;

   // process the contents of the div and render the math
   MathJax.Hub.Queue(["Typeset", MathJax.Hub, div]);
})

For more information, refer to this resource. Additionally, it is advisable to discontinue the use of cdn.mathjax.org.

Answer №2

The issue at hand is similar to what I mentioned in my previous response (MathJax triggers before Angular completes rendering the template, so a solution involves waiting for your component to load before displaying math).

This answer presents an alternative method compared to my earlier suggestion. One approach could be using a Directive to manage this task. You can apply it to any element and utilize data-binding to exhibit math within that specific element. For instance, if the directive is set up with selector [mathText], you just need to bind it to the property of the component containing the raw string:

<div [mathText]="questionString"></div>

Within the directive, MathJax will process the text and showcase the equations inside the <div>.

import {Directive, ElementRef, Input, OnChanges} from '@angular/core';

@Directive({
    selector : '[mathText]',
})
export class MathTextDirective implements OnChanges {
    constructor(public elementRef: ElementRef) {
        this.hostEl = elementRef.nativeElement; //capture the HTML element host
    }

    //Used for data binding examples: e.g., <div [mathText]="raw string">
    @Input('mathText') inputString:string;
    // host element
    private hostEl:HTMLElement;

    //let MathJax parse the host element and render the math
    render(){MathJax.Hub.Queue(['Typeset', MathJax.Hub, this.hostEl])}

    // executed when the inputString changes.
    ngOnChanges(){
        //set the input string as the innerText of the host element
        this.hostEl.innerText = this.inputString;
        this.render();
    }
}

View live demonstration

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error: Module not found '!raw-loader!@types/lodash/common/array.d.ts' or its type declarations are missing

I encountered a typescript error while building my NEXT JS application. The error message was: Type error: Cannot find module '!raw-loader!@types/lodash/common/array.d.ts' Below is the content of my tsConfig.json file: { "compilerOptions& ...

Error Encountered: Unable to locate angular/core module in Angular 2

I have encountered an issue while setting up a new Angular2 app from the quickstart folder on the Angular website. Despite following all suggested solutions, I am still facing errors. After running npm install, everything seems fine as I can see my node_mo ...

What is the best way to assign TypeScript interfaces to an Input field?

Suppose I have the following interface CardDetail: export interface CardDetail { name: string; type: string; logo?: string; What is the correct way to ensure that the @Input() decorators accept this type of data? import { CardDetail } from ' ...

Is it possible for Visual Studio Code to automatically update imports when relocating MULTIPLE TypeScript files?

(Providing an answer for better visibility and searchability.) I recently noticed that when I move a single TypeScript file in my VS Code project, it automatically updates the imports throughout the entire project. However, if I try to move multiple files ...

Leveraging Observables for Angular Drag and Drop Functionality

Struggling to use an Observable as the data source for a material drag and drop feature. Working on a kanban board with swimlanes containing movable items that need backend updates. Planning to implement socket.io for real-time item movement in the shared ...

Numerous instances of a specific custom directive in Angular2

I'm in the process of developing an Angular application, focusing on creating highly reusable code using directives (as components). <div class="container container-fluid" > <div class="row"> <table class="table table-responsive ...

A guide to accessing another component's config.ts file in Angular

After following the steps outlined in this tutorial, I successfully created a reusable modal component. However, when trying to personalize it, I encountered an issue where the modal appeared as an empty box and displayed the error ERROR TypeError: ctx_r1. ...

24-hour flatpickr timepicker

Can anyone assist me in setting my hour format to 24 hours instead of AM/PM in Angular? I've been struggling with it for the past 2 days. Below are my TypeScript and HTML code snippets: TypeScript: flatpickrOptions: any = { locale: French, enable ...

Is there a way to determine if silent login is feasible with adaljs-angular4?

I'm currently utilizing the library [email protected] for an Angular 6 project. I am attempting to achieve the following: If a silent login (login without requiring user input) with Office365 is achievable, then perform a silent login (using ...

Developing test cases for mat-autocomplete feature that customizes customer search

I am currently in the process of writing unit tests for an Angular app, specifically Angular6. One specific component that I am focusing on contains mat-autocomplete functionality. My goal is to ensure that the users are properly filtered based on the inpu ...

What is the best way to add DOM elements to an Angular 2 component while maintaining encapsulated styles?

I am currently in the process of upgrading a pre-existing Angular 2 application that heavily relies on JQuery Plugins, D3, and even some React Components (all utilizing sizzle). Instead of rewriting everything at once, I have decided to wrap certain compon ...

Next.js version 13 will now display the loading.tsx component whenever a setter function for useState() is

I am facing an issue with my client component that has a simple text field which utilizes the useState() hook. Every time I modify the text and call onChange, the loading UI defined in loading.tsx appears before the updated UI. However, this process causes ...

Combining and linking 3 RxJS Observables in TypeScript and Angular 4 without nesting to achieve dependencies in the result

I have 3 observables that need to be chained together, with the result from the first one used in the other 2. They must run sequentially and each one should wait for the previous one to complete before starting. Is there a way to chain them without nestin ...

Using Conditionals in React Props

In the process of developing a component that requires two props, inside and position, I've encountered an interesting dilemma. When inside is set to true, then position is limited to left or bottom. However, if inside is set to false, then position c ...

Error encountered: The function sns.publish is not recognized as a valid function. Consider using a mock implementation of

I've been working on mocking AWS.SNS, but I keep encountering an error. Despite referencing StackOverflow posts and making adjustments as suggested, the error persists. I have removed irrelevant portions of my code. Can anyone offer assistance? Here ...

The promise ended up on a different path

I have a function implemented to update my user on firebase, but for some reason the "then" function is never called. Any ideas on how to resolve this issue? The user's data was successfully updated in the database, and when I try to directly impleme ...

Angular Elements: integrate the Angular core just one time

Angular Elements refer to Angular components that are packaged as custom elements. Each Angular Element ships with the Angular core. If I were to include 5 Angular Element components on a standard HTML page, the page would download the Angular core 5 times ...

When null is multiplied by any number in Typescript, the result should be null, not 0

In C#, I encountered a situation: decimal? x; // number decimal? y; // null c=x*y //c returns null Contrastingly, in TypeScript: let x:number=null; let y:number=12; c=x*y //c returns 0 **How can I achieve null instead of 0 in TypeScript? I'm look ...

Show the CustomError message and HTTP status code that was raised by the server in the HttpErrorResponse

I am in the process of developing an ASP.NET Core API server paired with an Angular client. One of my main objectives is to ensure that the client can effectively capture any exceptions thrown by the server. I have implemented an HTTP Interceptor in Angula ...

Setting up roles and permissions for the admin user in Strapi v4 during the bootstrap process

This project is built using Typescript. To streamline the development process, all data needs to be bootstrapped in advance so that new team members working on the project do not have to manually insert data into the strapi admin panel. While inserting ne ...