Single select options binding with jQuery validation is not working

I am binding a list of objects to a select using knockout. Object Class can have any number of properties

<select id="TheProperty_City" 
        name="TheProperty_City" 
        class="required" 
        data-bind="options: cityList, 
                   optionsText: 'Name',  
                   value: selectedCity, 
                   optionsCaption: '--select the city--'" />

This works perfectly fine and I can use viewModel.selectedCity().Name or viewModel.selectedCity().Value for loading child elements.

My issue is with jQuery validation. If I leave the statement as above, jQuery does not reset the error even after selection.

I fixed it with by specifying the optionsValue in the bind, but then the selectedCity returns the scalar value and not the entire object. Any idea how to preserve the object behavior or do the validation differently?

 <select id="TheProperty_City" 
         name="TheProperty_City" 
         class="required" 
         data-bind="options: cityList, 
                    optionsText: 'Name',  
                    optionsValue: 'Value', //added the optionsValue
                    value: selectedCity, 
                    optionsCaption: '--select the city--'" />

The error stays there when optionsValue is not specified:

Without the OptionsValue

Here’s my Object Watch on selectedCity:

viewModel.selectedCity() in watch returns an object

Here’s an Object Watch on selectedCity when optionsValue is specified:

With OptionsValue

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

The issue is that when dealing with objects as the value, the option elements have their value set to “”. The jQuery validation fails because of this. You could write a binding or wrapper binding to the options binding that goes through and just sets them to a value, but I don’t think that it is preferable to go that route.

A decent option is to store the value and use a dependentObservable to represent the currently selected object.

It would be like:

var viewModel = {
    cityList: [{ Name: "Madison", Value: "MSN" }, { Name: "Milwaukee", Value: "MKE" }, { Name: "Green Bay", Value: "GRB" }],
    selectedCityValue: ko.observable()
};

viewModel.selectedCity = ko.dependentObservable(function() {
    var value = this.selectedCityValue();
    return ko.utils.arrayFirst(this.cityList, function(city) {
       return city.Value === value; 
    });
}, viewModel);

With a binding like:
<select id="TheProperty_City" name="TheProperty_City" class="required" 
    data-bind="options: cityList, 
    optionsText: 'Name', 
    optionsValue: 'Value',
    value: selectedCityValue, 
    optionsCaption: '--select the city--'" />

Sample here: http://jsfiddle.net/rniemeyer/EgCM3/

Method 2

Quite neat solution would be to add optionsAfrerRender parameter to options binding. Assuming that in ‘selectedCity’ object there is a field ‘cityId’ you could assign it to an option value. Please check the following solution based on your example:

 <select id="TheProperty_City" 
    name="TheProperty_City" 
    class="required" 
    data-bind="options: cityList, 
               optionsText: 'Name',
               value: selectedCity,
               optionsAfterRender: function(option, item) 
                                        { option.value = item.cityId; }
               optionsCaption: '--select the city--'" />

Using this approach you will get working both knockout options binding and jquery validation.

Method 3

The above answer works with one caveat. The first select value bound (“–select the city–“) will be undefined causing an error.

If you modify the code to handle this it will work.

<select id="TheProperty_City" 
name="TheProperty_City" 
class="required" 
data-bind="options: cityList, 
           optionsText: 'Name',
           value: selectedCity,
           optionsAfterRender: setOptionValue
           optionsCaption: '--select the city--'" />

Note: I’ve stripped out everything from the ViewModel except for the setOptionValue method for brevity.
<script type="text/javascript">
var vm = {
    setOptionValue: function(option, item) {
        if (item === undefined) {
            option.value = "";
        } else {
            option.value = item.id;
        }
    }
};
ko.applyBindings(vm);

Method 4

To extend on Robert’s answer, we can make the helper function work for every dropdown in the view model no matter the context data-binding by allowing the user to pass in a string for the right property:

JS

self.setOptionValue = function(propId) {
    return function (option, item) {
        if (item === undefined) {
            option.value = "";
        } else {
            option.value = item[propId];
        }
    }
};

HTML
<select class="custom-dropdown" data-bind="options: cityList, 
        optionsAfterRender: setOptionValue('AppointmentTypeId'), value: selectedCityObj, optionsCaption: 'Choose...'" data-validation="required"></select>


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x