Logo Mark L. Reyes
Angular Material...ish

Angular Material...ish

March 23, 2025
7 min read
Table of Contents

The Challenge

Create a functional custom component (e.g. dropdown menu) using the Angular framework that matches the design exactly as shown below. Use the screenshot. No Figma available. šŸ™šŸ¼

Create a functional dropdown menu using the Angular framework that matches the design exactly as shown below.

Guidelines

  • Do not use premade UI libraries (such as Material, Bootstrap, Kendo, etc.).
  • The code must be free of bugs and render correctly to pass the test.
  • Please follow best practices and organize your code with appropriate structures. Use SCSS for styling.
  • You are welcome to use the GSAP library to animate the dropdown menu if you wish.
  • Icons (such as the ā€œarrowā€) can be handled in any way you prefer. Since you wonā€™t have the exact icon from the design, feel free to use a similar icon or handle it with a PNG.
  • Some design details (such as colors, border radius, etc.) have been intentionally left out.
  • Please use your best judgment and the methods you normally rely on to determine these implementation details.

Requirements

  1. The dropdown must utilize a two-way binding that allows you to change the selected option.
  2. If the external variable changes, the drop down should update to reflect the correct item.
  3. If the dropdown selection changes, the value of the external variable should also update.
  4. The dropdown should include a binding to set the placeholder label, which is displayed when no option is selected.
  5. It must also feature a binding to dynamically change the available selections in the dropdown list.
  6. When an option is selected in the dropdown, an event called onSelection should emit the identifier of the selected item.
  7. The demo should demonstrate how this event is captured by the parent component.
  8. If there are more than five items in the list, a scrollbar should appear to allow users to scroll through the options (This part may be challenging due to the custom design for the scrollbar).

My First Draft

Instinctively, I felt something was already amiss. But I couldnā€™t put a pin on it quite yet. Lately, my headspace has been trending more towards React with the advent of version 19, NextJS aligning to that major release, and above all else this Astro world Iā€™m currently in. Read Goodbye WordPress. Hello Astro! for more details.

I digress. My bad.

Back to the lecture at handā€¦pivoting back to the bread and butter of Angular (IMO, two-way binding) my thought process was to kick out requirements 1-5 by way of a parent-child component setup to transmit data from OptionsMenuComponent back into its parent, AppComponent.

By way of a Reactive forms approach alongside @Input and @Output decorators at my side, I figured I had a shot at emitting the selected value back to AppComponent and thatā€™d be that!

So far so goodā€¦

Thatā€™s when my spidey senses finally kicked in and where I found myself in the crossroads. To this day, this Stack Overflow thread ages very well, How to style the option of an HTML select element?, in that you canā€™t style the actual HTML option element without intervention of a library.

Whoah, whoah, whoah! Slow down, sailor. Donā€™t go npm installing just yet. Youā€™re still at the mercy of the browser and operating system, so yeaā€¦ummmā€¦!@#$%^&*!

Letā€™s end the component here and try something else.

My Renaissance

Building off of what I previously wrote I knew that Iā€™d maintain the parent-to-child relationship using the previous decorators and that the road to requirements 6-8 went through customizing the ā€œoptionā€ element.

Rather than being so committed to classic HTML form implementations using tags like form, label, input, selectand textarea I decided to run with div elements paired with a button element (heads upā€¦I fracked in portions of the screenshot to make arrows) so I could better control the styling portion. Pairing that alongside some magic available from @angular/forms, specifically ControlValueAccessor interface and NG_VALUE_ACCESSOR constant, bridged the gap between DOM elements to the Angular forms API, allowing me to keep parity to my previous efforts by emitting the value selected back to AppComponent through the onSelection method defined in that respective parent.

The Finale

Today, chefs, I prepared for you a coconut-currā€¦I mean a functional dropdown menu using the Angular framework to match the design provided by the supplied screenshot.

I decided to save the previous component as an appetizer to illustrate the stark contrast in menu display between this and the respective entrƩe.

Select menu written as a Reactive Form component & custom component then styled to look like Material Design.

Conclusion

Outside of styling the improbable, what I found to be most interesting was dabbling with deferrable views on AppComponent using blocks like @defer, @loading, @error and @placeholder. Was it necessary for a UI this small? Maybe. Maybe not. But entertaining the thought of using tools already stuffed inside of your back pocket are enticing, especially ones that may promote an initial reduction in bundle size.

Vibe Coding

Honestly, my mental cardio DIED when I couldnā€™t God mode the style palette for the original component. Thus, a sit down with GitHub CoPilot through VS Code was certainly used. That said, in order to keep Maverick from gun slinging a bit too much I stubbed in Angularā€™s pseudo-class selector called :host to target that component directly. Thatā€™s more of a placeholder for Future Mark in the event he wants to refine the CSS in a different direction.

Itā€™s not the plane, itā€™s the pilot.

ā€” Top Gun: Maverick

About Options Service

I probably couldā€™ve just mocked the data closer to the components but I wanted to fake an API call to play around with the modern-day way of dependency injection through inject() to test out the concept of deferrable views. Big thanks to Mark Thompsenā€™s Angular 17+ Fundamentals course on Frontend Masters to play around with the idea. Watch that, and itā€™ll explain why I mucked with an asynchronous ngOnInit() lifecycle hook.

Final Thoughts

Some of these tactics were courtesy of habits infused in a pre-renaissance ng-world, so a few more mental model updates are needed but itā€™s been fascinating to see how Angular v17+ has come back to let me know that theyā€™ve still got it going on as the one-stop shop for single-page web apps.

Have some fun and feel free to fork the repo or PR back to Angular 19 Custom Select Components Demo. Dependabot is enabled so dependencies are up-to-date!

View Demo

Thanks and Aloha! šŸ¤™šŸ¾