tiberiuzuld / angular-gridster2

Angular gridster 2
https://tiberiuzuld.github.io/angular-gridster2
MIT License
1.28k stars 376 forks source link

How to dynamically add a component to this gridster? #51

Closed broodjetom closed 7 years ago

broodjetom commented 7 years ago

I am making a extensible dashboard. Depending on the data that I receive from the server, there need to be tiles created with different components in it. Because it is extensible, we cannot make a simple factory for this. An independent person needs to be able to make its own tilewidget, and be able to render that tile in the gridster by sending the right data to it.

tiberiuzuld commented 7 years ago

Hi @broodjetom , You need to use something from angular4 to dynamic init a component Try this

tiberiuzuld commented 7 years ago

Hi @broodjetom , Did the info helped ? made it work ?

broodjetom commented 7 years ago

Hi,

I tried this, but this won't work since this will add another dependency to the dashboard, where people have to manually add the components that they make to the entryComponents of the main component. This probably won't work for my implementation. Also we are receiving JSON data as a string, so how would we be able to load a component to a string without using a factory?

But I am still interested in how this works. Do you know how to pass data to the component that you load in the ngComponentOutlet? I am using microservices so I only need to pass an URL.

tiberiuzuld commented 7 years ago

Hi @broodjetom ,

Try this Should resolve your issue to load component based by a string saved in db.

Also found this blog using that ComponentFactoryResolver

broodjetom commented 7 years ago

Hi @tiberiuzuld

ComponentFactoryResolver is deprecated. I have found a way to dynamically render components with the ngComponentOutlet as follows:

<ng-container *ngComponentOutlet="getComponent(item.tag)"></ng-container>

I only have one issue with this solution. I am unable to input data into the loaded component. I have looked on the documentation but there seems to be a mistake there (#16373). I have looked into the source code and they are right about there not being a property to input an object into the loaded component.

I think you have been working with this for a while now. Do you know a solution for this? I also read about someone that setup a injector with providers but in that case the loaded component doesn't know what its provider is

tiberiuzuld commented 7 years ago

Hi @broodjetom ,

I didn't tried to load dynamic components in angular2 or greater. Found this maybe it will help you. From what I read seems to work with inputs.

Onyphlax commented 7 years ago

Hi @tiberiuzuld, To answer you with my issue, it's working but not really dynamically. In fact i juste have a ngSwitch inside the gridster-item and check what string i have so i can call different components. I'll try the ng-dynamic-component tomorrow and give you a feedback.

Onyphlax commented 7 years ago

Hi @tiberiuzuld,

I manage to make it work with ng-dynamic-component, thanks again for your help.

tiberiuzuld commented 7 years ago

Hi @Onyphlax , Good to know that is working will add in Readme.md the info

tiberiuzuld commented 7 years ago

Added here.

broodjetom commented 7 years ago

I made a possible solution as well. http://blog.tomscholten.nl/dynamically-load-components-with-data-in-angular/ Sorry for this late addition, you can close this issue now. I hope this issue will help other people as well.

adrianromanko commented 7 years ago

Loading components dynamically is not an issue. Does anyone have a sample of dynamically creating a 'gridster-item' attaching it to 'gridster' programmatically?

The current design will not allow GridsterItems component selector 'gridster-item' to be held in a separate template without 'gridster' GridsterComponent being its parent. The angular template parser will throw an error.

I would get rid of @Host and refactor it a bit to decouple Grid from GridItem and turn GridsterItemComponent to be a Directive allowing you to attach behavior anywhere. The GridsterItem directive constructor should inject Gridster.

tiberiuzuld commented 7 years ago

Hi @adrianromanko , Why would you need that ?

Thanks

adrianromanko commented 7 years ago

I have a dashboard use case where I need to split up the gridster and grid-item into separate components. grid-item will be dynamically added using resolveComponentFactory().

dashboard.ts

<dashboard>
<gridster [options]="options">
   <ng-template dashboard-host></ng-template>
</gridster>
</dashboard>

dynamicWidget.ts

<widget>
<gridster-item [item]="item">
  </gridster-item>
</widget>
pbackx commented 7 years ago

@adrianromanko I made that work using the ng-dynamic-componentmentioned above.

It's messy, but it works. The template of your dynamicWidget.ts should look something like this:

    <div style="display:none"><span #myContent><your content here></span></div>
    <ndc-dynamic [ndcDynamicComponent]="gridsterItem" [ndcDynamicInputs]="gridsterItemConfig" [ndcDynamicContent]="[[myContent]]">
    </ndc-dynamic>

And the variables:

  gridsterItem = GridsterItemComponent;
  gridsterItemConfig = {item: {x: 2, y: 0, cols: 2, rows: 1}};
ndce06 commented 6 years ago

@pbackx - can u please share working code. I tried the same way but it's throwing error - No provider for GridsterComponent....

pbackx commented 6 years ago

@ndce06 you still need a GridsterComponent somewhere in the hierarchy. In my case, this was split over two nested templates. Lets call them parent and child. The parent had the GridsterComponent defined the regular way. The child has the dynamic code I show in my example.

mkgn commented 6 years ago

I managed to get the basics of the gridster. I can add dummy widgets and play around with them. However I am stuck when trying to load different components as widgets dynamically.

What I have done up-to now is

1) I have a WidgetModule that imports all WidgetComponents such as;

    @NgModule({
    declarations: [],
    imports: [
        DynamicModule.withComponents([TicketWidgetComponent, UsageWidgetComponent])
    ],
    providers: []
    })
    export class WidgetsModule { }

