Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autocomplete with multiple selection (MdAutocompleteTrigger customization) #5053

Open
arlowhite opened this issue Jun 9, 2017 · 14 comments
Open
Labels
area: material/autocomplete feature This issue represents a new feature or feature request rather than a bug or bug fix needs: discussion Further discussion with the team is needed before proceeding P5 The team acknowledges the request but does not plan to address it, it remains open for discussion

Comments

@arlowhite
Copy link

arlowhite commented Jun 9, 2017

Feature proposal:

It would be nice if MdAutocomplete and MdAutocompleteTrigger supported the multiple attribute similar to MdSelect, or at least configuration options that make creating your own easier.

What is the expected behavior?

It should be possible to create an Autocomplete with multiple selection.

What is the current behavior?

MdAutocompleteTrigger is coded to assume single selection and is not easily customized, which makes a custom multi-autocomplete difficult to develop. Currently, you must "monkey-patch" private methods in MdAutocompleteTrigger.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

@angular/cli: 1.0.4
node: 6.9.1
os: linux x64
@angular/common: 4.2.0
@angular/material: 2.0.0-beta.6-f89c6db
@angular/cli: 1.0.4

Is there anything else we should know?

Here are my notes on the major issues I encountered in developing my own multiple Autocomplete.

In ngAfterContentInit, I "monkey-patch" MdAutocompleteTrigger. You cannot extend MdAutocompleteTrigger because these methods are private.

The objective here is to:

  1. not deselect other MdOptions on selection
  2. leave the Autocomplete open after option selection event.

EDIT updated for beta12

if (this.multiple) {
      const self = this;

      /*
      easiest to just modify this MatAutoTrigger instance to get the behavior we want.
      Hopefully, material2 will support this in the future
       */
      const autoTrigger: any = this.mdAutoTrigger as any;

      // make a no-op so other options aren't cleared when selecting an option
      autoTrigger._clearPreviousSelectedOption = () => {};

      // need to override to continue getting these events
      // copied from material2/src/lib/autocomplete/autocomplete-trigger.ts with CHANGEs
      autoTrigger._subscribeToClosingActions = function(this: any): Subscription {
        const firstStable = first.call(this._zone.onStable.asObservable());
        const optionChanges = RxChain.from(this.autocomplete.options.changes)
          .call(doOperator, () => this._positionStrategy.recalculateLastPosition())
          // Defer emitting to the stream until the next tick, because changing
          // bindings in here will cause "changed after checked" errors.
          .call(delay, 0)
          .result();

        // When the zone is stable initially, and when the option list changes...
        return RxChain.from(merge(firstStable, optionChanges))
          // create a new stream of panelClosingActions, replacing any previous streams
          // that were created, and flatten it so our stream only emits closing events...
          .call(switchMap, () => {
            this._resetActiveItem();
            this.autocomplete._setVisibility();
            return this.panelClosingActions;
          })
          // when the first closing event occurs...
          // CHANGE disable first() because we want to continue getting events
          // .call(first)
          // set the value, close the panel, and complete.
          .subscribe(event => this._setValueAndClose(event));
      };

      // prevent closing on select option event
      autoTrigger._setValueAndClose = function(this: any, event: MatOptionSelectionChange | null): void {
        if (event && event.source) {
          // CHANGE don't clear selection, clear input, or change focus
          // this._clearPreviousSelectedOption(event.source);
          // this._setTriggerValue(event.source.value);
          this._onChange(event.source.value);
          // this._element.nativeElement.focus();
          this.autocomplete._emitSelectEvent(event.source);
        }
        // CHANGE added else clause (close non-MatOptionSelectionChange event)
        else {
          // NOTE this is the Subscription returned from _subscribeToClosingActions
          // CHANGE unsubscribe from the Subscription created in _subscribeToClosingActions
          this._closingActionsSubscription.unsubscribe();
          this.closePanel();
          // CHANGE clear input so placeholder can show selected values
          self.clearInput();
        }
      };

    }

Also, while working on this, I noticed that the code I was writing was very similar to MdSelect and most of the logic is in MdAutocompleteTrigger. So maybe what's needed is a new MdMultiAutocompleteTrigger, which uses the same SelectionModel system that MdSelect uses.

The one other issue is that even with ngFor trackBy sometimes MdOption instances lose their selected state. So every time the options are filtered, I double-check MdOption.selected and select() deselect() as necessary.

Also, the way MdSelect sets MdOption.multiple seems awkward, but I basically do the same thing as the MdSelect code.

