softsimon / ngx-bootstrap-multiselect

Angular 9+ Dropdown Multiselect Bootstrap
332 stars 198 forks source link

Ability to clear the search input after user selects a value #200

Closed ravikzm closed 4 years ago

ravikzm commented 7 years ago

I am trying to clear the search text after selecting the item in list using below line

<ss-multiselect-dropdown [options]="serviceList" [(ngModel)]="serviceSelected" [texts]="myTexts" [settings]="mySettings" (ngModelChange)="clearText($event);"></ss-multiselect-dropdown>

clearText(event : any){
MultiselectDropdown.prototype.clearSearch(event);
}

angular-2-dropdown-multiselect version ^1.3.1

and getting error ERROR TypeError: event.stopPropagation is not a function at Object.MultiselectDropdown.clearSearch (dropdown.umd.min.js:1)

nicholasguyett commented 7 years ago

The prototype property in javascript is the "master copy" of a class instance, so your usage of it looks incorrect (static methods are never called via the prototype). Additionally, I don't see clearSearch as a public API, so I assume you are trying to call an internal API from your own code which is not a supported use case in this or even most libraries.

The dropdown component doesn't provide any callable public methods, so your use case probably requires a new options setup. I'll update your issue to reflect this.

nicholasguyett commented 7 years ago

This might alternatively be better implemented via an exposed api object, that would allow you to call something like "clearSearch". This alternate route would definitely require some serious thought to implement, but could make this particular use both simple and generic.

gund commented 7 years ago

We can just expose clearSearch() to be public and then while consuming:

<ss-multiselect-dropdown #dropdown
  [options]="serviceList"
  [(ngModel)]="serviceSelected"
  [texts]="myTexts"
  [settings]="mySettings"
  (ngModelChange)="dropdown.clearSearch()"
></ss-multiselect-dropdown>

NOTE: public clearSearch() method should not take any arguments for the sake of simplicity.

chris--jones commented 7 years ago

I would propose the search text be a bound property of the control so that you can both read and write the search text by updating the variable it is bound to.

e.g.

<ss-multiselect-dropdown #dropdown
  [options]="serviceList"
  [(ngModel)]="serviceSelected"
  [(ngSearchText)]="serviceSearchText"
  [texts]="myTexts"
  [settings]="mySettings"
></ss-multiselect-dropdown>

If there is desire for an api I think we need to further explore the concept of the control being in either a controlled mode or uncontrolled mode (similar to react components).

Controlled means you handle the setting and clearing through explicit code - you can append options, handle the click events (and decide to update your model or not) and clear the selection.

Uncontrolled means everything is handled through change detection and you simply update your backing models, for example clearing selection by setting your model to an empty array (this is how the control currently seems to operate.)

If we implement methods like clearSearch we risk a situation where the bound properties are out of sync with the UI (in cases where there is both a property and a method). An example of this would be if there was a clearSelection() method that updated the control to unselect the items as the model could still contain the ids of the previously selected items. Trying to keep these in sync can be problematic.

gund commented 7 years ago

@chris--jones I see what you mean, however to me that approach seems like exposing unnecessary internals of the component and will lead to increased complexity inside the component itself.

Also if component won't expose control of the search text but only the clear method - you will not have any sync issues with UI because component will know how to handle clearing and update UI accordingly.

chris--jones commented 7 years ago

I'm trying to think of a solution that covers a broad set of use cases. A clear method is all well and good now until someone wants to pull the search terms out of the control for analytics or control entry and force a particular entry format - these scenarios doesn't seem all that unreasonable to me.

Not saying we shouldn't do a clear method, but if we're going down that path we'll have to accept that it's gotta work one way or another (controlled vs uncontrolled) per area of concern (search text, options, etc) and it might open the floodgates for further enhancement requests (how come I can't call clearOptions() or clearSelections(), etc).

stankuhn commented 7 years ago

Guys, is there any workaround at the moment?

Also we have slightly different situation to think of.

We have a list of categories. Selecting that loads data into another dropdown with Types for particular category. Now if you search in types dropdown and don't find there what you want, you select differnet category. That will repopulate types dropdown with completely new options. But search remains in there which is irrelevant. So maybe an internal option for dropdown behviour "ClearSearchOnOptionsChange"=true. would be also nice. Alternativelly we can control clearing by script if we have access to clearSearch() you are suggesting. Thanks.

gund commented 7 years ago

I recently modified a bit this component for internal use to access all filter values and came up with just one additional @Output onFilter.

So I guess this would be pretty much enough to cover most of the use cases with filter if we will expose only clear method and one output when filter changes.

With this approach we will not expose component internals directly to keep it simple and developer can get limited access to update filter but full access to it's value updates.

What do you think?

stankuhn commented 7 years ago

Thanks @gund, is there clearFilter method also available now?

gund commented 7 years ago

Yes, you can call clearSearch(event: Event) method on the component instance with mocked event (can be as just empty object).

nicholasguyett commented 7 years ago

@gund, can you link to the relevant pull request in this issue so I can confirm that this issue can be closed?

gund commented 7 years ago

@nicholasguyett I guess this method was introduced long ago and is used internally by the component itself here

ravikzm commented 7 years ago

Can you please confirm the features is completed or not yet

gund commented 7 years ago

@ravikzm if calling a method will suffice - you can count as yes =) See this comment

asrinivas61 commented 6 years ago

We can clear the search input & selected items by calling clearSearch() of MultiselectDropdownComponent

The HTML template code

          <ss-multiselect-dropdown name="email"
              #emailRef
              *ngIf="myOptions.length>0"
              formControlName="optionsModel"
              [options]="myOptions"
              [texts]="myTexts"
              [settings]="mySettings"
              [(ngModel)]="optionsModel"
              (ngModelChange)="onSelectEmail($event)"
            ></ss-multiselect-dropdown>

The TypeScript code

import { Component, OnInit, ViewChild } from '@angular/core';
import { IMultiSelectOption, IMultiSelectSettings,IMultiSelectTexts } from 'angular-2-dropdown-multiselect';

export class MyComponent implements OnInit {
selectedEmails: Array<string>=[];
 mySettings: IMultiSelectSettings = {
    buttonClasses: 'btn btn-primary',
    dynamicTitleMaxItems:100,
    fixedTitle:true,
    showUncheckAll: true, 
    enableSearch:true
  };
  myTexts: IMultiSelectTexts = {};
  myForm: FormGroup;

  optionsModel: number[];
  myOptions: IMultiSelectOption[]=[];

  @ViewChild('emailRef') emailRef: any;

  constructor(
    private fb: FormBuilder,
  ) {
    this.myForm = this.fb.group({
      optionsModel: []
    });
  }

 ngOnInit() {
   this.myOptions = [
       { id: 1, name: 'sample@gmail.com' },
       { id: 2, name: 'test@gmail.com' },
       { id: 3, name: 'support@gmail.com' },
       { id: 5, name: 'support1@gmail.com' },
     ];
}

onSelectEmail(emails) {
   this.selectedEmails=emails;
   this.myForm.get("optionsModel").patchValue([]);
    this.emailRef.clearSearch();
}
}