Multiselect component

2 Answers 975 Views
MultiSelect
Vlad
Top achievements
Rank 1
Vlad asked on 25 Feb 2022, 11:20 AM | edited on 25 Feb 2022, 11:35 AM

We are trying to create multi select component with remote data (with paging) and virtual scroll to use it in grid menu filters. Unfortunately, there are few problems:

1) When I pass selected items from parent component everything that is not in loaded items is ignored (no tags presented) even if I set allowCustom to true. Loading entire data set is not an option for us, amount of data is too large.


2) Selected items are shown incorrect. It selects wrong items, seems the problem is in virtual scroll. Is there any possibility (any attribute?) to control selected items in our code?

3) Checkbox sometimes selects more than one item.


Code example: 
HTML:

<kendo-multiselect
  [data]="items"
  [valuePrimitive]="true"
  [virtual]="virtual"
  [filterable]="true"
  [allowCustom]="true"
  [checkboxes]="true"
  [tagMapper]="tagMapper"
  [kendoMultiSelectSummaryTag]="3"
  [popupSettings]="popupSettings"
  [loading]="isLoading"
  [autoClose]="false"
  valueField="id"
  textField="id"
  (valueChange)="onChange($event)"
  (opened)="onOpened()"
  (filterChange)="onFilterChange($event)"
>
  <ng-template kendoMultiSelectHeaderTemplate>
    <div class="k-grid-header">
      <div class="k-grid-header-wrap">
        <table class="multi-select-headers-grid">
          <thead>
            <tr>
              <th
                *ngFor="let col of columns"
                class="k-header"
                [style.width.px]="defaultColumnWidth"
              >
                {{ col.label }}
              </th>
            </tr>
          </thead>
        </table>
      </div>
    </div>
  </ng-template>
  <ng-template kendoMultiSelectItemTemplate let-item>
    <ng-container *ngFor="let col of columns">
      <ng-container *ngIf="item">
        <span class="k-cell" [style.width.px]="defaultColumnWidth">{{ item[col.propName] }}</span>
      </ng-container>
    </ng-container>
  </ng-template>
  <ng-template kendoMultiSelectGroupTagTemplate let-dataItems>
    {{ dataItems.length }} more selected
  </ng-template>
</kendo-multiselect>


TS:


  items: MultiSelectItem[] = [];
  isLoading: boolean = false;
  popupSettings: PopupSettings = { width: 500 };
  readonly defaultColumnWidth = 200;

  state: any = {
    skip: 0,
    take: 10,
  };

  virtual: VirtualizationSettings = {
    itemHeight: 36,
    pageSize: 10,
    total: 0,
  };

  private term: string;
  private stateChange = new BehaviorSubject<any>(this.state);
  private unsubscribe = new Subject();

  @Output() selectionChange: Subject<string[]> = new Subject();
  @Output() termChange: Subject<Term> = new Subject();

  @ViewChild(MultiSelectComponent, { static: false })
  multiSelect: MultiSelectComponent;

  ngOnInit(): void {
    this.stateChange
      .pipe(skip(1), debounceTime(50), takeUntil(this.unsubscribe))
      .subscribe(state => {
        this.state = state;
        this.isLoading = true;
        this.emitTermChanged();
      });

    this.dataSource
      .pipe(takeUntil(this.unsubscribe))
      .subscribe(response => this.onResponse(response));
  }

  ngOnDestroy(): void {
    this.unsubscribe.next();
  }

  ngAfterViewInit() {
    this.multiSelect.tags = this.selectedItems;
    console.log('this.multiSelect.tags: ', this.multiSelect.tags);
  }

  onChange(value) {
    this.selectedItems = value;
    this.selectionChange.next(this.selectedItems);
  }

  onOpened(): void {
    this.multiSelect.optionsList.pageChange.subscribe(state => {
      this.stateChange.next(state);
    });
  }

  onFilterChange(searchTerm: string): void {
    this.term = searchTerm;
    this.emitTermChanged();
  }

  tagMapper = (tags: any) => {
    return this.selectedItems;
  };

  private emitTermChanged() {
    this.termChange.next({
      value: this.term,
      offset: this.state.skip,
      size: this.state.take,
    });
  }

  private onResponse(response: any): void {
    if (this.virtual.total !== response.totalAmount) {
      this.items =
        response.totalAmount > response.result.length
          ? response.result.concat(new Array(response.totalAmount - this.state.take))
          : [...response.result];
    } else {
      this.items.splice(this.state.skip, this.state.take, ...response.result);
    }

    this.virtual.total = response.totalAmount;
    this.isLoading = false;
  }
}

