How to Create a Custom Select Dropdown with Angular

Diogo MachadoDiogo Machado
3 min read

At some point in your career as a front-end developer, you will likely need to build a custom component. In this article, I will share my experience and provide examples to help you tackle this challenge and achieve your goal.

Why custom?

The reasons can vary, but the main one is that the design system doesn't match the native <select>. In practice, the <select> is often difficult to customize, so we need to create our own. Alright, let's go, but how many variations can a simple component have? In my case, the answer is shown in the image below:

Architecture and organization

One good decision was to create a central component and extends all derivations from that component, since Angular is a Typescript framework based, I can easily extends a class.

The select.component.html:

Example of the select.component.ts:

export class SelectComponent implements OnChanges, OnDestroy {
    @Input() invalid = false;
    @Input() multiple = false;
    @Input() loading = false;
    ...
}

The variation only extends the parent:

export class SelectSimpleComponent extends SelectComponent {
    // Exclusive logic of this variation
}

The beauty of this architecture is that you have all the common inputs and methods, and you can extend them in variations if you need to add new logic. It's a perfect example of object-oriented programming in Angular with TypeScript and JavaScript.

Automatically display content with Overlay from Angular CDK

To display the content inside the select when the user clicks, we could build it using only HTML, CSS, and JavaScript. However, there's a challenge when dealing with dialogs. You might encounter a problem like this:

To solve this problem, we can use a reliable solution: the Overlay API from Angular Material CDK. Overlay is great because it solves almost all your issues by isolating the component outside of the HTML block.

Problem solved! When the user clicks to open, we create an Overlay and attach it to a TemplatePortal. The dropdownContent is the ID used in the template in select.component.html.

@ViewChild('dropdownContent') dropdownTpl!: TemplateRef<any>;

Here is the code to create the Overlay into a TemplatePortal:

private createOverlay() {
    if (this.overlayRef) {
      return;
    }

    const positionStrategy = this.overlay
      .position()
      .flexibleConnectedTo(this.elementRef)
      .withFlexibleDimensions(false)
      .withPush(false)
      .withPositions([
        {
          originX: 'start',
          originY: 'bottom',
          overlayX: 'start',
          overlayY: 'top',
          offsetY: 4,
        },
        {
          originX: 'start',
          originY: 'top',
          overlayX: 'start',
          overlayY: 'bottom',
          offsetY: -4,
        },
      ]);

    const overlayConfig = new OverlayConfig({
      positionStrategy,
      scrollStrategy: this.overlay.scrollStrategies.block(),
      hasBackdrop: true,
      width: this.elementRef.nativeElement.offsetWidth,
      panelClass: ['select-overlay'],
    });

    this.overlayRef = this.overlay.create(overlayConfig);

    const portal = new TemplatePortal(this.dropdownTpl, this.viewContainerRef);
    this.overlayRef.attach(portal);
  }

To close the content:

private closeOverlay() {
    if (this.overlayRef) {
      this.overlayRef.detach();
      this.overlayRef = null;
    }
  }

With this setup, you can create as many variations as you want. In my case, these were the components created by extending from select.

Conclusions

In times when we can use AI to assist developers, having a good architecture is important. If you don't organize from the start, your project can fail, making components hard to reuse and extend. The <select> component is a classic example of how challenging front-end development can be, whether using frameworks or pure JavaScript.

0
Subscribe to my newsletter

Read articles from Diogo Machado directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Diogo Machado
Diogo Machado