Kendo-angular: ComboBox custom values

Created on 15 Sep 2016  路  9Comments  路  Source: telerik/kendo-angular

In its nature, the ComboBox should allow the user to enter a text that is not present in the provided dataset. There are, however, scenarios that need to be handled a bit differently, otherwise some unexpected results might occur. Let's take a look at the following configuration:

//pseudo code
<kendo-combobox
    [data]="items"
    [textField]="'text'"
    [valueField]="'value'"
    [valuePrimitive]="true"
    [(ngModel)]="item"
>

export class AppComponent` {
    public item: number = 1;
    public items: any[] = [
        { text: "Item 1", value: 1 },
        { text: "Item 2", value: 2 },
        { text: "Item 3", value: 3 }
    ];
}

Scenario

  1. The component is initialized and successfully resolves the initial value to "Item 1"
  2. The user now enters "Item 999" into the input and presses Enter.
  3. The ComboBox component does not find a match in the dataset, but should pass the "Item 999" text to the App, thus allowing the custom value to be used.

    Problem

Passing the custom text to the App might look like the simplest approach, however it will result in updating the model with a value of different type. As seen in the example, the item is of type number, however we'll update it with a string. This is probably not what the users might want or expect. Passing null or undefined makes even less sense.

Solutions

:one:

Ask developers to subscribe to the ComboBox's ngModelChange event and handle the custom values as they wish.
:heavy_minus_sign: Requires too much effort on the user's end for handling basic and relatively common scenario

:two:

Introduce [allowCustom] configuration option. Unless explicitly specified, the ComboBox will ignore custom values and will not trigger the ngModelChange event. This is complimentary to :one:
:heavy_plus_sign: Require custom handling only when option is enabled.
:heavy_minus_sign: Both custom and existing values pass through the custom handler

:three:
Introduce [ignoreCustom] configuration option. Same as :two:, but enables custom values by default.
:heavy_minus_sign: Still requires the custom handling (from :one:) by default
:heavy_minus_sign: Both custom and existing values pass through the custom handler

:four:
Introduce (onCustomValue) event, triggered instead of the ngModelChange when custom value is detected.
:heavy_plus_sign: Allows handling of custom values only
:heavy_minus_sign: Unclear what the default action should be if the event is not handled - clear the text or keep it. This could be complimented well by the [ignoreCustom] option.

Do you see any other available options or possible pitfalls? Any input is greatly appreciated.
Feel free to vote using the :one: :two: :three: :four: emojis.

Question

Most helpful comment

Indeed @ggkrustev , Kendo UI customer support taught us that the ComboBox is used primarily for faster filtering. If we acknowledge that, then allowing custom values should be optional instead of enabled by default. Handling them is rarely wanted, and it requires extra effort from the developer. We cannot force that extra effort for every ComboBox instance.

In yet another offline discussion, @rusev, @rkonstantinov and I toyed with the idea of having a callback used for normalizing the custom values. The callback should be executed before the valueChange and ngModelChange events are emitted, however they should not be skipped. Ideally, the normalized value will be passed back to the ComboBox. In addition to that, we can have the custom values ignored by default and enabled explicitly via the allowCustom option. If that option is provided, then the normalizer is expected as well. If no normalizer is provided we could either throw an error in DevMode or return the text as-is. In a scenario where complex data without valuePrimitive is used, the default normalization would be an object using the valueField and textField options as keys (this is also the current behavior).

Comments are welcome, as always.

All 9 comments

@tapopov thanks a lot for the detailed explanation and for the possible solutions you've come up with. Here are my comments on them:

  1. I would vote with No.
    It will make the component unusable for anything excluding creating custom values. By default ComboBox allows custom values, I would need to 'guard' my model from those custom values for every single combobox on the page.
  2. I would vote with No.
    Although we came up with this idea in an offline discussion, I would consider it as _not a good solution_, as it solves the problem only partially, here it's why:
  3. It will prevent the custom input, which is totally fine for most customers that use the component as an advanced search box.
  4. The drawback is that users will need to determine whether the value is custom or not in the (ngModelChange), which would not be an easy solution if the values are of the same type.
  5. I agree with you here. It is not any better then [2].
  6. I would vote with Yes.
    This sounds like the best solution to me. With the event we will move the change (model update) logic in a separate event handler. This the model will stay 'clean' until the user decides to change it with the custom value.

The inconvenience is the "surprise" that it will bring when a custom value is typed and no of the 'regular' events is raised. _This, however, should be easy to handle with expressive documentation and detailed examples._

To sum up, I would vote for 4.

There is important question to ask- why will you allow custom values in first place?

For me when the user types custom text, that text becomes component value. That is the feature name, right? Inside the default change handler it could handle the custom value.

The fact that the user can enter random text implies _string_ as a type of the value. There is no other type that you can infer from a custom text. That said I see few options:

  • set the value always as a string, so there is no confusion
  • add callback option where the consumer of the component can plug and "normalize" the custom value (seems too much of a hassle)
  • automatically "normalize" the value based on the "shape" of the data item or based on the data/text fields

Having separate handler for custom value is _not a valid_ approach for me. It will cause more confusion and frustration in angular context. The component will not work seamlessly with angular/forms. The consumer will have to manually handle value change and validation (it might even need custom validators).

ComboBox is designed to allow custom values by default (this could change of course). It, however, is of no use to most of the consumers in the Angular 2 context (two-way binding to be more precise).

The main goal that we should pursue is to allow the developers to use the ComboBox without the need to _guard_ there model from custom values. I know that you would say that 'if you don't want custom values use the dropdownlist'. Unfortunately, my personal experience has shown that developers are not happy with such suggestion. Coming into environment where two-way binding happens automagically and you don't have control over it, this 'always allowed' custom value will make the ComboBox unusable.
Also, how the one will handle custom values even though they are desired? The developer should intercept the change, create the new item based on the custom text and update the model. In all other cases, he/she will need to intercept the change event and guard there model. This must be done for every single ComboBox on the page. Personally, I wouldn't use such component.

In an offline discussion, a potentially good idea came up, that is, to trigger (customValue) event before (ngModelChange) and allow the developers to handle custom values, manipulate them if they want (create new data item) and then continue with the model update. This should be researched but look promising.

I believe that we would need to work like this:

  • _ComboBox should update the model automatically only with data items that are present in the source_. Thus we will keep the model 'clean', will not change the type unexpectedly and avoid surprising the consumer with the unexpected custom values.
  • _ComboBox should provide an easier and intuitive way to intercept/manipulate custom values and thus support creation of new items_

The first scenario, where the developers would not need a custom value support, but still want to benefit from the easier and quicker search option (visible input). The ComboBox is more accessible in terms of WAI-ARAI, mainly due to the visible input.

With regards to the suggestions that @rusev gave, I do believe that:

  • changing the type in a strongly-typed environment is not a good behavior. Also the developers would like to receive the same type as the model field expects.
  • this is a plausible solution. As I mentioned, we will need to research, but nevertheless I really like the most
  • this would not work when data is still not provided. In this case, we do not know the shape and type of the data item. Also we cannot force the developers to work only with Typescript.

I tend to think that should implement, if not the same, at least the closest solution to the second option you mentioned.

I would love to hear other opinions if any :)

Indeed @ggkrustev , Kendo UI customer support taught us that the ComboBox is used primarily for faster filtering. If we acknowledge that, then allowing custom values should be optional instead of enabled by default. Handling them is rarely wanted, and it requires extra effort from the developer. We cannot force that extra effort for every ComboBox instance.

In yet another offline discussion, @rusev, @rkonstantinov and I toyed with the idea of having a callback used for normalizing the custom values. The callback should be executed before the valueChange and ngModelChange events are emitted, however they should not be skipped. Ideally, the normalized value will be passed back to the ComboBox. In addition to that, we can have the custom values ignored by default and enabled explicitly via the allowCustom option. If that option is provided, then the normalizer is expected as well. If no normalizer is provided we could either throw an error in DevMode or return the text as-is. In a scenario where complex data without valuePrimitive is used, the default normalization would be an object using the valueField and textField options as keys (this is also the current behavior).

Comments are welcome, as always.

I'm OK with this approach. I also think that we can restrain from having default implementation at this point. We can throw run-time exception if such callback is not provided.

Or a warning in !prodMode with a link to the documentation

I like the last iteration of the "normalizer". I would vote to go ahead and implement it. The custom value would be disabled by default (allowCustom: false).
Thus, we will get the best of both worlds (combobox) :)

A notification should be raised in dev mode for sure. I don't believe that the one should ever reach the production mode with broken (not configured properly) component.

An exception it is then. If you have allowCustom == true and don't have a normalization callback set.

Implemented in v0.13.0

Was this page helpful?
0 / 5 - 0 ratings