Question

I am trying to get four cascading dropdowns using knockout.js:

  1. Search Criteria
  2. Sub Criteria
  3. Value
  4. State

I was able to get the first cascade going (but not the others due to databinding issues) by using code from the following link:

http://blogs.msdn.com/b/thebeebs/archive/2011/12/01/price-calculator.aspx

The data for these dropdowns is being returned to my razor viewpage as an IEnumrable of SearchCriterion from an MVC view using ViewBag.CriteriaData variable. The code for my classes is as follows:

public class SearchCriterion
{
    public string Text { get; set; }

    public string Value { get; set; }

    public List<SubCriterion> SubCriteria { get; set; }
} 

public class SubCriterion
{
    public string SearchCriterionValue { get; set; }

    public string Text { get; set; }

    public string Value { get; set; }

    public List<ColumnValue> ColumnValues { get; set; }
}

public class ColumnValue
{
    public string SearchCriterionValue { get; set; }

    public string SubCriterionValue { get; set; }

    public string Text { get; set; }

    public string Value { get; set; }

    public IEnumerable<StateValue> StateValues { get; set; }
}

public class StateValue
{   
    public string SearchCriterionValue { get; set; }

    public string SubCriterionValue { get; set; }

    public string ColumnValue { get; set; }

    public IEnumerable<int> InputStateIds { get; set; }

    public IEnumerable<int> OutputStateIds { get; set; }

    public int SelectedInputStateId { get; set; }

    public int SelectedOutputStateId { get; set; }

    public string Text { get; set; }

    public string Value { get; set; }    
}

The issues I am facing are in the following portions of the .cshtml code:

  1. What do I specify in this template for the other two dropdowns. e.g. the third dropdown needs to be bound to ColumnValue.Value (ColumnValue is part of SubCriterion)

    <script id='criteriaRowTemplate' type='text/html'>
        <tr>
            <td><select  data-bind='options: criteriaData, optionsText: "Text", optionsCaption: "Search Criterion", value: SearchCriterion' /></td>
            <td><select data-bind='visible: SearchCriterion, options: SearchCriterion() ? SearchCriterion().SubCriteria : null, optionsText: "Text", optionsCaption: "Sub Criterion", value: SubCriterion' /></td>
            <td><select data-bind='visible: SubCriterion, options: SubCriterion() ? SubCriterion().ColumnValues : null, optionsText: "Text", optionsCaption: "Column Value", value: ColumnValue'/></td>
            <td><select data-bind='visible: ColumnValue, options: ColumnValue() ? ColumnValue().StateValues : null, optionsText: "Text", optionsCaption: "State", value: StateValue'/></td>                
            <td><button data-bind='click: function() { viewModel.removeLine($data) }'>Remove</button></td>
        </tr>
    </script>
    
  2. Is this correct?

    var CriteriaLine = function() {
    this.SearchCriterion = ko.observable();
    this.SubCriterion = ko.observable();
    this.ColumnValue = ko.observable();
    this.StateValue = ko.observable();     
    
    // Whenever the Search Criteria changes, reset the Sub Criteria selection
    this.SearchCriterion.subscribe(function() { this.SubCriterion(undefined); }.bind(this));
    this.SubCriterion.subscribe(function() { this.ColumnValue(undefined); }.bind(this));
    this.ColumnValue.subscribe(function() { this.StateValue(undefined); }.bind(this));
    

    };

  3. How do I map the complete C# object with the Javascript object? It works if we just have the first two dropdowns:

    // Create a Javascript object object with the same property names as the C# object
    var dataToSearch = $.map(this.lines(), function (line) { return line.StateValue() ? line.StateValue() : undefined; });
    
        var SearchObject = new function () {
                this.StateValues = dataToSearch;
        };
    
        // Convert the object to JSON
        var searchCriteria = JSON.stringify(SearchObject);
    
  4. Does anything need to change here for the binding?

    // Apply the data from the server to the variable
    
    var criteriaData = @Html.Raw(@Json.Encode(ViewBag.CriteriaData));
    
    var viewModel = new Criteria();
    
    ko.applyBindings(viewModel, document.getElementById("criteriaDiv"));
    

EDIT:

I am now able to populate the cascading dropdowns (updated code above). Now I have 4 columns, each column having one of the dropdowns. I also have 1...n number of rows being added dynamically by using Knockoutjs. So, the user can now select values from these dropdowns and add more rows of dropdowns if he wants. The only thing remaining is to return the values that the user selects for the dropdowns to the controller(point 3 above). I am not sure how I can do that. Any help would be appreciated.

EDIT 2:

Added working code for Item # 3 and modified the ColumnValue and StateValue classes.

Was it helpful?

Solution

I'm not sure I fully understand your question, but I'm going to take a whack at it anyway :). I think you're looking for a way to "validate" if it is in fact time to allow the next drop down to be active?

If so, you could approach it from a standpoint of Computed Observables. Basically, you would bind each of your dropdowns to a computed value which is derived from the previous dependencies.

Let me write fiddle and I'll show you :)

OK, give this a shot...sorry for the delay...http://jsfiddle.net/farina/ZNBcM/3/

OTHER TIPS

I update the answer, Hope, it will help new Comers. Methods for Binding Hierarchical Dropdowns using Knockout JS in MVC

Here you can find the good example .

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top