Skip to content

Next closest chip is not focused after chip removal. #12374

Closed
@ghost

Description

Bug, feature request, or proposal:

Next closest chip is not focused after chip removal.

What is the expected behavior?

Backspace should delete one chip per keystroke. Specifically, after chip is deleted, the next closest chip should be given focus so the backspace keystroke can target it.

What is the current behavior?

Focus is given to body on chip removal. User must select a chip before each backspace keystroke.

What are the steps to reproduce?

Open https://stackblitz.com/edit/angular-gbfa3u?file=app/chips-input-example.ts in chrome. (Edge and Firefox work ok)
select the last chip
hit backspace (last chip is removed, focus is lost)
hit backspace again - nothing happens

What is the use-case or motivation for changing an existing behavior?

Allow animations to be used in a component containing chips without breaking backspace workflow.

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

Angular 6.0.9
Material 6.4.1
Windows 10
Typescript 2.7.2
Chrome version 67.0.3396.99 (Official Build) (64-bit)

Is there anything else we should know?

StackBlitz is a fork of material.angular.io's "Chips with input" sample. Animation definition was added:
animations: [
trigger(
'oeuoeuoeu', [
transition(':leave', [
style({opacity: 0}),
animate('500ms', style({opacity: 1}))
])
]
)
],

'oeuoeuoeu' is simply defined and not attached to anything. Defining an animation, causes the chips to be tagged with a namespace. On removal, when the namespace is defined, ns.removeNode it triggered and the chip disappears from the DOM:

TransitionAnimationEngine.prototype.removeNode = function (namespaceId, element, context) {
if (!isElementNode(element)) {
this._onRemovalComplete(element, context);
return;
}
var ns = namespaceId ? this._fetchNamespace(namespaceId) : null;
if (ns) {
ns.removeNode(element, context);
}
else {
this.markElementAsRemoved(namespaceId, element, false, context);
}

After the chip is removed, _lastDestroyedIndex is null so the next closest chip is not focused:
MatChipList.prototype._updateFocusForDestroyedChips = /**
* Checks to see if a focus chip was recently destroyed so that we can refocus the next closest
* one.
* @return {?}
/
function () {
var /
* @type {?} / chipsArray = this.chips.toArray();
if (this._lastDestroyedIndex != null && chipsArray.length > 0 && (this.focused ||
(this._keyManager.activeItem && chipsArray.indexOf(this._keyManager.activeItem) === -1))) {
// Check whether the destroyed chip was the last item
var /
* @type {?} / newFocusIndex = Math.min(this._lastDestroyedIndex, chipsArray.length - 1);
this._keyManager.setActiveItem(newFocusIndex);
var /
* @type {?} */ focusChip = this._keyManager.activeItem;
// Focus the chip
if (focusChip) {
focusChip.focus();
}
}
// Reset our destroyed index
this._lastDestroyedIndex = null;
};

When animations are removed, chips are not provided with a namespace and thus markElementAsRemoved is triggered instead. The chip lives in the DOM until after _updateFocusForDestroyedChips executes and successfully focuses the next closest chip.

Note:
IE and Firefox do successfully trigger ns.removeNode(element, context), remove the element, and correctly focus the next chip. This is not accomplished by _updateFocusForDestroyedChips though. It still fails because _lastDestroyedIndex is still null. Some other workflow in the angular machinery is refocusing the chip.

Metadata

Metadata

Assignees

Labels

P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions