MVVM binding - how to work with multiple binds

0 Answers 115 Views
MVVM Templates
Paul
Top achievements
Rank 1
Iron
Iron
Veteran
Paul asked on 17 Oct 2022, 09:26 PM | edited on 17 Oct 2022, 09:26 PM

I'm specifically working with the scheduler, however I believe this applies to any MVVM items.

When double click an event in the scheduler, my editor template is rendered and Kendo binds this to the event being edited. This happens for "free" as I do not do this. Consider this element:

<div data-container-for="title">
   <input type="text" data-role="textbox" name="title" required="required" data-bind="value:title" />
</div>

The binding to title works perfectly fine and I see my value.

Next, my event has a CategoryId which tags the event and what it is. This is a property I have added. Let me point out that if I replace title above with CategoryId, the integer value of the category displays. So this value is known and part of the bound object. However, I am using a very custom templated dropdown for this.

<div data-container-for="CategoryId">
   <input id="CategoryId" data-bind="value: CategoryId, source: categoriesDataSource" data-role="dropdownlist" data-auto-width="true"
          data-header-template="categorySearchHeaderTemplate" data-template="categorySearchTemplate" data-text-field="Description"
          data-value-field="Id" data-auto-bind="false" />
</div>

Hopefully this makes sense. My categories have an Id and a Description. There is a list of them. The event has a CategoryId which holds the value of what category is selected. However this element has its own datasource! Note the data-bind.

// This datasource is created and then kendo.bind() is called to bind to my dropdown
var categoriesViewModel = kendo.observable({
   categoriesDataSource: new kendo.data.DataSource({
      data: jsonSerializedCategories
   })
});

This drop down works perfectly fine for templates and for the elements in it. See screenshot below. But the value binding does not work. This is obviously because I had to set my own datasource on it. The value is not found in that datasource; it's in the one Kendo generated and applied to the popup. What is the proper way to set this up? The value is in one datasource and the elements here are in the other one I created.

Paul
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 18 Oct 2022, 08:13 PM | edited

This seems like a terrible hack but this works for my categories. However other fields here use a datasource with a read URL and are not pre-filled with data. So this only works for this one scenario. Still need to get other drop downs working. Same question still applies. Running this logic in the edit event.

if (!initializedEditor) {
   // Adding categories one time only to the model
   initializedEditor = true;

   // Hack - find the title element and the kendoBindingTarget. Assign categories in it. Then force a rebind.
   let kendoBindingTarget = $("input[name='title']")[0].kendoBindingTarget;
   kendoBindingTarget.source.categories = @Html.Raw(JsonConvert.SerializeObject(categoriesSmall));
   kendo.bind(arguments[0].container, arguments[0].event);
}

Neli
Telerik team
commented on 20 Oct 2022, 11:36 AM

Hi Paul,

Could you please confirm if the entire solution is using MVVM or you are using the bindings only for the custom template?

As far as I understand you managed to find a solution to the issue. However, linked here you will find a small Dojo example where a DropDownList with remote datasource is configured in a custom editor template. In the example the edit event of the Scheduler is handled. In the event handler a dataSource to the DropDownList is set using the setDataSource method. 

- https://docs.telerik.com/kendo-ui/api/javascript/ui/dropdownlist/methods/setdatasource

And also the value to the widget is set using the value method:

edit: function(e) {           
            $("#ownerId").data("kendoDropDownList").setDataSource(products);
            $("#ownerId").data("kendoDropDownList").value(e.event.ownerId);
},

Please take a look at the provided example and let me know if the described approach is suitable in your scenario.

Regards,

Neli

 

 

Paul
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 20 Oct 2022, 02:35 PM | edited

Neli,

Thank you for this! This gets me closer. DropDownList elements that are prefilled with data work now. In your example, you have a transport with a read event. This does not work at all for me. In my case my read is an AutoComplete. To answer your other question, this is entirely an MVC project however I have to use MVVM inside the template editor for the scheduler. Consider the customers autocomplete:

<div data-container-for="CustomerId">
   <input id="CustomerId" data-bind="value: CustomerId, events: { select: customerSelected }"
          data-role="autocomplete" data-enforce-min-length="true" data-min-length="3"
          data-text-field="Name" data-value-field="Id" />
