Kendo-angular: The dropdownlist defaultItem isn't a replacement for placeholder + BUG when used with filtering + Need buton to clear selection!

Created on 26 Oct 2018  路  6Comments  路  Source: telerik/kendo-angular

I'm submitting a...

BUG - see Issue 2
Undesired behaviour - see Issue 1

Issue 1
With combo-boxes and placeholders, when we do filter items and press ENTER (without selecting any of the items) we get 1st item selected from the list. With drop-down-list, we always get the default item selected.

  • correction: 31/10/18: on the 1st run, indeed the 1st item get's selected, but if there's no valid item, the default item gets selected and from that point on, even if there's a valid item, pressing enter would keep the default item being selected instead of selecting the 1st valid item.

Based on https://github.com/telerik/kendo-angular/issues/319 the defaultItem is meant to be a replacement for the placeholder, yet it does behave differently.

Issue 2
Using dropdownlist and filtering and defaultItem together when there's a value on the filter, it's impossible to select the defaultItem (nor via mouse, neither via keyboard).

Expected behavior

  1. Be able to select default item when using filtering and filter is not empty (needs bugfixing)
  2. Be able to select the 1st filtered item (so first item, excluding defaultItem) from the list when pressing ENTER in the opened pop-up (so same behaviour as other controls have with placeholder)
  3. Additionally we'd welcome a properly (size independent) placed X button to clear DDL selection (missing feature)

Also inside the comments there are 4 questions based on what developers maybe would be able to achieve a solution for the Nr. 2. issue, if some hints would be provided.

Minimal reproduction of the problem with instructions

https://stackblitz.com/edit/valid-selection-withfilter-wont-allow-default-to-be-sele-vpt2yo?file=app/app.component.ts - With comments on the TS on reproduce some of the issues.

Environment

StackBlitz or Angular 4.x, Chrome (latest)

dropdowns

Most helpful comment

The fixes are included in the dev build of the @progress/kendo-angular-dropdowns package v3.4.0-dev.201811091501.

  • the defaultItem is now selectable after filtering;
  • the focus of the DropDownList after filtering stays either on the selected non-default item in the list (if there is one) or on the first non-default one;

An example with the corrected behavior can be seen here. The sample project uses the dev version of the dropdowns package.

All 6 comments

Dear Telerik,

I would like to thank you for the suggested soluitions (Ticket ID: 1348425), yet I need to express that those are hardly useable workarounds for different reasons.

Use combobox instead of dropdownlist is not a solution.

  • combobox does not have a separate filter field
  • combobox does not have all the templating possibilities as dropdownlist has (miss the selected item template) - i'd recommend to add it to the next sprint (TODO list)

FACTS:
The concept of a selectable default item as a replacement for a placeholder simply smells.

Some background (for others reading this issue): Combobox does not have a selected item (value) template, so assume its the reason why a plain placeholder text is used there. Dropdownlist has selected item template (value template), so it makes sense to use something more sophisticated than just a plain string to display - but anyhow - if it serves the same purpose, it should behave similarly (not being selectable, maybe not even show up inside the drop-down items). also calling it placeholderItem would possibly make more cleaner what it is intended for.

Based on that background my original suggestion (drop default item, add X button, add placeholder support) has to be modified as follows:


SUGGESTION:

  • default item (placeholder item) should serve as a fallback for invalid/empty selection
  • do not show default item in the pop-up - or at least make it unselectable
  • add button X (so we're able to easily clear selection)

This would of course also remove the need to address the issue of default item not being selectable with non empty filter expression.

Above changes would result in a very similar searching experience as with combo-boxes (which users do like) . So something similar as No-data template, just for the selected value.


Current issue:

  • default item is 1st in list
  • default item is unfortunatelly selecteable
  • when filtering and pressing enter, this leads to default item being selected instead of the 1st filtered item (so discrepancy in behaviour when compared to combobox)

The issue can be checked here (stackblit provided by Telerik):

  1. start: https://stackblitz.com/edit/valid-selection-withfilter-wont-allow-default-to-be-sele-7edupq?file=app/app.component.ts
  2. select item: Large
  3. open popup again, enter search text: s
  4. we will get 2 items listed aft. filtering: "selected items..." and "small"
  5. now press ENTER
  6. the default item will get selected, instead of the 1st valid (filtered) item (which we expect to be selected)

Of course having a way to add multiple (not necessarily just one) item(s) before a selection is also a good solution, but for something else. I use it to show selected item (id / text) that I loaded from the server, without the need to fetch items into the combo-box (as the combo box uses > 5.000 items and serer side filtering). i called that input InitialData and it's a part of my search-ex directive which supports advanced odata and local array filtering over multiple properties.


Anyhow if TELERIk will keep the current behaviour, the first thing that I'll do is to try to create a directive which will simply override this behaviour, and for that we will need:
1) a way to unselect default item on pop-up opening
2) be able to tell if user pressed ENTER or ESC (so we know if we we need to select 1st item from list if we hit ENTEr or simply leave selection/default if ESC is pressed)
...and I assume our team won't be the only one searching for similar, good old standards...