2) The component that should return widgets dynamically are defined as;

@Component({
    selector: 'my-component',
    template: `<ndc-dynamic [ndcDynamicComponent]="component" [ndcDynamicInputs]="gridsterItemConfig"></ndc-dynamic>`
})
class DynamicLoaderComponent {
    gridsterItemConfig = { item: { x: 0, y: 0, cols: 1, rows: 1 } };

    //i need a click event or something that checks some input and return widgets
    component = Math.random() > 0.5 ? TicketWidgetComponent : UsageWidgetComponent;
}

3) Each widget component is defined as(these will in turn register with the above module. so all widgets are controlled by the widget Module);

    @Component({
        templateUrl: './tickets.widget.component.html'
    })

    export class TicketWidgetComponent extends AppComponentBase {}

4) The template file for above component

<gridster-item [item]="item">
content here
</gridster-item>

5) My dashboard is like;

<div> 
other stuff
<gridster class="gridster" [options]="options">
<my-component></my-component>
</gridster>
</div>

From here i am stuck Because;

1) I only need to add the widget selected by the user (Example, user want to show the [ticket count] widget). I am not sure how I should wire that up with what i have done. Ultimately we should add something to the dashboard array.

2) in the widget host each is taken from the dashboard array. So with dynamic loading of other components how this will work?

I would like to see a working dynamic grid. The code at #250 doesn't use ng-dyamic-component. A sample simulating how different widgets can be added dynamically would be great since it'll be very useful for those who are building dashboards

nathanagez commented 6 years ago

@mkgn Hi, have you solved your problem ? I'm facing the same issue.

Thank you

gethari commented 5 years ago

@adrianromanko I made that work using the ng-dynamic-componentmentioned above.

It's messy, but it works. The template of your dynamicWidget.ts should look something like this:

    <div style="display:none"><span #myContent><your content here></span></div>
    <ndc-dynamic [ndcDynamicComponent]="gridsterItem" [ndcDynamicInputs]="gridsterItemConfig" [ndcDynamicContent]="[[myContent]]">
    </ndc-dynamic>

And the variables:

  gridsterItem = GridsterItemComponent;
  gridsterItemConfig = {item: {x: 2, y: 0, cols: 2, rows: 1}};

Is it possible to share the code in stackblitz or JsFiddle ? It would help. Thanks

gethari commented 5 years ago

@adrianromanko I made that work using the ng-dynamic-componentmentioned above.

It's messy, but it works. The template of your dynamicWidget.ts should look something like this:

    <div style="display:none"><span #myContent><your content here></span></div>
    <ndc-dynamic [ndcDynamicComponent]="gridsterItem" [ndcDynamicInputs]="gridsterItemConfig" [ndcDynamicContent]="[[myContent]]">
    </ndc-dynamic>

And the variables:

  gridsterItem = GridsterItemComponent;
  gridsterItemConfig = {item: {x: 2, y: 0, cols: 2, rows: 1}};

Hi is it possible to share your code in stackblitz or JSFiddle ? Thanks

n8patty commented 5 years ago

@mkgn @NastyZ98 Hi, have you been able to find the solution to your problem? I'm having the same issues.

nathanagez commented 5 years ago

Hello @n8patty it has been a long while I didn't worked with Gridster but I succeed to did what I wanted.

Can you describe your problem ?

What I can do is to dynamically generate components from an API using a component library (components are generated as a JS Bundle and I simply interpreting the JS file in Angular). I can also contextualize components services and behaviours using providers if I have multiple instances of Gridster.

If your problem is related to one of the topics I listed above I may can help you!

Cordially. Nathan.

n8patty commented 5 years ago

I currently have 3 different widget that are loaded onto a dashboard with a componentFactory as shown below. This works fine when I load one widget, however if I want to load a second widget it will load into the same grid item as the first. I hope that makes sense.

this.dynamicPluginLoader.getComponentFactory(componentId).subscribe( componentFactory => { const ref: ComponentRef<any> = this.widgets.createComponent( componentFactory ); }

<gridster [options]="options"> <ng-container *ngFor="let item of dashboard"> <gridster-item [item]="item"> <div #widgets></div> </gridster-item> </ng-container> </gridster>

eapatel commented 1 year ago

Hi @n8patty , I'm also facing the same issue. Have you found any solution?

eapatel commented 1 year ago

I solved the problem of loading widget with it's parent grid item with ng-dynamic-component, thank you.