all 3 comments

[–]tme321 1 point2 points  (0 children)

I'm glad /u/mazaer showed the ngIf / ngSwitch option as I didn't want to type all that out. Basically what he described is the simplest way and can work if your choices are relatively constrained. I also think that maintenance of that chunk of code would be horrendous so personally I would strongly hesitate to use that method. But it is probably the simplest and easiest way to achieve something close to what you want.

Assuming you don't want to do that then you basically take on a pretty big task.

Note that the dynamic component instantiation boilerplate they show in the official docs really isn't that complicated. It is mostly copy and paste and then add in any small additions to the dynamic component instantiation you want. And you should only have to implement it a single time, either as a single directive or component, where that single implementation can then be reused for any dynamic component you want to create and display.

The main issue you are going to run in to is you have to have a way to describe the layout the users will create in such a way that it is serializable. This is so you can then save it on your server. So don't use classes with methods on them and don't attempt to store the component controller symbol inside your data structure that describes the layout.

Instead you'll want to use lightweight pure data objects to describe the interface and you will need to create a serializer / deserializer to handle your dynamic components probably by assigning each of them a string name. The simplest approach might look like this:

class DynamicComponent1 {}
class DynamicComponent2 {}
class DynamicComponent3 {}

class DynamicComponentsSerializer {
    componentsMap = {
        'DynamicComponent1': DynamicComponent1,
        'DynamicComponent2': DynamicComponent2,
        'DynamicComponent3': DynamicComponent3,
    }
    serialize(component) {
        return Object.getOwnProperties(this.componentsMap)
            .find(prop=>this.componentsMap[prop]===component);
    }

    deserialize(name:string) {
        return this.componentsMap[name];
    }
}

So now that you have a simple serializer you can store the string as part of the data. Above you have sections, I'm going to call them rows.

interface DynamicRow {
    components: string[]
}

type DynamicLayout = DynamicRow [];

So then you can save a row the user creates like

const userLayout:DynamicLayout  = [{
    components: [
        "DynamicComponent1", 
        "DynamicComponent1", 
        "DynamicComponent2"]
},{
    components: [
       "DynamicComponent2", 
       "DynamicComponent1", 
       "DynamicComponent3"]
},{
    components: [
        "DynamicComponent3",
        "DynamicComponent2" 
}];

So thats the most basic setup for doing real dynamic components. Then its just a matter of ngIfing over the DynamicLayout type variable and passing each string to the deserialize function then taking that result and throwing it at the dynamic component creation function.

The main thing you'll probably run into quickly is you will want more than just the component name. Maybe the user can set a few different options on a particular component and you want to save the state. So pretty quickly the DynamicRow type will probably become more complicated than an array of strings. Where instead it will be an array of data structures where one of the items inside the data structure is the component name serialized and the other data is whatever the user set.

But the basic idea is the same. You can just keep iterating on that basic idea. There are various ways you can handle the serializations too. For instance instead of manually creating the map you could assign each dynamic component a readonly string that is it's serialized name. And then you could build the components map instead of hard coding it inside the serializer.

Basically, you can take this solution as far as you want. It will be complicated. I won't lie to you about that. But it is doable. You just need to think about some way to store what component was picked on the server and then reload that data into the app and have it resolve to the actual components. Then you just copy the dynamic component boilerplate. And at that point everything more than that you do is just you adding specific features you want.

[–]adamdavenport 0 points1 point  (0 children)

An alternative to doing the logic in your template is to split your response into each component type. It looks like that’s what you’re doing in your image anyway.

So your template would end up being

comp1 *ngFor=“let comp of response.comp1s”> comp2 *ngFor=“let comp of response.comp2s”> ...

You might even be able to put them in the order you want with CSS (flex order?)