Dynamically load HTML template in angular2
Asked Answered
P

5

47

I have created a project using angular-cli which contains AppComponent as follows:

import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';
}

And app.component.html as

<h1>
  Good Morning, {{title}}
</h1>

So when I build it with ng build it generates some files like this ./dist/main.bundle.js which contents some code as follows-

/* 586 */
/***/ function(module, exports) {

module.exports = "<h1>\n  Good Morning, {{title}}\n</h1>\n"

/***/ },
/* 587 */

That means, at the time of build the compiler/bundle-er is reading the html file and concatenating those into the generated js file.

But in my case the html is also dynamic and content-driven from server side. Lets say, instead of html, my template file is app.component.jsp and residing on some different server or folder altogether.

Also that JSP file sometimes returns <h1>Good Morning, {{title}}</h1> and sometimes <h1>Good Afternoon, {{title}}</h1> depending on current server time.

How to achieve this functionality?

What I understand is that, I need to define some kind of loader function say : loadDynamicTemplate(template_url)

and need to use that loader-function on Component decorator template property. In that case, when the main.bundle.JS is generated, it will use that function also. So in runtime angular will call this function and load the HTML by ajax and use it.

Update 1

Here I found some difference between SystemJS and Webpack . I also found we can load the HTML files in run-time if we can use SystemJS. So I believe this problem can be solved with SystemJS configuration. But for that another problem comes into play, though I believe that could be a separate question. So I posted a new question to sort it out here.

Probably if that question get solved I will try with SystemJS and then post solution here if it helps.

Psalmody answered 24/11, 2016 at 12:30 Comment(7)
This is not too common in Angular2. See #34785278Lynching
In my case all of my component will have same behavior. And it is really problematic to define all of my component as dynamic. So actually I am looking for like this. template: "<my-template url='./app.component.html'></my-template>". Here my-template could be a directive and it will load the HTML dynamically. Aslo it should keep all other functionality intact. As it should work same as templateUrl: './app.component.html'Psalmody
I understand this is not too common. Actually I have a server side content management system with Adobe AEM. Which I want to use for its great authoring feature. Since Angular2 is a great framework, I am sure there should be some way around. (Without changing so many code Just I want to serve the templates at run-time via AJAX call only). Please help.Psalmody
You can load HTML in existing components, but only plain HTML. If you it to become Angular components, directives, or data- or event binding, it has to be in the template of a compiled component.Lynching
@ParthaSarathiGhosh, did you try the ng-dynamic library ? I think it really do the job : github.com/laco0416/ng-dynamicCorse
Yes, I think it could work. Can you please answer with this. So that I can select the answer if I got success.Psalmody
Did you ever figure out this AEM and angular 2 (or 4) problem?Consternate
C
39

You could use [innerHtml] in a my-template component with something like this (I didn't test) :

@Component({
    selector: 'my-template',
    template: `
<div [innerHtml]="myTemplate">
</div>
`})
export public class MyTemplate {
    private myTemplate: any = "";
    @Input() url: string;
    constructor(http: Http) {
        http.get("/path-to-your-jsp").map((html:any) => this.myTemplate = html);
    }
}
Corse answered 7/12, 2016 at 16:35 Comment(8)
Something like Good Morning, {{title}} won't resolve {{title}} this way.Lynching
Well the good Plunker was ever here : plnkr.co/edit/uzcYiN I post an example for @Partha Sarathi GhoshCorse
How to force the default setup to serve certain html templates from the project folder?Loaning
I don't understand. You may take a look to webpack documentation : webpack.github.io/docsCorse
@EricBergman could you please explain what didn't work for you ?Corse
http.get("/path-to-your-jsp").map((html:any) => html = html.text()).subscribe(this.myTemplate = html)Welter
Angular does not resolve bindings inside [innerHTML], so if title changes it does not update correctly. What you could do is create a GreetingComponent that receives raw data from your server and handle it inside Angular.Cutch
hi, it is not working in angular 7. can you please give me some idea to accomplish this idea.Chick
C
18

To interpolate a template with some Good Morning, {{title}}, you may use Suguru Inatomi's "ng-dynamic" component.

First you have to install it :

npm install --save ng-dynamic

Then import into your NgModule :

@NgModule({
  imports: [
    ...
    DynamicComponentModule.forRoot({}),
    ...
  ],
  ...
})
export class AppModule {}    

Finally use it like this :

@Component({
  selector: 'app-root',
  template: '<div *dynamicComponent="template; context: bindings;"></div>'
})
export class AppComponent {
  bindings: any = {title: "Chuck Norris"};
  template: string = `
    <h1>Good Morning, {{title}}</h1>
  `;
  constructor(http: Http) {
    http.get("/path-to-your-jsp").map((html:string) => this.template = html); //<- You may set bindings in request headers...
  }
}

You could use components in your template by defining a SharedModule. I added a custom "my-button" with succes like in the documentation example here : https://github.com/laco0416/ng-dynamic

Corse answered 9/12, 2016 at 14:25 Comment(4)
The way of writing the code here is very complex if I have so many binding variables and methods. Here I found one more info about SystemJs and Webpack. I updated my question with the findings.Psalmody
Why do get error: Can't bind to 'dynamicComponent' since it isn't a known property of 'div' ?Selfreliance
Maybe you miss the star ? *dynamicComponentCorse
getting error on production build "TypeError: Cannot read property 'element' of undefined"Daveen
A
3

worked with angular 6

import { Component, Input } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  template: `
            <div [innerHtml]="myTemplate">
            </div>
          `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  private myTemplate: any = '';
  constructor(http: HttpClient) {
    http.get('/service-path', {responseType: 'text'}).subscribe(data => this.myTemplate = data);
  }
}
Agraffe answered 9/6, 2018 at 22:27 Comment(0)
B
2

It appears that the way to do this now is to set the responseType when you make your request. HttpClient-Requesting non-JSON data `

@Component({
  selector: 'my-template',
  template: '<div [innerHtml]="myTemplate"></div>'
})
export public class MyTemplate {
  private myTemplate: any = "";
  @Input() url: string;
  constructor(http: Http) {
    http.get("/path-to-your-jsp", { responseType: 'text' })
      .subscribe(
        (data: string) => {
          this.myTemplate = html;
        }
      );
  }
}

`

Breaker answered 15/8, 2017 at 1:24 Comment(0)
C
0

Why not have all the template in one file and display them according to the condition.. Like your ./app.component.html will look like:

<div *ngIf="isWorld" >
  <h1>  Hello World </h1>
</div>
<div *ngIf="isUniverse" >
  <h1>  Hello Universe </h1>
</div>

Any idea on its effect on build time/size?

Crudity answered 27/6, 2018 at 18:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.