gecko
gecko

Reputation: 23

Angular 4 Container Element with Dynamic Content - AOT friendly

I have a backend database containing help manual topics in html format, including anchors with on click function. My Angular (4.3.5) app has a tree of topics, when a tree topic is clicked the topic's body html is got from the backend and displayed next to the topic tree in a container div.

My question is about how to display the html page in the container div. Initially I tried:

<div [innerHtml]="htmlbody | safeHtmlPipe"></div>

This solution did not fully work because the htmlbody contains anchors to other pages as:

<a (click)="loadNewPage(topicId)">display topic</a>

Angular is sanitizing (filtering out) the anchor on click handler.

I have followed many google links on this subject like Dynamically displaying elements containing HTML with (click) functions Angular 2. Also I have looked at a wrapper component as in Dynamic Injection in Angular 4. But failed to find any actual examples of working code that specifically provides loading html from a dynamic source (ex the backend). I would like to make use of the new ngComponentOutlet directive and have it AoT compatible.

Does anyone know how to achieve this?

Upvotes: 1

Views: 625

Answers (1)

gecko
gecko

Reputation: 23

Since no one answered this question, here is a working solution albeit it not the ideal it does work perfectly. And is AOT compliant.

Template:

<div id="pagecontainer" [innerHtml]="htmlbody | safeHtmlPipe"></div>

The following code replaces all dom anchor hrefs inside the pagecontainer with a click callback:

// use service to get backend html
this.db.get('getpage', pageId)
.subscribe(res => {
    // load innerHtml with page markup
    this.htmlPage = res.data;
    // short pause (for template [innerHtml] = htmlbody)
    setTimeout(() => {
        // get all achors children of the page container
        let pc = document.body.querySelector("#pagecontainer");
        let x = pc.querySelectorAll('a');
        Array.from(x).forEach(el => {
            // get the href content
            let hr = el.getAttribute('href');
            if (hr != undefined) {
                // replace href with onclick event
                el.setAttribute('href', 'onclick=\"link()\"');
                // bind click listener
                el.addEventListener('click', e => {
                    // prevent browser precessing the anchor
                    e.preventDefault();
                    // callback with original href content
                    this.link(hr);                  
                });
            }
        });
        // scroll to the first h1
        pc.getElementsByTagName('h1')[0].scrollIntoView();
        this.g.showloading = false;
    }, 100);
});

link(href) {
  // call the topic on click handler with href to load the new page
}

And for the sake of completeness, the safeHtml pipe:

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

// Ref: https://stackoverflow.com/questions/39794588/angular2-innerhtml-removes-styling

@Pipe({
  name: 'safeHtmlPipe'
})

export class safeHtmlPipe implements PipeTransform {

  constructor(private sanitizer:DomSanitizer){}

  transform(html) {         
    return this.sanitizer.bypassSecurityTrustHtml(html);
  }
}

Upvotes: 1

Related Questions