Bootstrap dropdown height not updating with change in data - Angular

Viewed 180

I'm sorry I'm reposting this but my previous question was marked as closed due to absence of minimal reproducible code. I have attached a stackblitz link with this one.

Original question - Bootstrap search-select dropdown but now when I try to search through the list and the dropdown opens on top, the list gets reduced and floats mid-air

Please don't close this one too. This is very important for my project and I have been stuck on this one for 16 hrs.

I have created a custom search select dropdown using the bootstrap dropdown in my Angular 8 project. The functionality works perfectly but the issue lies when the dropdown opens up on top of the element. Now when I search using the search box, the list gets updated accordingly and gets shortened but it keeps floating mid-air. You can see the issue in the GIF below.

Issue GIF - https://imgur.com/a/SeMVjns

As you can see in the first case when I search, the list gets shortened and it floats mid-air. What I want is when the list gets shortened, it still appear directly above the Expert dropdown button without any space in between.

Below is my HTML code:

<div class="dropdown h-100" [ngClass]="{'statusDropdownContainer': config.src != 'unavailability'}">
    <a class="btn btn-white dropdown-toggle mb-2 statusFilterDropdown h-100 w-100 flex-middle" [ngClass]="{'btnDisable': disable, 'srcUnavailability': config.src == 'unavailability' }" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
        <span class="text-truncate">{{config.src != 'unavailability' ? config.dropdownTitle + ':' : ''}} {{selectedValue ? selectedValue : 'All'}}</span>
    </a>
    <div class="dropdown-menu w-100 pt-0" aria-labelledby="dropdownMenuButton">
        <input type="text" class="w-100 p-2 searchInput" [ngModel]="searchValue" (ngModelChange)="filterDropdown($event)" placeholder="{{config.placeholder}}">
        <a *ngFor="let option of filteredList; let i=index" class="dropdown-item pointer text-contain" [ngClass]="{'alternateBackground': i%2 == 0 }" (click)="selectValue(option.name, option.unique_code)">
            {{option.name}} 
        </a>
        <div *ngIf="filteredList.length <=0" class="text-center text-muted mt-1">No {{config.user}} found</div>
    </div>
</div>

What CSS changes do I need to make?

Stackblitz - https://stackblitz.com/edit/angular-ivy-nghkhp?file=src/app/app.component.html

Edit: I found the exact issue that's causing the problem. Bootstrap's dropdown uses popperJS to position the dropdown. PopperJS calculates the dropdown's position on the scroll event, so whenever the window scrolls, the dropdown's dimensions are recalculated (uses transform CSS). But in my case, since there is no scroll, the dimensions are not calculated and the dropdown has the same height as before the user searches something in the search box. But what I want is for the dimensions to be recalculated every time there is a change in the list's data. Any suggestions would be helpful. Thanks.

1 Answers

Bootstrap's dropdown uses popperJS to position the dropdown. PopperJS calculates the dropdown's position on the scroll event, so whenever the window scrolls, the dropdown's dimensions are recalculated (uses transform CSS).

One approach could be to give the height of the body slightly more than the viewport height and then programmatically trigger the scroll on ngModelChange

STACKBLITZ SOLUTION

You may add to CSS (You may also hide the vertical scroll-bar if not required with overflow-y: hidden;)

body {
   min-height: 101vh;
}

And you might add scroll down 1px and scroll up 1px in your filterDropdown(e) method on ngModelChange as below to achieve the desired effect.

  filterDropdown(e) {
    console.log("e in filterDropdown -------> ", e);
    window.scrollTo(window.scrollX, window.scrollY + 1);
    let searchString = e.toLowerCase();
    if (!searchString) {
      this.filteredList = this.data.slice();
      return;
    } else {
      this.filteredList = this.data.filter(
        user => user.name.toLowerCase().indexOf(searchString) > -1
      );
    }
    window.scrollTo(window.scrollX, window.scrollY - 1);
    console.log("this.filteredList indropdown -------> ", this.filteredList);
  }
Related