Priyesh Tiwari
Priyesh Tiwari

Reputation: 341

Kendo UI angular 2 multiselect select all option

I want to have select all option in kendo UI multiselect in angular 2. Here is my html for that:

<kendo-multiselect #testSetsList [data]="testDataSets"
                             [filterable]="true"
                             [(ngModel)]="selectedTestSets">
</kendo-multiselect>

But I am not getting how to have select all option in dataset and when I click that select all item all items get filled in box.

Upvotes: 1

Views: 3487

Answers (2)

Andrei Shostik
Andrei Shostik

Reputation: 352

intro: it sucks that kendo still doesn't have such logic out of the box.

<kendo-multiselect #itemList
                   [data]="data"
                   ...
                   [tagMapper]="tagMapper"
                   (valueChange)="onChange($event)"
                   [(ngModel)]="selectedItems">
</kendo-multiselect>
  1. itemList to operate with instance of MultiSelectComponent
  2. data input data obviously
  3. tagMapper to correct applying options after select/deselect actions if you use tagMapper of course
  4. onChange to detect changes. main logic will start from there
  5. selectedItems to operate with selected items. you can use values from MultiSelectComponent via #itemList directly but let's left such workaround as workaround.
import * as _ from 'lodash';
import { Component, OnInit, ViewChild } from '@angular/core';
import { MultiSelectComponent } from '@progress/kendo-angular-dropdowns';
import { map, tap } from 'rxjs/operators';


@Component({
  selector: 'my-multiselect',
  templateUrl: './my-multiselect.component.html'
})
export class MultiselectComponent implements OnInit {
  // using in that example such type for clarity
  private selectAllOption: { key: string; value: string; } = { key: null, value: 'Select all' };

  @ViewChild('itemList') public itemList: MultiSelectComponent;

  public data: { key: string; value: string; }[];
  public selectedItems: any[];

  public ngOnInit() {
    this.service.getData()
      .pipe(
        map(data => {
          // prepending `selectAllOption` before all data. it will be like an additional item
          return _.concat([this.selectAllOption], data);
        }),
        tap(data => this.data = data),
      )
      .subscribe();
  }

  public tagMapper(tags: any[]): any[] {
    // here `this` links to `MultiSelectComponent` instance so `value` property doesn't relate to our component.
    // it was done via `this['value']` to tag selected or untag deselected values postfactum otherwise even if you will select all values not of them will be tagged. To check it you can just use `tags` instead of `this['value']`
    const selected = this['value'];

    return selected < 3 ? selected : [selected];
  }

  public onChange(data: any[]) {
    // main logic to select or deselect values
    this.selectAll(data);
    // remove `selectAllOption` from resultant selection
    data = this.prepareData();
    // emitter.emit(data)...
  }

  private selectAll(items: any[]) {
    const isSelected = !!_.find(items, { key: null });
    // unfortunately we are not able to get directly a selected item because of `valueChange` logic and neither from private properties
    // so we have to detect intersection between arrays of all currently selected items - `items` and previous items without the last selected - `this.itemList['selectedDataItems']` which we can get only from `MultiSelectComponent` instance
    const isDeselected = !!_.find(this.itemList['selectedDataItems'], { key: null });

    if (isSelected) {
      if (!isDeselected) {
        this.selectedItems = this.data.slice(); // select all
      } else if (this.data.length - items.length === 1) {
        this.selectedItems = _.filter(this.selectedItems, item => !_.isNull(item.key)); // deselect 'select all' element
      }
    } else {
      if (isDeselected) {
        this.selectedItems = []; // deselect all
      } else if (this.data.length - items.length === 1) {
        this.selectedItems = this.data.slice(); // select 'select all' element
      }
    }
  }

  private prepareData(): any[] {
    const items = [...this.selectedItems];
    _.remove(items, item => _.isNull(item.key));

    return items;
  }
}

and there is one small but maybe for someone it will be an important issue with tagMapper. I haven't found a correct solution how to exclude tagged selectAllOption. I mean that selectAllOption option should be in all data to detect select and deselect actions but during selection of that option we cannot somehow skip it in tagMapper if we do it there we will have it deselected. so we clicked on selectAllOption but eventually it deselected. for me it's not applicable but maybe it's not an issue for someone

Upvotes: 0

Bonneville
Bonneville

Reputation: 3583

You need to set the multiselect 'value' property to be the same as the 'data' property. i.e. data and value are the same, hence all fields will be selected.

In your case, the component code behind your template would contain something like this:

@ViewChild('testSetsList') public multiselect: any;

Then when you get the data, you can also set the value, thus:

testDataSets = myArrayOfData;
multiselect.value = myArrayOfData;

Upvotes: 0

Related Questions