Autocomplete with multiple selection (MdAutocompleteTrigger customization)
- Langage dominant
- TypeScript
- Étoiles
- 25k
- Forks
- 6.8k
- Merge moyen
- 1 j 8 h
- PR mergées (30 j)
- 91
Description
#### 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.
Guide de contribution
Ouvrir le guide de contribution
Piste de recherche
Commencez par lire MdAutocompleteTrigger et la logique correspondante de sélection multiple de MdSelect, en vous concentrant sur l’effacement de la sélection, les actions de fermeture et les méthodes privées décrites dans l’issue. Le travail est terminé lorsque MdAutocomplete prend en charge la sélection multiple ou fournit une voie de personnalisation prise en charge sans monkey-patching, tout en préservant les options sélectionnées et en laissant le panneau ouvert après la sélection.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- angular, typescript
- Domaine
- frontend
- Type d'issue
- Fonctionnalité
- Difficulté
- 5/5
- Temps estimé
- Plus d'une semaine
- Activité
- À l'abandon
- Clarté
- Plutôt claire
- Accessibilité débutants
- 25/100