-
Notifications
You must be signed in to change notification settings - Fork 26.2k
Dynamically load template for a component #15275
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Why not just have two components then load on whatever one corresponds to the situation that you need. For example: |
I know the template can't be dynamic right now and that's a pain as I outlined. I don't want to implement two, more or hundreds of components with the same logic. |
@KarlXOL What I mean by it not being possible is that it would be impossible to make it dynamic without breaking AOT. |
@DzmitryShylovich: I know you don't want to provide this feature. But as I tried to explain all other ways for doing this, are crutches and not practicable. |
@Toxicable : If you have an example on how to encapsulate e.g. CRUD functionality in a service, I'd like to see. Even if this possible there is the need for implementing for each e.g. product a component. |
@KarlXOL I do not understand why CRUD functionality could not be implemented as a service. Probably it is the best way how to do it and a common pattern. If I understand correctly that you do not care about AOT at all and you want to do all in JIT mode with Compiler module loaded in runtime. Then you can do it right now. Look at for eaxmple at https://www.ag-grid.com/ag-grid-angular-aot-dynamic-components, https://eyalvardi.wordpress.com/2016/09/04/injecting-components-in-runtime or even http://blog.assaf.co/angular-2-harmony-aot-compilation-with-lazy-jit-2. |
@mlc-mlapis : Of course the actual data operations (new, update, etc.) are in a service. But they get triggered by user interactions (click, ...) which are part of a component. The link you are referring to, just shows how deal with multiple components. And that's the point, I don't want to have multiple components for red, green, blue. I just need one component and change the template/layout. In my feature request I've explained why |
@KarlXOL But this example is exactly what you want, yes or no (to understand your point of view)? https://eyalvardi.wordpress.com/2016/09/04/injecting-components-in-runtime Right, the clicks, ... are there but do not understand why you mention them because your components inject the service through DI (it should be a singleton) and will provide a necessary interface.
|
@mlc-mlapis This is one of the possible solutions I refer to in my request ( "dynamiccomponentloader"). The downside of this approach is
In summary this solution is to heavy-weight, very low-level, risky that id needs to adapted as angular evolves,... and it is not competitive to the lean, quick approach Angular 1 provides for this requirement. That's why I request the feature to change/load the template on the fly. The framework should deal with this low level stuff, not the business application. |
It is clear now what you mean. I can imagine that type of simplification and having so high level of functionality. It is also necessary say that this would be strictly limited only for JIT mode. Actually there is not any Another question is the priority of such functionality in timeline and the standpoint of Angular core team. |
@mlc-mlapis Thanks for your feedback. I'm happy I was able to bring the message across :-) I know from many other issues here, stackoverflow, there is a big need for this feature as it is a very common requirement in real business applications. In the past and even more in the future. |
@DzmitryShylovich I can't see from the ng-descendant proposal it will be a solution for my request. I'd recommend you rethink the angular architectural approach to make it more flexible and usable for big enterprise application scenarios. It can't be an impossible mission for the angular framework and team to load templates dynamically for a component. |
@KarlXOL Enterprise application scenarios are the reason why AOT exists. 😄 The dynamic templates are just shortcuts and a |
@mlc-mlapis I expected an answer like this. The more I'm wondering why the angular team isn't providing what is needed by enterprises. And making applications faster and cheaper is key in enterprise environment. But you confirmed this is not what you are supporting with angular 2. |
@KarlXOL Just out of curiosity, do you have any info about and how Google develops its global applications? |
@mlc-mlapis Not sure about. Is it Angular 1 or Angular 2?? |
@KarlXOL Because this isn't the only thing needed by enterprises, just because it's something you want to do doesn't mean it's something that everyone needs. Everything they have done lately is to make applications faster and easier to make applications, just take a look at the bundle reductions from v2 -> v4. cc @robwormald |
@KarlXOL No, I meant the most of |
@KarlXOL here's is a small example I made https://plnkr.co/edit/kz2XKSKWSWZhPoncDQhG?p=preview |
@Toxicable I appreciate your plunker. My implementation looks like this. This part of the application is not related to the actual problem as I explained in my initial post. @Toxicable You are right. My post was inaccurate regarding the service. |
@KarlXOL I don't get what you want then. I don't see how being able to swap out templates on the fly can be any better than using a generic setup like this. Since with this you only have to defined a component which is only 20 LOC |
@mlc-mlapis Thanks for your lessin in web development. As you mentioned it is about how google develop web frameworks. I have to admit that my request is from a perspective on how to provide business solutions for enterprises and how to utilize tools and frameworks for doing this. In addition to developing frameworks as you mentioned there arise some other requirements and needs. I'd appreciate if you would also give these kind of requirements some valuation. At the end, we all would benefit more from each other and angular 2 could be a more powerful framework. |
@Toxicable Your sample is not about swapping out templates on the fly but for providing a data access service All other implementations for swapping templates (which is not supported actually) is done by dynamic module/component creation, which is much more complicated and low-level angular implementation. |
@KarlXOL Yes I know, my sample wasn't about swapping out templates at all. You said:
This is what I did. I provided a solution to your problem, not in the way you wanted to solve it, but it's a solution none the less. However, my point remains; I don't see what you're gaining by having templates swapped in and out if it were somehow possible, in both cases you'd still have to write the template code, so what exactly do you gain for the largely extensive amount of work it would take to get even close to implementing something like this in the framework? |
@Toxicable Exact. You got the point. I only have to write the template code. All the other code remains unchanged!! Imagine the general implementation of a master data management, including your service from above. Just change the template and another table (business object) is implemented. At least this is possible in Angular 1. |
@KarlXOL And this is a problem in reality if you apply that principe in a large scope. Because you can not make proper tests as a lot of code is compiled at runtime, so you can find a lot of hidden errors only at runtime. And this is unacceptable. You can play with that concept only in a small scale and still you will risc a lot of unpredictability.
|
@mlc-mlapis Sure, you are a right. But consequently you have to remove the compiler from angular 2 then, because in principle I can already achieve what I want to do. It's just way too complicated. I overstress your argument, but it includes also all kind of data (database, etc.) you are processing with an application. You can't test everything and there will always be something left to the responsibility of an application manager. |
@KarlXOL I can agree with some of your arguments. But on the other side I see also places on your side where you should re-think your application concepts simply because you will get better apps. We are probably on the end of our discussion because the standpoint of Angular core team is important from this point of view. I just wanted to express some background arguments why AOT is a prefered way now. |
I actually found a solution that works for us, the only downside is we have to disable AoT compilation for that project. import { Component, NgModule, Compiler, ViewContainerRef, Type } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { SharedModule } from '../modules/shared.module';
export class DynamicComponentHelper {
public static addComponent<ViewModelType = any>(compiler: Compiler, container: ViewContainerRef, template: string, viewModel?: ViewModelType, components: Array<Type<any>> = []): void {
@Component({ template: template })
class DynamicComponent {
public vm: ViewModelType = viewModel;
constructor() { }
}
components.push(DynamicComponent);
@NgModule({
imports: [BrowserModule, SharedModule],
declarations: components
})
class DynamicComponentModule { }
const mod = compiler.compileModuleAndAllComponentsSync(DynamicComponentModule);
const factory = mod.componentFactories.find((comp) =>
comp.componentType === DynamicComponent
);
const component = container.createComponent(factory);
}
} I then have a component calling it like so... export interface VM { text: string; }
@Component({
selector: 'app-component',
template: `<ng-template #dynamicComponent></ng-template>`
...
})
export class VariationsComponent implements OnInit, AfterViewInit {
@ViewChild('dynamicComponent', { read: ViewContainerRef }) _container: ViewContainerRef;
private vm: VariationsComponentVM = { text: 'Hello World' }
private viewInitialized: boolean = false;
private componentTemplate: string;
constructor(private compiler: Compiler) { }
ngAfterViewInit(): void {
this.viewInitialized = true;
this.setUpDynamicComponent();
}
ngOnInit(): void {
this.httpService.getComponentTemplate().subscribe(template => {
this.componentTemplate = template;
this.setUpDynamicComponent();
});
}
private setUpDynamicComponent(): void {
if (this.viewInitialized === true && this.componentTemplate) {
DynamicComponentHelper.addComponent(this.compiler, this._container, this.componentTemplate, this.vm, [NestedComponent]);
}
}
} And then the actual template being used could be as simple as... <div>{{ vm.text }}</div>
<app-nested [input]="vm.text"></app-nested> The great part about this, is all the built in angular templating stuff works, all the directives and everything. Once again, the only downside, is you have to lose AoT. Would it be/is it possible to disable AoT at a component level? It would be nice if we could compile the majority of a project in AoT, but leave a defined one out. Right now my build commands have to be: |
@Nixon-Joseph Yep, it was my historical demo created a long time ago. Generally, you should be able to build your app in AOT mode as usual, and additionally, you have to inject JitCompiler (this theme is discussed in some Angular GitHub issues with demo code) and using it to dynamically compile ad-hoc templates. So finally you can have a combined solution of AOT + JIT together. It's still not ideal, even it's not recommended from the Angular core team, again because of the performance/security/stability reasons, but it's working. I also recommend investing in some extended possibilities of the customizable interface of components and not allow patching HTML code without any control. The JitCompiler is able to stop the use of some not valid templates from its principle, but still, it's the too vague way how to support any customers. |
@mlc-mlapis thank you for your comments. I'd love to integrate what I see in that |
@Nixon-Joseph Start with angular/angular-cli#17663, that is the actual problem with |
@mlc-mlapis I and probably many others would really appreciate a meaningful roadmap on angular features planned. |
Hi, I have a solution on my GitHub, it works also with AOT :) |
@KarlXOL I understand you. But I also think that you're partially unfair in relation to the Angular as the whole. There are certainly some complicated parts that could be designed more nicely, but I also understand very well that breaking-changes in those areas are even more problematic and should be created with super high attention. And there is also one of the principal Angular building stones, static analysis of the compiler that is in a direct position against dynamic anonymous patching of templates. All the time, many of us are thinking and looking for a clever solution that would satisfy both sides without serious and dangerous compromises. |
@mlc-mlapis I bet on angular in the past and I'd like to do so in the future. |
@CharlesElGriego thanks for that! It looks promising, and super close to where I already was. I should be able to modify my solution to match more closely to yours and try it out pretty quickly. I see you have one issue in the repo mentioning that it doesn't compile aot in production. I'll see what it does for me and proceed from there. @KarlXOL @mlc-mlapis I can definitely see both of your points here, and I appreciate the discussion being made. I will say though, I'm more on @KarlXOL's side on this one. While I fully understand and respect the attempt to make Angular as safe and stable as possible, it wouldn't hurt to have features (like the one requested), and just put them a little out of the way, with documentation that suggests against use - listing all the good reasons here. |
Will this be documented? I want to create a client side for a wordpress like allowing users to put html on a page like a blog. But it would be fun to allow some model bindings. Is this solution good or hacky? I think using angularjs would be easy. Im looking for client side rendering only to avoid server script injection. |
For anyone interested I want to explain how I solved this issue in my projects. I had the exact same issue: HTML and CSS coming in from a web API, completely dynamic. You cannot use both AOT and I went completely out of the Angular bubble since it seem there is not really any support planned for such dynamic usecases. I just implemented web components. Not Angular Elements or any other framework, just plain browser web components running everywhere. I receive the HTML and CSS together with some binding information. The web component is capable of opening up a new shadow DOM in order to isolate application and component styles. This way incoming CSS or HTML cannot break your app. In the end I am where I wanted to be: AOT + build optimizer in Angular, dynamic HTML and CSS from a web API at runtime including data binding. All that without having to pack the entire Angular compiler into the runtime. And it's a lot faster as well because I just stripped 2mb of unneeded js from the runtime. |
Hi, Do you have any example of this? Thanks |
Here you go! I currently don't have time to explain every detail so here's the short story:
export class TemplateContentWebComp extends HTMLElement {
private tplDiv: HTMLDivElement;
private tplSpans: Array<HTMLSpanElement> = new Array<HTMLSpanElement>();
public init(templateCss: string, templateHtml: string): void {
const template: HTMLTemplateElement = document.createElement('template');
const css: string = `<style>:host { flex: 1; display: flex; flex-direction: column; } .tpl { flex: 1; box-sizing: border-box; }${!String.isNullOrWhiteSpace(templateCss) ? (' ' + templateCss) : String.empty()}</style>`;
const html: string = `<div class="tpl">${!String.isNullOrWhiteSpace(templateHtml) ? (' ' + templateHtml) : String.empty()}</div>`;
template.innerHTML = `${css} ${html}`;
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
this.tplDiv = this.shadowRoot.querySelector('div.tpl');
const spans: NodeListOf<HTMLSpanElement> = this.shadowRoot.querySelectorAll(`span[data-var]`);
if (spans != null && spans.length > 0) {
spans.forEach(span => this.tplSpans.push(span));
}
}
public update(isEditable: boolean, values: Array<string>): void {
if (this.tplDiv != null) {
if (isEditable) {
this.tplDiv.removeAttribute('tpldisabled');
} else {
this.tplDiv.setAttribute('tpldisabled', '');
}
}
for (let i = 0; i < this.tplSpans.length; i++) {
this.tplSpans[i].innerHTML = values[i];
}
}
} |
Reading this comment I think we can close this issue right here, too 😞 https://github.com/angular/angular-cli/issues/17663#issuecomment-705737272 |
Hi, I have been searching for a way to load multiple templates into 1 single component for quite a while. Thanks to the post of @alarm9k I was able to find a way to do this. Ofcourse that way is only temporarily as it is not possible to launch it in production mode since it requires the compiler. So for months I have been looking around for a way to do this but in production mode. Ofcourse if you want separate stylesheets you can just add the stylesheet to the html template after you loaded it in from the database and it will work just fine (probably not the best but it works). Please don't credit me for this answer as all credits goes to the one who answered my question ofcourse. |
@BillyCottrell It also depends on what you know ahead-of-time. If you know all your templates ahead of time, you can always run a |
@SamuelMarks that is true, but this can be used when you request templates from a server since the innerHTML in the example will work async so you don't need to know the templates beforehand and even if you do you can still load them in beforehand making it only work faster since there is no need to request them from a server. It is even possible to combine it with a timer and switch template every so often. This is just an easy way to still load templates no matter what you know and you can refresh it afterwards if you want to so you can display a different template or perhaps a newer version of the template. I mean you could easily create a templating service that has your pre-known templates. Add those templates to it and if you wanted you could either select one of the templates that are known or you add a new one from the server based on your needs. |
Yeah the premade templates is the key here. If you know AOT then you can have some fun, like with the aforementioned https://github.com/SamuelMarks/ng-md-components (that let's you use markdown templates). Trivial to upgrade its dependencies and add support for multiple templates to combine together (assuming anyone cares; just indicate). |
@KarlXOL @Nixon-Joseph Funny, I had the same question. How do you solve this problem? |
still unsolved (angular 11) |
@hexoh I don't expect the angular team is going to make this available. If this is something you're going to need to require, then you can either see the comments above (there's one with a github repo that has a good example of how to accomplish this (note, in my case I had to disable aot)) or maybe see if react can work for you. React is a library, not a framework so it can be much more flexible. It wouldn't surprise me to see a solution for this already available there. |
there is nothing speical in react to solve this. Same as with angular you have to dynamically create components recursively from some json (usually you can convert html string to json). With angular you also can use innerHTML + manual |
Thank you for submitting this feature request. The team has reviewed the feature request and has decided not to move forward with it. The approach described here would introduce significant XSS concerns and does not align with the angular architectural approach. We do see the need, though, and we will discuss addressing dynamically creating components in other creative approaches that should satisfy this need in a safe and robust way. |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
I'm submitting a
[X] feature request
I have a component which provides some generic business logic. Let's say:
Depending on a condition I want to show a layout (template) which is specific for a type of product, related to the user department, etc.
Right now I can't load a template on the fly for doing this.
I have read through all the issues here and on stackoverflow for possible ways on doing this.
The most practicabel way is the ngTemplateLayout. But in real life if you have dozens of different layout types, it blows up your template file and make unmaintenable.
If you use the dynamiccomponentloader which is the most recommend way by the angular team, it adds an huge overhead of code for generating the component dynamically, even if the functionality for component and module creation is encapsulated. In addition it doesn't allow a real generic solution, because in the ngModule which has also be created dynamically you have to provide all imports, exports, providers, ... to make the component work. So if you don't want to have a huge overhead in your generic component builder, you have to implement a generic componentbuilder for each "type" of your form types. Which is not practicable and increases the danger of recoding all after the next angular release
Expected behavior
Provide a way of loading a template dynamically for a component. As it is available in Angular 1
Please don't be dogmatic on this issue and consider supporting real world requirements in Angular 2
Thanks
The text was updated successfully, but these errors were encountered: