'foo' is not a known element when dynamically rendering HTML
'foo' is not a known element when dynamically rendering HTML
I am trying to dynamically render a string containing HTML markup in Angular. The HTML should be 'Angular compiled', i.e. including data binding and rendering of components (the sort of things I did with $compile
in AngularJS).
$compile
I have the most part of it working using p3x-angular-compile:
works as expected and correctly renders Template.Source
, i.e.:
Template.Source
this.Template.Source = '<p>Hello</p>';
and also
this.Template.Source = '<p>{{Foo}}</p>';
where Foo is a property on the bound Data
object.
Data
However, rendering my self defined angular components doesn't work:
this.Template.Source = '<app-sc-navbar></app-sc-navbar><p>Other arbitrary markup anywhere in string'</p>;
yields an error:
CompileAttribute.js:80
Error: Template parse errors: 'app-sc-navbar' is not a known element:
1. If 'app-sc-navbar' is an Angular component, then verify that it is part of this module.
2. If 'app-sc-navbar' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component
to suppress this message.
The component (ScNavbarComponent
) is part of the declarations of the app module, and - if used in static markup - works fine.
ScNavbarComponent
What am I missing here? How can I make the dynamic rendering aware of ScNavbarComponent
?
ScNavbarComponent
EDIT:
This is the full debugger output:
I have tried adding ScNavbarComponent
to exports and/or entryComponents.
ScNavbarComponent
@NgModule({
declarations: [
AppComponent,
ScNavbarComponent,
...
],
imports: [
BrowserModule,
...
CompileModule
],
entryComponents: [
ScNavbarComponent
],
providers: ,
bootstrap: [AppComponent],
exports: [
ScNavbarComponent
],
})
export class AppModule { }
ScNavbarComponent
entryComponents
AppModule
@PierreDuc Thank you! I've tried that already, but it doesn't change the behavior. I have attached more information to my question.
– Marc
Jul 3 at 10:42
What angular version are you using?
– PierreDuc
Jul 3 at 11:35
@PierreDuc Angular 6.0.3
– Marc
Jul 3 at 13:07
1 Answer
1
if you want to declare components in one module and use them in another module you need to export them so that you are able to import the module in another module.
In your app.module.ts declare and also export them so that your other module can understand that these are from another module.
@NgModule({
imports: [
BrowserModule
],
declarations: [
AppComponent,
ScNavbarComponent
],
exports: [
ScNavbarComponent
],
providers: ,
bootstrap: [AppComponent]
})
export class AppModule {
}
In your other module, you can now import the ScNavbarComponent.
If however you do not have different modules, you may just add the ScNavbarComponent to the entryComponents section of your app.module.ts. If it's not there already you can just add it like below.
entryComponents: [
ScNavbarComponent
]
EDIT:
Something you might consider doing (which might suit your goal better) is using the ComponenFactoryResolver, which allows the dynamic rendering of Angular Components. Below is an example of how this could work:
In your template you could use a template ref like:
To assign a component to this ref, in your component you should reference this using a ViewChild annotation:
@ViewChild('navbar', { ViewContainerRef }) navBar: ViewContainerRef;
Next you should inject the resolver itself in your constructor:
constructor(private resolver: ComponentFactoryResolver) {}
The resolver is now ready to be used and should be used within the lifecycle hook ngAfterContentInit as described below (make sure your component implements ngAfterContentInit):
ngAfterContentInit() {
const navBarFactory = this.resolver.resolveComponentFactory(ScNavbarComponent); // make sure to import your component
const component = this.navBar.createComponent(navBarFactory);
}
After implementing the above code, your ScNavbarComponent should be dynamically projected inside your templateRef.
I hope this helps!
Thank you very much! I've tried both approaches, but they don't change the behavior. I've attached more info to the question. Currently, I only have one module, which declares the App and the NavbarComponent. Do have more ideas? ;-)
– Marc
Jul 3 at 10:44
No problem at all! Could you double check the name of the selector of ScNavbarComponent. Are you sure it's "app-sc-navbar"?
– Arwin
Jul 3 at 14:51
I updated my answer with another option you might consider trying out. Let me know if this works for you!
– Arwin
Jul 3 at 15:05
Thank you very much! I've read about
ComponentFactoryResolver
before, but unfortunately it doesn't fit my requirement. I am developing a kind of CMS, where advanced users should be able to edit the rendered markup in an editor, where the markup can contain a couple of predefined directives. As I understand ComponentFactoryResolver
and your example, it is only suitable to create instances of specific components at runtime. I have not yet found the link to rendering a complete markup string. The example was misleading in my question (only one tag in the markup). I will edit it. THANK YOU.– Marc
Jul 3 at 15:14
ComponentFactoryResolver
ComponentFactoryResolver
Ah alright, I understand. I stumbled upon this article which seemed to fit your needs: gearheart.io/blog/…. But I'm not sure if this will also work with a complete markup string. This technique is also said te be a $compile equivalent.
– Arwin
Jul 3 at 16:26
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
I guess you should add
ScNavbarComponent
to theentryComponents
array of yourAppModule
– PierreDuc
Jul 3 at 9:14