Loading Content Dynamically

This section will cover loading the components onto the dashboard without any direct reference to the components.

To load components dynamically we need a reference to the ViewContainerRef of each items template. To get access to it, create a directive, in my case I call it “dashboardOutlet” and add ViewContainerRef to the constructor and an @Input() for the Item.

dashboard-outlet.directive.ts

Then replace the item.id expression from your dashboard.component.html with an ng-template tag. Next add the dashboard-outlet directive to the ng-template and bind the item’s data to the item directive.

dashboard.component.html

Now before we render the content components to the dashboard, we must do a couple things.

First I create a dashboardCard component and dashboardCardContainer component that each dashboard card and it’s parent container will extend which allows us to have a common set of variables and events to bind to. The DashboardCardContainer needs to have an @Input() item to hold a reference to the item itself. The idea is that the container will do all the service calls and data retrieval for the presentation component. For now these are just placeholder components.

dashboard-card.container.ts

Second we must have a place to host the components that does not exist on the dashboard. To do that create an file and export an object called dashboardCards. dashboardCards is an object that maps a string to a dashboard component. That way we can give it a string and it returns us a component for us to render. I also like to create an enum to hold a reference to each of the strings which allows us to use autocomplete when building the items on the track object.

dashboard-cards.ts

dashboard-cards.enum.ts

Using these references we can reference the content component without the dashboard having any direct references to the components in the template.

Back on the dashboard component, we need a reference to all the templates that are created from the *ngFor loops. We can achieve this by using @ViewChildren querying for the DashboardOutlet directive.

dashboard.component.ts

With this reference we can now load components onto the dashboard. We achieve this using a method I named loadContent. It takes two parameters: template with the type DashboardOutletDirective and item with the very cleverly named type Item. We then use the ComponentFactoryResolver in the loadContent method to create and render the content onto the template. I also create another method loadContents (notice the ‘s’) which loops through all the templates provided by the @ViewChildren and passes it to loadContent.

dashboard.component.ts

It is important that we tell the changeDetectorRef to detectChanges because we have updated the dom. Without this call you may get errors or unexpected results.

Finally, all we have to do to get things going on the dashboard is add items to the tracks and call loadContents in ngAfterViewInit().

dashboard.component.ts

And that’s it! You should have a fully functioning dynamic dashboard independent of the content it renders.

You can get the fully functioning example here: https://stackblitz.com/github/buttars/enterprise-dynamic-dashboard

In part 2 I will go over how to implement drag and drop while maintaining state, how to do services calls for individual cards, and how to add/remove items from the tracks at run time.

This is my first article I’ve written and it would mean a lot to me if you could give it a few claps if you liked it or learned anything. Also, I will be responding to as many questions as I can, so please if you have any, comment below.