angular / angular/components

Autocomplete with multiple selection (MdAutocompleteTrigger customization)

Offen
#5,053 15 Kommentare 36 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
area: material/autocomplete feature P3
Vorherrschende Sprache
TypeScript
Sterne
25k
Forks
6.8k
Ø Merge
1 T. 8 Std.
Gemergte PRs (30 T.)
91

Beschreibung

#### 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 `MdOption`s on selection
2. leave the Autocomplete open after option selection event.

EDIT updated for beta12
```typescript
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.

Beitragsleitfaden

Beitragsleitfaden öffnen

Rechercherichtung

Lies zunächst MdAutocompleteTrigger und die entsprechende MdSelect-Logik für die Mehrfachauswahl, wobei du dich auf das Löschen der Auswahl, Schließaktionen und die im Issue beschriebenen privaten Methoden konzentrierst. Als erledigt gilt die Aufgabe, wenn MdAutocomplete Mehrfachauswahl unterstützt oder einen unterstützten Anpassungsweg ohne Monkey-Patching bietet, dabei die ausgewählten Optionen beibehält und das Panel nach der Auswahl geöffnet bleibt.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
angular, typescript
Bereich
frontend
Issue-Typ
Feature
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
25/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.