<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 "> Bitovi Blog - UX and UI design, JavaScript and Frontend development
Loading

Angular |

How ComponentFactoryResolver can unlock powerful higher-order components

Learn how ComponentFactoryResolver can help you build advanced Angular components.

Kyle Nazario

Kyle Nazario

Twitter Reddit

If you work with any component-based framework long enough, you’ll run into a problem. How do you share functionality across components?

Angular gives us many options. You can share logic in services, child components or directives. However, sometimes these options are not enough. A directive can only modify an element, a service cannot display content, and a child component should not modify its parent.

Sometimes, the best option is to build a higher-order component. A higher-order component accepts a another component as its input and “wraps” it. This is a common pattern in React and a great way to share logic across components without burdening them with extra service dependencies or logic.

With a little help from ComponentFactoryResolver, we can do this in Angular too.

Why use ComponentFactoryResolver?

Imagine you’re creating a dashboard with two tables, each with a different layout. As indicated below, the top table is for transactions, and the bottom table is for users.

Screen Shot 2022-06-21 at 1.06.48 PM

Screen Shot 2022-06-21 at 1.07.01 PM

The transactions table has checkboxes and three columns showing a number, currency and date, respectively. While the user table also has a checkbox, it displays only two additional columns, the first rendering a string and the second a currency value.

On one hand, the checkbox logic for each table is the same and should be shared. On the other, how would you reconcile two radically different row layouts?

You could toggle between them with a switch statement, but the code would messy, large, and unreadable. And that’s before adding more columns in the future.

What we should do instead is create a higher-order checkbox table component that accepts a row component class as its input. This is how simple our final API will be:

IMG_1583

ComponentFactoryResolver will let us write our checkbox logic once and dynamically render row components at runtime.

Creating the Component Using ComponentFactoryResolver and ViewContainerRef

The table will create instances of our row component and render them into a ViewContainerRefs attached to <tr>s in our template. A ViewContainerRef is a container where one or more views can be attached, resulting in a host views. A host view is a view for the component containing its data. This is how Angular creates and renders components behind the scenes.

To return to our table example, we will pass in two inputs: the row component class and an array of items to render.

Screen Shot 2022-04-19 at 7.57.44 AM-1

We will inject our row component into the ViewContainerRef of each <tr>. Each row will display one item.

Screen Shot 2022-04-19 at 7.57.29 AM-1

The ComponentFactoryResolver will resolve a factory for making rowComponent instances. We will then assign the items of the table to instance.item. Finally, using the instance of the global Angular project, we will render the row component instance onto the page.

Screen Shot 2022-04-19 at 8.23.56 AM-1

Content Projection

Our table can now render the row components needed to display users and transactions. Now we need to display the checkboxes using ngContent inside our user row component.

Screen Shot 2022-04-19 at 8.33.08 AM-1

Back in the table component, make a <td>. You will inject the checkbox component into the <td>.

Screen Shot 2022-04-19 at 8.41.16 AM-1

We will need some logic to know if the checkbox is checked.

Screen Shot 2022-04-19 at 8.42.24 AM-1

We now pass the checkbox as the second parameter for projectableNodes.

Screen Shot 2022-04-19 at 8.45.28 AM

Putting this all together, we will have this block of code.

Screen Shot 2022-04-19 at 8.46.22 AM-1

The result of all this is our table that we can plug into and use throughout our application using ComponentFactoryResolver.

IMG_1583

Using ComponentFactoryResolver in Angular 13

Angular 13 and newer no longer requires component factories. You can just inject an instance of a component class directly into a ViewContainerRef.

Screen Shot 2022-04-19 at 1.54.22 PM

Learn more

Still curious about making powerful higher-order components in Angular? Our team of Angular experts is standing by. We’re happy to help you level up your team or just build great code. Get in touch today.