M.Babcock
M.Babcock

Reputation: 18965

Cascading Telerik combo boxes remember selected value after client side databinding

I have two combo boxes and a grid in my MVC View:

<div id="Products" class="products maintField">
    @Html.Label("something", "Product:")
    @(Html.Telerik().ComboBox().Name("productList")
                               .AutoFill(true)
                               .DataBinding(c => c.Ajax().Select("GetProducts", "Maintenance").Enabled(true).Cache(true))
                               .Filterable(c => c.FilterMode(AutoCompleteFilterMode.StartsWith).Enabled(true))
                               .HighlightFirstMatch(true)
                               .ClientEvents(eb =>
                               {
                                   eb.OnChange("productChanged");
                               })
    )
</div>
<div id="Layouts" class="layout maintField">
    @Html.Label("something", "Layout:")
    @(Html.Telerik().ComboBox().Name("layoutList")
                               .ClientEvents(eb =>
                               {
                                   eb.OnChange("layoutChanged");
                               })
    )
</div>
<div style="clear: both;"/>
<div class="fields">
    @(Html.Telerik().Grid<MaintenanceModel>().Name("fieldList")
        .DataBinding(bc =>
        {
            bc.Ajax().Select("GetProductFields", "Maintenance", new { prodID = 0, layoutID = 0 }).Enabled(true);
        })
        .ClientEvents(c =>
        {
            c.OnDataBound("fieldGridBound");  
        })
        .Columns(c =>
        {
            c.Bound(mm => mm.Field_ID).ClientTemplate("<input id='Field_ID_<#= Field_ID #>' type='checkbox' name='checkedRecords' class=\"checkBoxGroup\" value='<#= Field_ID #>' />")
                                      .Title("<input id='mastercheckbox' type='checkbox' style='display:none;' />")
                                      .HeaderHtmlAttributes(new { style = "text-align: center;" })
                                      .Width(36)
                                      .HtmlAttributes(new { style = "text-align:center" });
            c.Bound(fld => fld.Field_Name).Title("Field Name").Width(225);
            c.Bound(fld => fld.Field_Description).Title("Field Description");
        }).Scrollable(c =>
        {
            c.Enabled(true);
            c.Height(700);
        }).Footer(false).Sortable(c =>
        {
            c.Enabled(false);
            c.OrderBy(oc =>
            {
                oc.Add(fld => fld.SeqNbr);
            });
        })
    )
</div>

And with some less than beautiful JS the comboboxes cascade and eventually populate the grid:

function productChanged(e) {
    clearLayout();
    var layoutCombo = $("#layoutList").data('tComboBox');

    if (layoutCombo == null) {
        alert("Can't find layoutList");
        return;
    }

    if (e.value == "" || e.value == null) {
        clearLayout();
        return;
    }

    layoutCombo.loader.showBusy();
    $.get('@Url.Action("GetProductLayouts", "Maintenance")', { prodID: e.value }, function (result) {
        layoutCombo.dataBind(result);
        layoutCombo.loader.hideBusy();
    });
}
function layoutChanged(e) {
    var prodCombo = $("#productList").data('tComboBox');
    var fieldGrid = $("#fieldList").data('tGrid');

    if (fieldGrid == null) {
        alert("Can't find fieldList");
    }
    if (prodCombo == null) {
        alert("Can't find prodCombo");
    }

    if (e.value == "" || e.value == null) {
        clearGrid();
        return;
    }

    fieldGrid.rebind({ prodID: prodCombo.value(), layoutID: e.value });
    $("#mastercheckbox").css("display", "block");
}
function fieldGridBound(e) {
    var prodCombo = $("#productList").data('tComboBox');
    var layoutCombo = $("#layoutList").data('tComboBox');

    $.get('@Url.Action("GetLayoutFields", "Maintenance")', { prodID: prodCombo.value(), layoutID: layoutCombo.value() }, function (result) {
        var cbGroup = $(".checkBoxGroup");

        $(cbGroup).each(function (i, e) {
            if (result.indexOf(e.value) >= 0) {
                $(e).attr('checked', 'checked');
            } else {
                $(e).removeAttr('checked');
            }
        });
    });
}

This works beautifully except in the situation where the two combo boxes are filled, the grid is bound, and the user goes to select a different value from the first combo box. The second combo box rebinds as expected, but if the values bound to the combobox didn't change (in position or display value) and the user selects the same item again, the change event doesn't get fired.

Specific example

In the database I have a hierarchy of Products and Layouts:

 Product A
     Platinum Layout
     Gold Layout
     Silver Layout
 Product B
     Platinum Layout
     Gold Layout
     Silver Layout
 Product C
     Platinum Layout
     Silver Layout

Scenario 1

  1. User selects 'Product A' from the first drop down causing the second drop down to be populated with Platinum, Gold, and Silver Layouts.
  2. User selects 'Platinum Layout' from the second drop down causing related information to show in the grid.
  3. User selects 'Product B' from the first drop down causing the second drop down to again be populated with Platinum, Gold, and Silver Layouts.
  4. User selects 'Platinum Layout' from the second drop down again, however the changed event doesn't fire because the combo box didn't forget what was selected from before being rebound.

Scenario 2

  1. User selects 'Product A' from the first drop down causing the second drop down to be populated with Platinum, Gold, and Silver Layouts.
  2. User selects 'Platinum Layout' from the second drop down causing related information to show in the grid.
  3. User selects 'Product C' from the first drop down causing the second drop down to again be populated with Platinum, Gold, and Silver Layouts.
  4. User selects 'Platinum Layout' from the second drop down, causing the same erroneous behavior described in step 4 of Scenario 1.

Scenario 3

  1. User selects 'Product A' from the first drop down causing the second drop down to be populated with Platinum, Gold, and Silver Layouts.
  2. User selects 'Silver Layout' from the second drop down causing related information to show in the grid.
  3. User selects 'Product C' from the first drop down causing the second drop down to be populated with Platinum and Silver Layouts.
  4. User selects 'Silver Layout' from the second drop down causing related information to show in the grid (as should have happened in the other 2 scenarios).

In order to try to force the combo box to forget which item was selected prior to being rebound, I've tried manually setting the selected item to -1 (nothing) before binding using:

layoutCombo.select(-1);

and I've also tried reloading the combo box after the bind using

layoutCombo.reload();

However neither of them worked for me.

Has anyone else seen this behavior under similar circumstances? Is there any way to clear the combo box's last selected item variable manually from within my page (without digging around in the Telerik source)?

Upvotes: 1

Views: 2816

Answers (1)

M.Babcock
M.Babcock

Reputation: 18965

After playing with it a little more and hoping it would just work, it looks like it works if I fire the change event manually after layoutCombo.select(-1) and before I force the dataBind on the second combo box. Here's what my productChanged code looks like now:

function productChanged(e) {
    clearLayout();
    var layoutCombo = $("#layoutList").data('tComboBox');

    if (layoutCombo == null) {
        alert("Can't find layoutList");
        return;
    }

    if (e.value == "" || e.value == null) {
        clearLayout();
        return;
    }

    layoutCombo.loader.showBusy();
    $.get('@Url.Action("GetProductLayouts", "Maintenance")', { prodID: e.value }, function (result) {
        layoutCombo.select(-1);
        layoutCombo.trigger.change(); //**** Trigger the change manually so the combobox knows its value has changed
        layoutCombo.dataBind(result);
        layoutCombo.loader.hideBusy();
    });
    //layoutCombo.reload();
}

It feels like a bit of a hack since the combo box should know it's own value already. Any other ideas to make this cleaner would be much appreciated.

Upvotes: 1

Related Questions