Render HTML snippet obtained from REST server and attach it to DOM

Hello,
How can i render a html snippet that i get from a REST api, on to aurelia application DOM?

Using http client i have got the html snippet in welcome.ts (skeleton-typescript-webpack).
How do i slap a viewmodel and “process” the HTML snippet? I also need to add this to the DOM. element.appendChild?
Will templating APIs help or View binding apis? Please help.

I have to render a server side HTML, as part of a my UI that is being made with Aurelia. I want to treat some HTML as “dynamic” and render it on UI.

Any help in some direction would be appreciated. I dont want to repeat the templates on both server and client.

Thanks a lot.
Subhro.

1 Like

You can enhance Dom to be treated by Aurelia. There are multiple topics about that here in discourse. E.g Refresh bindings

There is also this docs entry https://aurelia.io/docs/fundamentals/app-configuration-and-startup#leveraging-progressive-enhancement

3 Likes

I think you will need something like this:

a template, html-fragment.html:
<template> <div innerhtml.bind="content"></div> </template>

and a view model, html-fragment.ts:

@autoinject
export class HtmlFragment {
content: string = "";

constructor(private service: ServiceThatFetchesTheHtml) { }

attached(): void {
    this.fetchHtml();
 }

private fetchHtml(): void {
    this.service.getHtml()
      .then(response => this.content = response)
      .catch(err => LogManager.getLogger("").error(err));
  }
}`

https://aurelia.io/docs/binding/basics#element-content

1 Like

Thanks, The docs says this. " Binding using the innerhtml attribute simply sets the element’s innerHTML property. The markup does not pass through Aurelia’s templating system. Binding expressions and require elements will not be evaluated." This is precisely what I want. The HTML is not just plain visual elements, there are some bindings (one way), which needs to be processed.

1 Like

Thanks a lot, I am trying this out. A couple of questions,
How to get the DOM from the HTML string? Using the ViewCompiler, etc?

const fragment = document.createRange().createContextualFragment(blob);
const eins = <EnhanceInstruction>{
        element: fragment,
        bindingContext: this.data,

    };
    this.getTemplatingEngine().enhance(eins);

This one does not work. I have set up innerhtml.bind (thanks to @lancelot316) for contents and “ref”
to get element reference, and using that too wont work.

Please help.

1 Like

So you would first Insert the String e.g via innerhtml and then enhance it. To get the set element simply make use of document.querySelector(…). You cant use ref before the element is enhanced

1 Like

Thanks a lot @zewa666, my dumbself is not able to get the bindings processed.

Here is the snippet.
welcome.ts

   async attached(){
        const response = await this.http.fetch('/');
        this.templateBlob = await response.text();//bound to innerHtml
        const templatePanelArr = document.getElementById("templatePanelId");

        this.data = {
            lessor: {
                firstName:'FN lessor',
                lastName: 'LN lesser'
            },
            lessee: {
                firstName: 'LL FN',
                lastName: 'LN LL'
            },
            address: 'NDLS',
            startDate: '12/12/2019',
            endDate: '20/03/2020'
        };


        const eins = <EnhanceInstruction>{
            element: templatePanelArr,
            container: this.container,
            bindingContext: {
                message: 'This is some instruction',
                heading: this.heading,
                ...this.data
            }
        };

       this.getTemplatingEngine().enhance(eins);


       /* const factory = this.viewCompiler.compile(templatePanelArr);
        factory.create(this.container, { enhance: true });*/

    }

welcome.html

        <div innerHtml.bind="templateBlob" id="templatePanelId">
        </div>

HTML returned by REST API.

<section>
    ${heading}
    <p>
        ${message}
    </p>
    <p>
        This is a agreement between ${lessor.firstName} ${lessor.lastName} and ${lessee.firstName} ${lessee.lastName}.
    </p>
    <p>
        Signed at property address ${address}.
    </p>
    <p>
        Valid between dates ${startDate} and ${endDate}.
    </p>
</section>

The bindings are not processed and values does not appear.
If i call the enhance on button click in the same view, something undefined is being returned.

/** Called on click of a button in welcome.html/ts. **/
doStuff() {
        const templatePanelArr = document.getElementById("templatePanelId");
        console.log('Doing stuff.....', templatePanelArr);
        const eins = <EnhanceInstruction>{
            element: templatePanelArr,
            container: this.container,
            bindingContext: {
                message: 'This is some instruction',
                heading: this.heading,
                ...this.data
            }
        };

        this.templateEngine.enhance(eins);
        console.log('Doing stuff 2.....', templatePanelArr);
    }

The console output:

<div innerhtml.bind="templateBlob" id="templatePanelId" class="au-target au-target au-target" au-target-id="21">undefined</div>

please help. Thanks a lot for your time.

1 Like

You cannot either bind innerhtml as long as that element is not processed by Aurelia. Try doing it manually by queryselecting the div and setting innerhtml.

Edit: would help If you could replicate your scenario with a Sandbox than we can look at it more easily. https://codesandbox.io/s/4xwp3neqn?from-embed
For the sake of the demo replace the fetch with a hardcoded string of your sample html

1 Like

It worked!!! Aurelia magic in action!!!
Thanks a lot @zewa666 and @lancelot316 for helping sort this out.

 this.templateBlob = await response.text();//no more bound to innerHtml
 const templatePanelArr = document.getElementById("templatePanelId");// getting the element.
 const templateFragment = document.createRange().createContextualFragment(this.templateBlob);//construct the DOM fragment from the tring.
 templatePanelArr.appendChild(templateFragment); // append it to the existing element.

this.templateEngine.enhance(eins);  //let the magic happen!!!

The data is also bound to a form in welcome.ts. The above does the job, terrific!!

Thanks a lot,
Subhro.

4 Likes

The approach with a fragment is even better. Good job :+1:

1 Like