@hidegh The default item should indeed be selectable when there's a filter query in place and we'll look to resolve the issue as soon as possible. Thank you for pointing this out.

About the other questions raised:

  • Default item as placeholder: The default item is not a substitution for the input placeholder. It's similar in some ways, but not the same. The default item is a valid item with value, specified by you, just as the other elements in the list. So it's natural to keep seeing it when the options list is open. As explained in the issue you yourself have linked (#319), there's no implementation of a placeholder in the HTML select element and consequently in controls like ours which give you an extended and more advanced version of it. This is by design and we have no intention to change that behavior currently.
  • During filtering the focus should jump to the first item after the default one: This _is_ the current behavior. The focus is, however, on the default item when it is selected, which is expected. No intentions here to change that behavior again.
  • Adding a reset button: This could be considered a custom functionality, as it is not something typical for the select element or something that is a common scenario. It is also not too hard to achieve with some custom code - refer to the example below.

If you weren't satisfied with the proposed solution in the support ticket thread, you could have just asked for a different solution there - the GitHub repo is for reporting issues with the components and not for asking questions on how to achieve some custom functionality that is not supported out of the box.

Anyway, you can refer to this example that gives you a placeholder-like functionality and a reset button.

@dimitar-pechev

TX for the way to emulate placeholder correctly.

Reasons for creating a BUG were:

  • default item behaving as a regular item inside the list but being referred to it as a placeholder replacement (so inconsistency both in telerik's way to express the use-scenario and thus also an inconsistency in behaviour of those two)
  • missing feature that a selection should have (to allow to clear selection) in a simple way for the user

Still some shortcomings:

1)
You mentioned that During filtering the focus should jump to the first item after the default one - actually not working in every case. Provided a tackblitz: here. Just select the default item. Then re-open the dropdown, enter serach text: s, press ENTER - the default item is the one that has been kept as the selection, not the 1st avail. one. So this is not working so (or not in every case).

Also updated original comment (with proper note).

Btw: is there a way to intercept selection event (while pop-up is opened) and keyboard events (from pop-up) so we can code our own bypass?

2)
Creating a reset button via wrapping - it's not a hello world app people wan't to create with extra controls they buy. Despite not being a control developer (like you), this kind of solution never ever crossed my mind: it's violating dry. Also creating a wrapper control (via directive) is an overkill (to push down all inputs, re-bind all events).

I'd go into a solution via extra directive, but the proper X placing (and on-click event listening) is heavily KENDO CSS dependent and a proper way to place it is needed in all cases (sizes - px, em, rem, %, ...). But if that small part would be shown in those samples, rest would be easy to go and a 100% success (for a long term).

Beside of whether the above is something we developer get, considering an empty selection (and allowing users to clear a selection easily) is IMHO a grounded request. You got it on comboboxes too, right? And dropdowns and comboboxes differ mainly (just) in the way of filtering...

Hello @hidegh

