Reputation: 184
I am learning Angular, I have a CMS with API that returns contents of pages. pages can have short codes for forms, I have updated the API to replace short with a component selector
Sample page contents would look like
<div>bla bla bla </div>
<app-form [id]="1"></app-form>
In angular I have created FormComponent to load form accordingly, but when I get page contents with above mentioned selector I get the error
'app-form' is not a known element:.....
I did some research and found that I need some dynamic component loader but could not found any working examples as per my scenario, can any one help on how I can fix this issue
Upvotes: 2
Views: 1435
Reputation: 1415
Indeed you would have to create those components dynamically. See this plunkr for an example code to do that: https://plnkr.co/edit/kkM1aR4yPcIqeBhamoDW?p=info
Although you need a ViewContainer for Angular to know where to insert that dynamic component. Which would not work because you can't bind to innerHTML
and then change the code of the innerHTML
manually. I am not sure but I think that would mess with angulars change detection.
I had to do this a few months ago and came up with a solution. I want to mention at this point that I am not sure if there is a better solution to this problem by now. Anyway, what I did is not to create dynamic components but rather create some custom rendering with *ngIfs.
Let me explain: Your content contains tags. You decide how those tags look like.
In my case I had a tag that the user can insert wherever he wants: [galerie="key_of_gallery"]
.
So the content could look like
some normal text
<h2>Oh what a nice heading</h2>
[galerie="summer_gallery"]
text again
Now how can I render this? I would have to get something like
<div>
some normal text
<h2>Oh what a nice heading</h2>
</div>
<galerie [key]="summer_gallery"></galerie>
<div>
text again
</div>
So I created a custom component which creates this:
import { Component, Input } from '@angular/core';
@Component({
selector: 'ffam-render-key-value',
template: `<div *ngFor="let contentToRender of contentArray">
<div *ngIf="contentToRender.type==='normal'" [innerHTML]="contentToRender.value">
</div>
<galerie *ngIf="contentToRender.type==='gallery'" [key]="contentToRender.key"></galerie>
</div>`
})
export class NoeRenderKeyValueComponent{
@Input('contentArray') contentArray: any[] = [];
}
All this component needs is an array of tags which will be rendered with an *ngFor. Depending on the type of the tag either normal HTML or a component is created.
This component can be inserted like
<ffam-render-key-value [contentArray]="keyValues['_text'].arrayWithCustomTags">
</ffam-render-key-value>
To get this array of tags I have created a service with a function:
public getArrayWithCustomTags(keyValue): any[] {
let arrayWithCustomTags: any[] = [];
//check if custom Tag exists in the innerHTML
if (keyValue.value.indexOf('[galerie=') !== -1) {
//replace double quotes
keyValue.value = keyValue.value.replace(/"/g, '"');
//it exists, get array of all tags
//regex that matches all [galerie="SOME KEY"] or [someAttribute="some text here"] -> You have to change this regex to fit all your tags
let pattern = /(?:(\[galerie=\"[^\"]+\"\]))+/;
//split with regexp to get array
let arrayOfContents: string[] = keyValue.value.split(new RegExp(pattern, 'gi'));
for (let i = 0; i < arrayOfContents.length; i++) {
if (typeof arrayOfContents[i] === "undefined") {
arrayOfContents.splice(i, 1);
i--;
}
else {
let customTagToBeInserted: any = {};
if (arrayOfContents[i].indexOf('[galerie=') !== -1) {
//custom tag gallery
customTagToBeInserted.type = "gallery";
//get key only
customTagToBeInserted.key = arrayOfContents[i].replace("[galerie=\"", "").replace("\"]", "");
}
//else if some other attribute or even create a switch () {}
else {
//insert the normalHtml sanitized
customTagToBeInserted.type = "normal";
customTagToBeInserted.value = this.sanitizer.bypassSecurityTrustHtml(arrayOfContents[i]);
}
arrayWithCustomTags.push(customTagToBeInserted);
}
}
}
else {
arrayWithCustomTags.push({ type: "normal", value: this.sanitizer.bypassSecurityTrustHtml(keyValue.value)});
}
return arrayWithCustomTags;
}
This will create an array like:
[0]: {type: "normal", value:"SecureHTML"},
[1]: {type: "gallery", key:"summer_gallery"},
[2]: {type: "normal", value:"SecureHTML"},
Well I think you get the idea. If you create a whole CMS with more tags I would recommend creating a function that easily creates this whole process (regex etc.) for a tag. This example code is just meant for one tag.
The result is that the components are rendered right where the user places them. I hope this helps you.
Btw, if you have editable key value pairs for the user you might find this helpful: https://github.com/bergben/ng2-ck-editable. It's a little module I built to make any div editable using a ck-editor.
Upvotes: 1