Create a highly customizable component

achtlos

thomas

Posted on November 10, 2022

Create a highly customizable component

Welcome to Angular challenges.

The aim of this series of Angular challenges is to increase your skills by practicing on real life exemples. Moreover you will have to submit your work though a PR which I or other can review; as you will do on real work project or if you want to contribute to Open Source Software.

The first challenge is to create an highly customizable component that you can reuse with any crazy ideas that the product team can come up with.

If you haven’t done the challenge yet, I invite you to try it first by going to Angular Challenges and coming back afterward to compare your solution with mine. (You can also submit a PR that I’ll review)


We are going to implement a dashboard of multiples entities. (Teacher, Student and City). A naive working implementation of Teacher card and Student Card has already been coded and we need to refactor it to make it easier to customize.

Each Card must have a background-color, image, a list of removable items and an add button.

image of student componentCard component of Student Entity

You will find below the current implementation of CardComponent and ListItemComponent

Issues:

  1. Lots of ngIf condition: this will be become harder and harder to implement new cards in the future. Each card will need a new condition.

  2. You can only pass a name as input in your ItemListComponent. What will happen if the product team decides to add an icon, or multiple properties to display, or if you have a new button? You will have to add new specific inputs and new if conditions

  3. The constructor of the component contains very specific imports. We want a generic component (called a presentational component), which doesn’t have any logic inside.

  4. Component should be set to OnPush strategy.

  5. Component is not strongly typed!


Let’s tackle each issue one by one :

Issue 1:

To delete all ifs inside my html, we will need to project the content of our image from the parent to the card component. To handle this, Angular has a tag called ng-content. (You can add a “select” attribute to be more specific on what you want to project from your parent)


We can now see that l.2–11 from our previous component has been replaced by a simple line. And on your parent component, you can simply add the img tag you want to project between your Card Component tags.

Issue 1bis and 3:

What about all the if conditions inside our component. To resolve this issue, we need to add a @Ouput() decorator to emit an event to the parent component which will take care of doing its specific action.

Issue 2:

This is the most complex part of the exercice. We could move ngFor to our student component and use ng-content to project the result. This will work but we want to keep the list pattern inside card component because we want to add (outside the scope of this exercice) generic logic and we don’t want to copy that logic in all our parent components.

We could add a ng-content inside ngFor. But this won’t work since we are missing the reference of the current item being displayed.

But Angular has us cover. NgTemplateOutlet is a directive which take a TemplateRef as input. We can then retrieve a custom template from the parent using @ContentChild() and pass it to our outlet directive. At this stage we are still missing our current items. And Angular has us cover again. We can pass a context to our custom template.

Remarks:

  • The first argument of ngTemplateOutletContext is $implicit. If you want to pass more, you must named them.
[ngTemplateOutletContext]={$implicit: item; arg2: index}
<ng-template #rowRef let-teacher let-myIndex="arg2">
Enter fullscreen mode Exit fullscreen mode
  • let-teacher in ng-template is not typed. You can add a ngTemplateContextGuard for strong typing, but this will be part of another challenge. (stay tuned!!)

  • app-list-item is still poorly customizable. But you know how to do it now. Go back to Issue 1 and apply the same strategy to your component.

Issue 4:

Now that our component only have Inputs and Outputs, there is no danger to simply add **changeDetection: ChangeDetectionStrategy.OnPush **in the decorator of our Component.

Issue 5:

To type our list of item in our component, we can use Generic.


And now our final CardComponent and ListItemComponent look like this: (This allows us to customize any dashboard card at will)

Remarks:

  • I’ve added the host property inside my decorator. Thus I can get rid of one level of encapsulation.

  • Don’t forget to import NgIf, NgFor and NgTemplateOutlet to your imports array. Or you can simply import CommonModule.


Finally, the code of StudentCard looks like below:

Remarks:

  • All the logics is now located only inside the smart component. Easy to maintain.

  • app-list-item is fully customizable. We can easily add an icon, or change the property we want to display

  • Component use the OnPush strategy since we are using the AsyncPipe to retrieve our data from the store.


I hope you enjoyed this first challenge and learned from it.

Other challenges are waiting for you at Angular Challenges. Come and try them. I’ll be happy to review you!

Follow me on Medium, Twitter or Github to read more about upcoming Challenges!

💖 💪 🙅 🚩
achtlos
thomas

Posted on November 10, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

What was your win this week?
weeklyretro What was your win this week?

November 29, 2024

Where GitOps Meets ClickOps
devops Where GitOps Meets ClickOps

November 29, 2024

How to Use KitOps with MLflow
beginners How to Use KitOps with MLflow

November 29, 2024

Modern C++ for LeetCode 🧑‍💻🚀
leetcode Modern C++ for LeetCode 🧑‍💻🚀

November 29, 2024