FYI, my solution for displaying multiple values is just to clear the input, floatPlaceholder='never' and set placeholder to the displayWith() of each selected value separated by commas. This works fairly well. I also added a tooltip with the same content in case the entire text is not visible.

@arlowhite
Copy link
Author

@jelbourn Is someone planning to work on a multiple autocomplete? Or would you be open to me creating a new MdMultipleAutocompleteTrigger?

@jelbourn jelbourn added discussion feature This issue represents a new feature or feature request rather than a bug or bug fix P5 The team acknowledges the request but does not plan to address it, it remains open for discussion labels Jun 12, 2017
@jelbourn
Copy link
Member

No plans to work on this, would need some discussion to determine if we want to add it to the library. The a11y would be problematic since an autocomplete is a role="combobox" and I'm not sure if that supports multiple values.

@arlowhite
Copy link
Author

@jelbourn Don't know much about a11y, but MdSelect uses 'role': 'listbox', anything wrong with changing MdAutocomplete's role to listbox for multiple mode or does that cause problems?

@jelbourn
Copy link
Member

A listbox doesn't have a text input like autocomplete does. See https://www.w3.org/TR/wai-aria/roles#combobox

Looks like combobox theoretically supports being associated with a listbox with multiple selection, but it would need some testing; screen-readers are often "best effort" on certain boundary cases in the spec

@Aaron-Zhao
Copy link

I am also in need of this function. Either a customization or a new component "multi-search-select" will work. I would imagine something close to this, https://semantic-ui.com/modules/dropdown.html#multiple-search-selection

@bharathmuppa
Copy link

It is good to have functionality especially in corporate application where there is always a need of this component

@PawelOwczarekFalcon
Copy link

I want this :) I created own component, but good to see native implementation :)

@george-oakling
Copy link

@PawelOwczarekFalcon is your component open sourced? :)

@arlowhite
Copy link
Author

@Aaron-Zhao The Semantic UI example is functionally more like chips with an autocomplete.
There is some work on combining MatChipList and MatFormField
https://material2-dev.firebaseapp.com/chips
See this issue comment #3273 (comment)
They claim an Autocomplete should work, but I haven't tried it yet.
I'm not sure what the official status on this is.

However, if you want it to look more like Semantic UI you'll need to do some re-styling with CSS.

IMO, the advantage of Chips + Autocomplete is that you clearly see all selected values. However, it takes much more space.

The advantage of the Autocomplete+multiple hack I created is that it takes less space. However, it's a bit confusing to users. The fundamental problem is that the input text is your search AND the display of selected values (after blur), which is confusing. And there's also the accessibility issues that @jelbourn mentioned.

So I think going with Chips+Autocomplete is probably better.
For my use case, I'll just create more compact styling since my App isn't used on touch devices where the big chips are needed.

@jelbourn I'm tempted to close this issue unless someone has a design for multiple+Autocomplete that isn't equivalent to Chips+Autocomplete.

@jrood
Copy link
Contributor

jrood commented Dec 6, 2017

@arlowhite What about extending the mat-select to be searchable/filterable while multiple selection is enabled. Does that accomplish the same purpose? There's a pr open for a mat-select-header #7835 where you could add a search input to filter the options. The author of the pull requests is waiting for feedback because I think he want's to make sure @jelbourn's a11y concerns are addressed.

@singhkis14
Copy link

@jrood
Extending mat-select approach looks clean.

@irinelpascu
Copy link

irinelpascu commented May 15, 2018

Here is how i handled it https://stackblitz.com/edit/angular-v1b716
I used two form controls. One for the autocomplete and one for multiple selection which I managed manually.

@sandeeppatidar30
Copy link

This is how i implemented https://stackblitz.com/edit/angular-xgtey4 using mat-chips, mat-autocomplete.
Using above approach there is a flicker in autocomplete panel while selecting multiple items so i have used another approach https://stackblitz.com/edit/angular-ah51ss where i used mat-select-list in autocomplete instead of mat-option

@dandydarshan
Copy link

Here is how i handled it https://stackblitz.com/edit/angular-v1b716
I used two form controls. One for the autocomplete and one for multiple selection which I managed manually.

i took a reference from your solution. could you let us know how to bind the selected values as in mat select like for e.g. i tried ([value])="somearray"

@mmalerba mmalerba added needs: discussion Further discussion with the team is needed before proceeding and removed discussion labels Mar 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: material/autocomplete feature This issue represents a new feature or feature request rather than a bug or bug fix needs: discussion Further discussion with the team is needed before proceeding P5 The team acknowledges the request but does not plan to address it, it remains open for discussion
Projects
None yet
Development

No branches or pull requests