The default item and the placeholder are by no means equivalent.
The placeholder attribute in it's nature is just a plain text that hints what the expected user input is. That is why placeholders are typically seen in empty components that expect user input, such as AutoComplete, ComboBox, MultiSelect, DateInput and so on.
The DropDownList's default item on the other hand is, as the name suggests, an item. While its text could, and often is used as a hint, the item also has a value. The most common scenario is to use a default item with a value of undefined, which has two benefits. First, it gets automatically selected and displayed after initializing the component. And second - it allows the users to easily clear the component's value by select the default item. This, aside from the cluttered UI, also is the reason why we currently do not provide a dedicated "clear value" button. I would however, encourage you to submit this and the placeholder as a feature requests on our feedback portal.
I hope this clears the distinction between the two, why the DropDownList currently does not have a placeholder attribute and why the default item behaves the way it does.
Furthermore, I'd like to mention the existence of the TextBoxContainerComponent, which could be used to add a floating label. This closely resembles a placeholder in Angular Material.

Let me get to the other points you mentioned:

  1. At this point both the selection and the focus remain on the selected item (if exist in the filtered data set), regardless of whether it's an actual item or the default one. After discussing this behavior, we decided to move the focus to the first item after the default one. Bypassing the keyboard and selection events is currently not supported out of the box, however you could tap into the SelectionService's events.

  2. Creating a wrapper component or a directive is a rather common case when reusing a customized component is necessary. Adding an element to the DOM and listening to its click event is hardly an overkill. Here's a simple proof of concept example of how adding a clear button could be done. As mentioned above, we would welcome a feature request through the feedback portal. This would help us prioritize the feature based on how popular the request is.

The fixes are included in the dev build of the @progress/kendo-angular-dropdowns package v3.4.0-dev.201811091501.

  • the defaultItem is now selectable after filtering;
  • the focus of the DropDownList after filtering stays either on the selected non-default item in the list (if there is one) or on the first non-default one;

An example with the corrected behavior can be seen here. The sample project uses the dev version of the dropdowns package.

Awesome. Tx.

Also based on the responses and samples (with some minor alteration and with 1 internal method call) I managed to create a generic (no-code) clear function that should make dropdownlist behave as combobox when clearing. Samlpe (temporary) here, a cleaned up code below.

import { Component, Directive, Renderer2, AfterViewInit, OnDestroy, ViewChild } from '@angular/core';
import { DropDownListComponent } from '@progress/kendo-angular-dropdowns';

import { Subscription } from 'rxjs/Subscription';
import { take } from 'rxjs/operators/take';

/*
Adds a clear button to all drop-down lists!

    Original implementation used an Output property (event) so for the clearing a view-method was responsible.
    See: https://stackblitz.com/edit/angular-uv553z-objyev?file=app/app.component.ts

    This version behaves same as the combo-box or autocomplete (so sets a undefined value).
 */
@Directive({
    selector: 'kendo-dropdownlist'
})
export class DropDownListClearButtonDirective implements AfterViewInit, OnDestroy {

    public resetButton: HTMLElement;

    private changeSubscription: Subscription;

    constructor(
        public renderer: Renderer2,
        public ddl: DropDownListComponent) {
        this.changeSubscription = this.ddl.valueChange.subscribe(() => { this.toggle(!!this.ddl.value) });
    }

    private toggle(visible: boolean): void {
        this.renderer.setStyle(this.resetButton, "visibility", visible ? "visible" : "hidden");
    }

    private reset(): void {
        this.ddl.open.pipe(take(1)).subscribe((e: any) => { e.preventDefault(); });

        // does the desired clean, but without UI update and events...
        this.ddl.reset();

        // update UI
        this.ddl.writeValue(this.ddl.value);

        // NOTE: emit change does the same as the 2 lines below - (<any>this.ddl).emitChange(undefined);
        (<any>this.ddl).onChangeCallback(this.ddl.value);
        this.ddl.valueChange.emit(this.ddl.value);

        this.toggle(false);
    }

    public ngAfterViewInit(): void {
        const openButton = this.ddl.wrapper.nativeElement.querySelector(".k-select");
        this.resetButton = document.createElement('span');

        this.resetButton.setAttribute('class', 'k-select');
        this.resetButton.innerHTML = "<span class='k-icon k-i-close'></span>";

        this.renderer.insertBefore(this.ddl.wrapper.nativeElement, this.resetButton, openButton);
        this.renderer.listen(this.resetButton, "click", this.reset.bind(this));
        this.toggle(false);
    }

    public ngOnDestroy(): void {
        this.changeSubscription.unsubscribe();
    }

}
Was this page helpful?
0 / 5 - 0 ratings