Binding DropDownList Value to complex model

10 posts, 0 answers
  1. Joseph
    Joseph avatar
    2 posts
    Member since:
    Nov 2019

    Posted 25 Mar Link to this post

    I'm wondering how I can two-way bind a TelerikDropDown selection to a complex model object rather than a primitive type such as an Id field. I'm trying to add a TelerikDropDownList to a page where the user can select a user-friendly name of an object and have that selection's value bound to the object itself rather than a primitive data type. For example:

    <TelerikDropDownList Data="@DropDownItems" TextField="LabelField" ValueField="ValueField" @bind-Value="@SelectedItem"/>
     
    @code {
        public List<GenericDropDownModel<Item>> DropDownItems { get; set; }
        public IEnumerable<Item> AllItems { get; set; }
        public Item SelectedItem { get; set; }
         
        protected override Task OnInitializedAsync()
        {
            AllItems = await DataService.LoadItems();
            SelectedItem = AllItems.First();
        }
         
        private void SetDropDownItems()
        {
            List<GenericDropDownModel<Item>> dropDownItems = new List<GenericDropDownModel<Item>>();
            if (AllItems != null)
            {              
                foreach(var item in AllItems)
                {
                    GenericDropDownModel<Item> dropDownItem = new GenericDropDownModel<Item>()
                    {
                        ValueField = item,
                        LabelField = item.Name
                    };
                    dropDownItems.Add(dropDownItem);
                }
            }
            DropDownItemsItems = dropDownItems;
        }
     
        public class GenericDropDownModel<T>
        {
            public string LabelField { get; set; }
            public T ValueField { get; set; }      
        }
     
        public class Item
        {
            public string Name { get; set; }
            public IModel Model { get; set; }
            public IEnumerable<IOtherModel> OtherModels { get; set; }      
        }
         
        public class Model
        {
            public int Id { get; set; }
            public string Name { get; set; }

            public string Info { get; set; }

            public IAnotherModel MoreDetails { get; set; }
        }
    }

     

    The drop-down is not allowing me to bind to non-primitive types nor specify a property of a property, i.e.:

    <TelerikDropDownList Data="@AllItems" TextField="Name" ValueField="@Model.Info" @bind-Value="@SelectedInfo"/>


    I get the following error with the first code snippet scenario:

    System.InvalidOperationException: Telerik.Blazor.Components.TelerikDropDownList`2[GenericDropDownModel`1[Item],Item] does not support the type 'Item'.
       at Telerik.Blazor.Components.Common.TelerikSelectBase`2.TryParseValueFromString(String value, TValue& result, String& validationErrorMessage)
       at Telerik.Blazor.Components.Common.TelerikSelectBase`2.set_CurrentValueAsString(String value)
       at Telerik.Blazor.Components.TelerikDropDownList`2.SelectItem(ListDataItem item)
       at Telerik.Blazor.Components.TelerikDropDownList`2.OnParametersSetAsync()
       at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
       at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
  2. Joseph
    Joseph avatar
    2 posts
    Member since:
    Nov 2019

    Posted 25 Mar in reply to Joseph Link to this post

    SetDropDownItems() was also meant to be placed in the setter for AllItems.
  3. Marin Bratanov
    Admin
    Marin Bratanov avatar
    5458 posts

    Posted 25 Mar Link to this post

    Hello Joseph,

    The following article explains the situation: https://docs.telerik.com/blazor-ui/knowledge-base/dropdowns-get-model and why only certain types can be bound to the Value parameter.

     

    Regards,
    Marin Bratanov
    Progress Telerik

    Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
    Our thoughts here at Progress are with those affected by the outbreak.
  4. Kasimier Buchcik
    Kasimier Buchcik avatar
    15 posts
    Member since:
    Jun 2020

    Posted 22 Jun in reply to Marin Bratanov Link to this post

    Hi Marin,

    would adding a bindable "SelectedItem" (plus "OnSelectedItemChange(d)") interfere with the existing "Value" mechanism?
    E.g. when using ObjectGraphDataAnnotationsValidator/ValidateComplexType, it would be convenient to bind directly to the complex type property. One cpuld still specify the ValueField to be used by the existing "Value" mechanism. Or would this somehow break something?

    <TelerikDropDownList Data="items"
        @bind-SelectedItem="editModel.Item"
        OnSelectedItemChanged="ev => SomeHandler(ev)"
        ValueField="Id"
        TextField="ItemName" />

    The edit model type:

    public class MyThingy
    {
        public Guid Id { get; set; }
     
        [Required]
        public string Name { get; set; }
     
        [Required]
        [ValidateComplexType]
        public MyThingyItem Item { get; set; }
    }
     
    public class MyThingyItem
    {
        public Guid Id { get; set; }
     
        [Required]
        public string ItemName { get; set; }
    }

    Regards,

    Kasimier Buchcik

  5. Marin Bratanov
    Admin
    Marin Bratanov avatar
    5458 posts

    Posted 23 Jun Link to this post

    Hi Kasimier,

    I will try to cover each point in your question:

    would adding a bindable "SelectedItem" (plus "OnSelectedItemChange(d)") interfere with the existing "Value" mechanism? - highly likely, binding two things to a similar field or a related field is likely to cause trouble.

    @bind-SelectedItem="editModel.Item" AND   OnSelectedItemChanged="ev => SomeHandler(ev)" - this is not possible in Blazor - the <ParameterName>Changed event that provides two-way binding cannot be used together with two-way binding. This applies to all components, including the standard ones.

    using a complex object type - If you use the current Value and point it to an identifier of the item, the OnChange or ValueChanged event we provide let you get the entire item from the data source (as per the article linked above), and so you can set it to the full custom object field in your view-model, which can, in turn, trigger any custom validation you have, or let you invoke any other business logic. At the same time the current value binding mechanism allows extremely flexible validation with various types (GUIDs are, by the way, very popular) and not just with strings, and also filtering of the data.

    With all that said, binding to an entire model is actually possible and only requires just a few lines of code in an event handler, while the current mechanism has the benefit of flexible validation and allows filtering. Moreover, if you want to get entire complex models, you can use the grid single row selection feature. You could even put it in a window or other popup (such as an animation container) to show when needed only.

     

    Regards,
    Marin Bratanov
    Progress Telerik

    Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
    Our thoughts here at Progress are with those affected by the outbreak.
  6. Kasimier Buchcik
    Kasimier Buchcik avatar
    15 posts
    Member since:
    Jun 2020

    Posted 23 Jun in reply to Marin Bratanov Link to this post

    - highly likely, binding two things to a similar field or a related field is likely to cause trouble.


    I can imagine that. Although if one needs to bind to a complex object then Telerik's advise it to do exactly that via the OnChange handler. It just moves responsibility over to the component consumer.

     

    @bind-SelectedItem="editModel.Item" AND   OnSelectedItemChanged="ev => SomeHandler(ev)" - this is not possible in Blazor - the <ParameterName>Changed event that provides two-way binding cannot be used together with two-way binding. This applies to all components, including the standard ones.

    "SelectedItemChanged" is not equal to "OnSelectedItemChanged". 
    Even just an "OnSelectedItemChanged" without a "SelectedItem" parameter would be an improvement because the OnChange/ValueChanged only provide the selected value. By the way: if OnChange would use an event args object holding the selected item and selected value, that would also help a bit.


    binding to an entire model is actually possible and only requires just a few lines of code in an event handler

    That can be said for any missing functionality. Multiply that with every DropDownList folks have to use in the next years.

    I wonder where the Blazor/Razor component world is headed to in the future. If the XAML/WPF/WinUI and e.g. the Angular world has a way of binding to complex objects but Blazor not... then something's wrong. Especially PWA's are not only about setting some GUIDs and sending those to the server. Working with object graphs should be common here.

    I find it great that Telerik wrote the Blazor components from scratch and does not just wrap JS widgets. But I currently tend to think that Telerik's Blazor components inherited too much philosophy, restrictions and pecularities of its Kendo for jQuery implementation. 
    Another thingy I just realized: one has to set TelerikDropDownList.DefaultText=" " (i.e. to at least a space) in order to have a way of clearing the selection. No "clear" button yet. This feels a lot like Kendo for jQuery.
    I think such restrictions and peculiarities will become hard to work with because in Blazor one can't add missing functionality like in Kendo for jQuery.
    May I ask which team is writing the components? Do Telerik's WPF/WinUI/Angular folks contribute to Blazor components?

    My thoughts should be taken with a grain of salt, because I'm really new to Blazor + Telerik's Blazor components (3 weeks), so I may be missing the big picture and sound silly :-) 

    Regards
    Kasimier Buchcik

    Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
    Our thoughts here at Progress are with those affected by the outbreak.
  7. Marin Bratanov
    Admin
    Marin Bratanov avatar
    5458 posts

    Posted 23 Jun Link to this post

    Hello Kasimier,

    If entire model binding is provided, it will come with the suitable events. I would not speculate on their exact nature until a feature is implemented, as I can go really off the mark. Right now, the component does not carry a selected item reference in itself so it can pass it to a handler, it is up to the view model to fetch such a reference.

    On the default text - an item with a default value is what "clears" the value in such a scenario, and DefaultText provides that. The DefaultText simply matches the default value of the Value field - for a string that's null. I am attaching a short video that shows how this works below, and also a project you can use to test this. So, you can think of the DefaultText as the TextField of a model you bind to, where the ValueField is the default value for its type. We hide this behind a simle string property so you don't have to stuff your data with padding.

    On a clear button - a dropdown list does not have one by design, but a combo box or an autocomplete do as they can be text inputs. A dropdownlist is a <select> element equivalent, but with better design. So, clearing it is done through the DefaultText feature.

    As for how Blazor should work - I would personally rather transmit an ID (number, string, guid, whatever my models and data use) than an entire model if I need a lookup. Of course, this is subject to personal preferences, existing APIs and coding approaches. Also, if you need an async operation, you need an async event anyway, plain MVVM binding can't achieve that (barring some fidgeting with nested components and using the ParametersSet event). So, there is no universally correct answer. The DropDownList is, first and foremost, a form component, and so it provides binding to primitive types and validation. Fetching an entire model from it is not in the immediate plans for out-of-the-box support, mainly because it is extremely easy through the OnChange event. As to whether comparing a web technology with desktop technologies is fair, or comparing to a framework that's a decade old, while blazor has been official for one month - that's also a question that does not have a definitive answer in my view.

    Lastly, if entire model selection is so crucial to you that you can't wrap it in a component that steps on the dropdownlist for reuse - I'd suggest going with the grid single row selection.

    Regards,
    Marin Bratanov
    Progress Telerik

    Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
    Our thoughts here at Progress are with those affected by the outbreak.
  8. Kasimier Buchcik
    Kasimier Buchcik avatar
    15 posts
    Member since:
    Jun 2020

    Posted 23 Jun in reply to Marin Bratanov Link to this post

    If entire model binding is provided, it will come with the suitable events. I would not speculate on their exact nature until a feature is implemented, as I can go really off the mark

    If this will ever be implemented then the "ValueField" would have to be empty, right? If yes then the ValueField's default value (which is "Value") could become a problem because devs would have to explicitely set that to "", right?

    I found a comment of Steve Sanderson in an AspNetCore Binding to objects issue.
    "This could make sense for “select”, but would not for any other type of form control. We might do it for select at some point, but it will depend on what usage patterns arise and what level of community demand there is."
    I find it very irritating that binding to members of complex type is presented as something unheard of. I'm happy that - at least - we had not had such discussions in the WPF community. Sometimes one just needs to bind to such a property; especially in the context of offline/PWA applications where data might not always be intended to be sent to a server immediately - or ever. Sometimes one wants to build an object graph.
    Dunno, having worked with other UI technologies, this restriction does not make sense to me. But maybe there's a deeper technological hurdle that I'm not aware of.

    dropdown list does not have one by design

    I understand. On the other hand e.g. Angular/PrimeNG has a dropdown with a clear button by design. If designs differ that I would go with the design that has more functionality in case I need it.
    Wouldn't hurt to add a clear button for folks that need one.
    I guess with Kendo UI for jQuery one could simply wrap or derive the drop down and hack together such a button. Would this be possible with Blazor?

     if you need an async operation, you need an async event anyway, plain MVVM binding can't achieve that

    This is true for any binding. It isn't restricted to binding to properties of complex type.

  9. Marin Bratanov
    Admin
    Marin Bratanov avatar
    5458 posts

    Posted 23 Jun Link to this post

    Hello Kasimier,

    To each question:

    What will happen to Value and ValueField and the component type - That's one of the key problems - the component is generic based on the type of the Value field, so implementing an entire selected item will likely be either a breaking change, or will require specific configuration, or both. I can't answer this question more accurately because doing so would require that the research and implementation of such a feature are done.

    The standard Blazor select not supporting entire models as values - we are following the same pattern as Microsoft right now - the standard form type controls bind to simple values. If other requests and usage patterns arise, things will be considered. I suppose that's one of the major differences between the Web and Desktop - with the Web we have standard approaches for forms and their values that date back many years, and those patterns shape things to this day.

    Binding to complex objects and async events - my point in saying that is that at the moment, to get the model you need an event. TO fetch data from an endpoint, you need it too, for the majority of cases - so, the code you need to add to get models is not that much.

    Designs in other technologies - It is their prerogative to choose to add one, each vendor, framework and style can choose their own design rules and guidelines.

    Hacking a clear button in Blazor - your best bet would be using some CSS to position the desired button above the dropdown list. Then, a simple @onclick handler for it can set the value to whatever you want. Maybe you would also like to prevent the click, too, so that the dropdown does not open. At the moment - the Telerik ComboBox component offers a clear button so you may want to consider that instead of hacking a solution on the dropdownlist.

    Regards,
    Marin Bratanov
    Progress Telerik

    Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
    Our thoughts here at Progress are with those affected by the outbreak.
  10. Kasimier Buchcik
    Kasimier Buchcik avatar
    15 posts
    Member since:
    Jun 2020

    Posted 24 Jun in reply to Marin Bratanov Link to this post

     If other requests and usage patterns arise, things will be considered. I suppose that's one of the major differences between the Web and Desktop - with the Web we have standard approaches for forms and their values that date back many years, and those patterns shape things to this day.

    I guess when people start migrating WinForm/WPF apps to RIA/PWA Blazor apps, then Telerik will have lots of similar disussions. There's no glory in it to find out that one can't simply add a drop-down-list somewhere, give it a collection of complex objects and receive the selected complex object. For me it's not the difference between web and desktop, but the difference between simple classic "form" input and RIA applications.
    I'm quite sure that people do realize that, but somehow we are all waiting because someone at MS didn't press the start button.
    Thus, I have to wait and jump through the hoops until then :-) 
Back to Top