</div>

Here's the data source and JS:

var customersDataSource = new kendo.data.DataSource({
   serverFiltering: true,
   transport: {
      read: {
         url: "myUrlToSearch",
         /* simply returns e.filter.filters[0].value */
         data: autoCompleteSearchData
      }
   },
   customerSelected: function () {
      // Supposed to be an event on select!
      debugger;
   }
});

// Now set the value
let customersAc = $("#CustomerId").data("kendoAutoComplete");
customersAc.setDataSource(customersDataSource);
customersAc.value(e.event.CustomerId);

Setting the value to an integer doesn't work. The customer object is not loaded and the autocomplete makes no attempt to go and get the object from the server. No read happens. So I end up with this:

This is the internal ID. The datasource is correct because if I type a few characters I see myself:


Next I tried grabbing the real object and setting the value that way.

$.ajax({
   type: "GET",
   url: "myUrlToGetCustomers",
   data: {
      customerId: e.event.CustomerId
   }
}).done(function (data) {
   // A single JSON customer comes back, no array, just { Id: 333, Name: "foo" }
   customersAc.value(data);
});

Unfortunately this does not work either.

When I select a customer after typing text, the templates and objects work fine:

So there are two remaining issues: How do I set the value? The auto complete never attempts to get it. I have a JavaScript breakpoint on the data method to confirm this as well as one in my controller where the load would occur.

Second, how can I assign a SELECT event? It just crashes saying the select event is not defined. Where does the event go? I have a customerSelected event on my data source above, however I tried a ton of other places:

// Attempt to see where the select method should go. On the customer data source 
// did not work. It did work when it was on the customer view model but I do not have
// one anymore. Perhaps getting the binding target from another component will work?
let kendoBindingTarget = $("input[name='title']")[0].kendoBindingTarget;

// None of these break in the debugger, I only get a Kendo error saying the event is not defined
e.customerSelected = function () { debugger; };
e.sender.customerSelected = function () { debugger; };
e.sender.dataSource.customerSelected = function () { debugger; };
kendoBindingTarget.customerSelected = function () { debugger; };
kendoBindingTarget.source.customerSelected = function () { debugger; };

Paul
Top achievements
Rank 1
Iron
Iron
Veteran
commented on 21 Oct 2022, 02:08 PM | edited

After calling the dataSource.read() method, passing in a custom JSON message including the ID I want, my dataSource is now correct. Calling value(CustomerId) just puts the integer in there and not the name.

Finding more documentation online it appears that value() is actually called with the data-text-field. This is crazy! What if I have Steve Smith of Anytown and Steve Smith of Smallville? The users searches for Steve Smith and finds two. The value is the internal ID. The collapsed display text is "Steve Smith". The autocomplete template would have shown phone, city, and address to correctly select the right one. Maybe the guy moved, maybe there are two of them?! Anyway, calling value() with my TEXT finally makes the screen look correct. But there seems to be no way to get the dataItem of what is selected. Is it just a piece of text? Or is this guy really selected as an object?

The SELECT binding turns out to be impossible in the data-bind from what I can tell. Because the function needs to exist in the view model of the scheduler editor which I cannot find. So, I am calling bind("select", function) when I am assigning the dataSource. Finally the select method fires.

Getting closer. But SELECT doesn't fire when I set programatically so all I really need to do now is clarify whether my autocomplete has a real value in it or just text that appears to be a selected customer.

Neli
Telerik team
commented on 25 Oct 2022, 09:25 AM

Hi Paul,

The AutoComplete Widget doesn't expose a dataValueField configuration property. The widget supports only text binding and not value binding. Thus its value is set according to the dataTextField configuration. If you need to use the value method and set the id, you could take a look at the ComboBox component

Here you will find the Dojo example sent previously but using the ComboBox.

Let me know in case you have additional questions on the matter. 

Regards,

Neli

No answers yet. Maybe you can help?

Tags
MVVM Templates
Asked by
Paul
Top achievements
Rank 1
Iron
Iron
Veteran
Share this question
or