export interface MultiSelectItem {
  id: string;
  name: string;
}



Thanks in advance,
Vlad

2 Answers, 1 is accepted

Sort by
0
Svet
Telerik team
answered on 02 Mar 2022, 08:56 AM

Hi Vlad,

Thank you for the provided details.

1) When setting a value to the MultiSelect, it is required that the options in the value are indeed present in the data of the MultiSelect. What could be done for the specific use-case scenario is to add the items that represent the value of the MultiSelect to the data array passed to the MutliSelect manually when instantiating the component. Such an approach doesn't require loading the complete data set.

2) There used to be such an issue on our side, but it was fixed with version 5.4.0 of the DropDowns package:

https://www.telerik.com/kendo-angular-ui/components/dropdowns/changelog/#2b269b05-9672-5fcc-ae29-734fc04b8fd0

Please make sure that this or a more recent version is used and let me know in case the issue persists. Thank you.

3) The described undesired behavior seems to be related to the same issue described in point 2. Thus please check if the issue persists when using the suggested version. If it does persist, then I will try to reproduce it in a sample example.

Feel free to share any further details. I am looking forward to your reply.

Regards,
Svet
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Mike
Top achievements
Rank 1
commented on 26 May 2023, 04:27 AM

The multiselect checkbox bug happens for me as well. It's only when virtual is on, and it only happens when you directly click the checkbox. Clicking the item works fine. And it doesn't happen for most checkboxes, only some, so you need a lot of options in order to find one that selects multiple. I'm also working around it by turning checkboxes off.
Evan
Top achievements
Rank 1
commented on 09 Nov 2023, 04:38 AM

Simple to repro the issue using latest samples...
a. add checkboxes to the virtualization sample

[virtual]="virtual"
[checkboxes]="{ checkOnClick: false }"

b. scroll down to the last item in the list
c. select the last checkbox
The list ends up scrolled near the top and one of the initial items gets selected incorrectly.
Let me know if I should submit a ticket.
Thanks.

Tsvetelina
Telerik team
commented on 13 Nov 2023, 03:16 PM

Hi Evan,

The checkboxes issue when using virtualization is a known bug on our side, which has been logged in our public GitHub repository:

https://github.com/telerik/kendo-angular/issues/3977

Please consider subscribing to the issue in order to track how the resolution process goes.

For bugs, please use our repository to search or report them.

Regards,
Tsvetelina
Progress Telerik
0
Nicolas
Top achievements
Rank 1
Iron
answered on 28 Feb 2023, 02:13 PM | edited on 28 Feb 2023, 02:17 PM

Hi,

I am using version 7.0.3 of the DropDowns package. The issue '2)' seems to be fixed. However, I still faced the issue '3)'. It seems to be that kendo fire one event for clicking on the line ("onClick" - kendo-angular-dropdowns.js:1505) and another for clicking on the checkbox ("onCheckedChange" - kendo-angular-dropdowns.js:1669). By clicking on the line, everything works as expected.

This is only a workaround but, for now, I disabled the checkboxes in the HTML.

Regards,

Nicolas

Tags
MultiSelect
Asked by
Vlad
Top achievements
Rank 1
Answers by
Svet
Telerik team
Nicolas
Top achievements
Rank 1
Iron
Share this question
or