What is best way to bind a Kendo UI dropdownlist to a ViewModel that is populated by a datasource?

16,185

Solution 1

You could use the dataBound event on the dropdown and set selectedAppointment from there:

data-bind="source:appointmentTypes, value:AppointmentTypeId, events: { dataBound: onDataBound }"

and your view model:

var viewModel = new kendo.observable({
    appointmentTypes: appointmentTypesDataSource,
    selectedAppointment : null,
    onDataBound: function(e) {
        e.sender.select(0); // will trigger your change handler
    }
});

Solution 2

You need to set the initial value of the selectedAppointment. This is the only way the span text will be set before the data source has been populated. Here is a runnable demo based on Northwind's Products:

<span data-bind="text:selectedProduct.ProductName"></span>
<select data-bind="source: products, value: selectedProduct"
      data-text-field="ProductName"
      data-value-field="ProductID"
      data-role="dropdownlist"></select>
<script>
var o = kendo.observable({
  selectedProduct: {
    ProductID: 2,
    ProductName: "Chang"
  },
  products: new kendo.data.DataSource({
    transport: {
      read: {
        url: "http://demos.telerik.com/kendo-ui/service/products",
        dataType: "jsonp"
      }
    }
  })
});

kendo.bind(document.body, o);
</script>

Here is a live demo: http://jsbin.com/pawiz/1/edit

Share:
16,185
ssmith
Author by

ssmith

Software developer geek and entrepreneur. I offer mentoring and coaching services to help teams improve the quality of software they deliver, improving team (and stakeholder/customer) happiness. Twitter: @ardalis Facebook: StevenAndrewSmith

Updated on June 26, 2022

Comments

  • ssmith
    ssmith about 2 years

    I have a kendoUI dropdownlist that is in a template and is bound to a ViewModel, along with a span that is bound to the data item that is selected in the dropdownlist:

    <p>
        <label>Appointment Type: </label>
        <select id="appointmentTypeDropDownList"
                data-text-field="Name"
                data-value-field="AppointmentTypeId"
                data-role="dropdownlist"
                data-bind="source:appointmentTypes, value:AppointmentTypeId"
                data-autobind="true"
                data-select="onSelected" />
    </p>
    <p><label>Duration:</label>
        <span data-bind="text:selectedAppointment.Duration"></span> minutes
    </p>
    

    My ViewModel:

    var viewModel = new kendo.observable({
        appointmentTypes: appointmentTypesDataSource,
        selectedAppointment : null,
    });
    

    Originally, I was using a hardcoded array of appointmentTypes, and setting the selectedAppointment to appointmentTypes[0] in the above viewModel declaration. That doesn't work now, because the datasource loads asynchronously. The viewModel is updated in the onSelected function:

    var onSelected = function (e) {
        var dataItem = this.dataItem(e.item.index());
        viewModel.set("selectedAppointment", dataItem);
    };
    

    The template is in a window, and the span is empty the first time it loads, and then works thereafter (once the data has loaded from the first request).

    My question is, how can I get the data binding of the span to work on the first request, so that it will display the Duration of the currently selected appointmentType from the list that is returned by the data source? Do I try and bind it to the selected data item of the dropdownlist? Is there a callback somewhere I should be using to do this? The template is inside of a kendoScheduler, if that matters:

    var template = kendo.template($("#editor").html());
    
    $("#scheduler").kendoScheduler({
        editable: {
            template: template()
        }
    });
    

    Thanks!

    Update: The template I've been working with is an editor for a Kendo UI Scheduler, which was not bound to its viewmodel, but was using part of the viewmodel for its datasource. In this case, trying to use the data-bind="events:{...}" syntax was causing the weird type errors I was getting. To wire up the databound event, Atanas let me know to use data-bound="onDataBound" and a global handler function (or alternately to configure my scheduler declaratively and bind it to the viewmodel). Using data-bound combined with the answer(s) below worked for me.

  • ssmith
    ssmith over 10 years
    Trying to get this working but getting error "Uncaught TypeError: undefined is not a function" which seems to occur in the declarative initialization. I've tried it with onDataBound on my viewModel as shown and as a global variable; same effect. Any ideas? Thanks.
  • ssmith
    ssmith over 10 years
    also why can i use data-select for the select event, but not data-databound for the databound event?
  • ssmith
    ssmith over 10 years
    This jsFiddle implies the databound binding is not available for the DropDownList widget: jsfiddle.net/7GE3b/1
  • Lars Höppner
    Lars Höppner over 10 years
    I set up a demo here; your jsfiddle is probably not working because it's using a pretty old version of kendo ui; re your question: if you want to bind methods on the view model to events, you need to do it in data-bind (if you set it up with data-select, it'll only look for the function in the global scope)
  • ssmith
    ssmith over 10 years
    No worries regarding edit. Updated jsfiddle to latest KendoUI and jQuery 1.9.1 here. jsfiddle.net/7GE3b/3 Error is now: Uncaught Error: The dataBound binding is not supported by the DropDownList widget. Thanks again.
  • Lars Höppner
    Lars Höppner over 10 years
    You used "events =" but it should be "events:" - check my plnkr again; I also updated your fiddle
  • Julie Lerman
    Julie Lerman over 10 years
    really? a stinkin' little colon caused him all that grief? ;)
  • ssmith
    ssmith over 10 years
    Thanks Lars - I'll try and apply the Plnkr (which works great!) to my actual code this evening, and will likely mark this as the answer.
  • Lars Höppner
    Lars Höppner over 10 years
    @Julie I keep arguing with my computer "My intentions were obvious! Surely, you must know what I meant?"; but he always scoffs and throws exceptions and other obscenities at me. Sometimes I wonder whether he takes me seriously at all.
  • Julie Lerman
    Julie Lerman over 10 years
    re your last comment, Lars: "like" oh, I mean "like:" :)
  • ssmith
    ssmith over 10 years
    What about this example Lars showed: plnkr.co/edit/kpQzzY9xWLxpt5aOlRj3?p=preview ? I haven't been able to get it to work in my actual code because I'm always getting the "Uncaught TypeError: undefined is not a function" error in my events:{} binding, but it clearly works in his demo. I can get it working with your code, but that's clearly a hack, since I now have to embed magic values in my client script instead of relying on the actual data from the datasource. Thanks!
  • Atanas Korchev
    Atanas Korchev over 10 years
    Why is this clearly a hack? Where does the "value" come from in your real scenario? Isn't it a property of something else? A data source doesn't support "selected value" on its own - it just represents an array of items. The linked example always selects the first data item once data has been received.
  • ssmith
    ssmith over 10 years
    Since the dropdownlist doesn't have an empty first item (e.g. "choose an item"), whatever the first item is, is the default item. By this same convention, whatever the first item is in the dropdownlist's datasource, should be the default selectedItem in the viewmodel (with its Duration property in my case shown in the bound span). It's a hack to include names/IDs from the datasource in the client code since I might change datasources in the future, or the order of that data might change, and my code will be incorrect unless I update the hardcoded values.
  • ssmith
    ssmith over 10 years
    In your example, if someone adds a new product that is listed before "Chang", let's say "Asparagus". Now, the span will read "Chang" but the first item in the dropdownlist will read "Asparagus", right?
  • Atanas Korchev
    Atanas Korchev over 10 years
    No. Both will stay the same - Chang. Unless the new item has the same ProductID as Chang. Anyway if you don't want to set initial value you would have to do something like Lars did. Somehow trigger the change of the dropdownlist: jsbin.com/pawiz/2/edit