How to bind AutoComplete related property to another field

12 posts, 1 answers
  1. Richard
    Richard avatar
    38 posts
    Member since:
    Mar 2011

    Posted 10 Feb 2014 Link to this post

    I have an AutoComplete working in a view.  It's used to look up and employee name.  There is a related field, EmployeeID which I need to populate if the user changes the assigned Employee.  It's natural to look up the name, however, the key field is EmployeeID and that's the value I really need to send back to the database when the View posts.

    I'm sending a list of objects (as Json) to the AutoComplete.  There are two properties, Name and ID.  Name is binding correctly to the AutoComplete and is being sent back on Post.  I think I need to use the Select event to get the ID value and populate that field but I can't find the object when I look at the debugging info in the browser when the page is running.  The event variable 'e' doesn't seem to have it.

    How can i do this?  Code is appended.

    View Code:
    <!-- AutoComplete text box for Issue To Employee -->
    <div class="row">
         <!-- IssuedToEmployeeName  NOTE: Autocomplete control MUST be the same name
                                          as the field that's being bound or it won't
                                          be sent back in the post operation!!!
          -->
        <span class="col-sm-2">
            <label>Issued To:</label>
        </span>
        <span class="col-sm-2">
           @(Html.Kendo().AutoComplete()
                        .Name("IssuedToEmployeeName")
                        .Delay(100)
                        .MinLength(2)
                        .DataTextField("IssuedToEmployeeName")
                        .Filter("contains")
                        .Events(e =>
                                {
                                    e.Change("onChange")
                                     .Select("onSelect")
                                     .DataBound("onDataBound");
                                })
                        .DataSource(source =>
                                        {
                                            source.Read(read =>
                                                {
                                                    read.Action("LookUpEmpName", "Home")
                                                        .Data("onAdditionalData");
                                                })
                                            .ServerFiltering(true);
      
                                        })
            )
        </span>
        <span class="col-sm-1">
            @Html.TextBoxFor(model => model.IssuedToEmployeeID, new { @class="form-control" })
        </span>
       <!-- IssuedOnDateTime -->
        <span class="col-sm-2">
            <label>Issue Date/Time:</label>
        </span>
        <span class="col-sm-3">
            @Html.TextBoxFor(model => model.IssuedOnDateTime, new { @readonly = "true" })
        </span>
        <span class="col-sm-2"></span>
    </div>

    <script>
        function onAdditionalData() {
            return { text: $("#IssuedToEmployeeName").val() };
        }

        function onChange(e) {
            //alert('onChange() event fired');
        }

        function onSelect(e) {
            alert('onSelect() event fired; event arg = ' + e.arguments);
        }

        function onDataBound(e) {
            //alert('onDataBound() fired. event arguement = ' + e); 
        }
    </script>


    Controller Code:
    //
    // LookUpEmpName()
    public JsonResult LookupEmpName(string text)
    {
        string[] empNames = DataAccess.LookupEmpName(text);
        List<NameSearchResult> result = new List<NameSearchResult>();
     
        if (empNames!=null)
        {
            foreach (string name in empNames) {
                NameSearchResult searchResult = new NameSearchResult();
                string empName = null;
                string empID = null;
                CommonMethods.ParseNameSearchResult(name, ref empName, ref empID);
                if((empName!=null) && (empID!=null)) {
                    searchResult.IssuedToEmployeeName = empName;
                    searchResult.EmployeeID = empID;
                }
                result.Add(searchResult);
            }
        }
        return Json(result, JsonRequestBehavior.AllowGet);
    }
          
  2. Petur Subev
    Admin
    Petur Subev avatar
    1882 posts

    Posted 13 Feb 2014 Link to this post

    Hello Richard,

    I am afraid it is not possible. The AutoComplete value is always equal to the text, there is no underlying value. If you need to send the ID of an item not the text, then you should consider using a DropDownList or a ComboBox widget.

    Regarding the ComboBox - if the text that the user entered does not match any item from the underlying collection then the value becomes the text that the user typed. You can, however fix this behavior with a small work-around discussed on the forums.

    Kind Regards,
    Petur Subev
    Telerik
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  3. UI for ASP.NET MVC is VS 2017 Ready
  4. Richard
    Richard avatar
    38 posts
    Member since:
    Mar 2011

    Posted 13 Feb 2014 in reply to Petur Subev Link to this post

    Hi Petur!
    I think that's a bug in the AutoComplete widget that should be fixed.  When first configuring the AutoComplete I sent it a list of strings from the Controller.  What rendered in the View was a selection list of 'undefined'.  The example I was working from t http://telerikhelper.net/2012/11/16/kendo-autocomplete-wrapper-for-asp-net-mvc/ shows sending a list of objects to the widget.  That object has multiple properties.  What's the point if you can't use them?

    In my case, the user is editing in the view and is changing the employee for the transaction.  It's more natural to look up the name via the AutoComplete widget than to have them enter the employee id which they would have to look up anyhow.  Since I can get the id into the list along with the name I want to populate the employee id field based on the selection.  Since the AutoComplete widget has that object at that moment it makes no sense that I can't make use of it at the point of selection. Now you're forcing me to do another workaround and another trip to the server.  I don't think a combobox is an appropriate choice.  I would have to download all employees in the company, hence my use of the AutoComplete widget.

     Is there another way to accomplish this?  I thought of looking up the empID based on the name but that will fail in cases of common names as the query won't produce a unique result.


  5. Answer
    Petur Subev
    Admin
    Petur Subev avatar
    1882 posts

    Posted 17 Feb 2014 Link to this post

    Hello Richard,

    No matter that you can use "list of objects with multiple properties" only the text property that you specified is used by the AutoComplete and only its value is send to the server. Here is an example:

    http://jsbin.com/voveyuzi/2/edit

    A work around would be to use a hidden field on your page and update its value based on the text when the AutoComplete changes its value.

    Kind Regards,
    Petur Subev
    Telerik
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  6. Richard
    Richard avatar
    38 posts
    Member since:
    Mar 2011

    Posted 25 Feb 2014 in reply to Petur Subev Link to this post

    I ended up doing the workaround by sending the concatenated name/id to the autocomplete text field and parsing them and setting the proper values using javascript in the onChange event.

    I still feel this is a bug and would like to see it fixed.  If I can send the object to the widget, it seems only logical that I should be able to get access to the selected object and its properties in an event of the widget.  This is a very common scenario.

    I'm marking this as answered.
  7. Kumar Bharat
    Kumar Bharat avatar
    9 posts
    Member since:
    Feb 2013

    Posted 30 Apr 2014 in reply to Richard Link to this post

    Hi Richard

    Please try my code. I hope it will matched with your requirement.

    This an example of employee page in which you need a Autocomplete for manager.
    User will type manager name and the corresponding EmployeeId of manager will be populated in "ManagerId"


    CSHTML
    ---------

    @model TelerikMvc5App.ViewModel.EmployeeViewModel


    @Html.TextBoxFor(m => m.ManagerName)
    @Html.TextBoxFor(m => m.ManagerId)

    JavaScript
    ---------------
     $(function () {

     var GetManagerURL = '@Url.Action("GetManager", "Employee", new { Area = "Employee" })'; // if you have cretaed are as "Employee"
     var GetManagerURL = '@Url.Action("GetManager", "Employee")'; // if you have not created any Area

    $("#ManagerName").kendoAutoComplete({
                placeholder: "Enter your manager name",
                minLength: 3, // you need to type atleast 3 charactes
                dataTextField: "EmployeeName",
            select: onManager_select,            
                dataSource: {
                    transport: {
                        read: GetManagerURL,
                        parameterMap: function (data) {
                            return {
                                input: $("#ManagerId").val(),
                                take: data.take,
                                skip: data.skip
                            };
                        }
                    },             
                    serverFiltering: true,
                    serverPaging: true,
                    pageSize: 10 // maximum list size in autocomplete
                }
            });

    });

    // do something when user will select an item from autocomplete
      function onManager_select(e) {         
            var selectedOne =  this.dataItem(e.item.index());
            $("#ManagerId").val(selectedOne.EmployeeId);
        }


    "Employee"  Controller
    -------------

    public JsonResult GetManager(string input, int take, int skip)
           {
                List<EmployeeViewModel> mgrList = new List<EmployeeViewModel>();
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 1, EmployeeName = "Prashad" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 2, EmployeeName = "Ashreewad" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 3, EmployeeName = "Sathish" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 11, EmployeeName = "Prashad1" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 21, EmployeeName = "Ashreewad1" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 31, EmployeeName = "Sathish1" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 12, EmployeeName = "Prashad2" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 22, EmployeeName = "Ashreewad2" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 32, EmployeeName = "Sathish2" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 13, EmployeeName = "Prashad3" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 23, EmployeeName = "Ashreewad3" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 33, EmployeeName = "Sathish3" });
                try
                {
                    if (!string.IsNullOrWhiteSpace(input))
                    {

                        if (mgrList != null)
                        {
                            mgrList = mgrList.Where(e => e.EmployeeName.ToLower().StartsWith(input.ToLower())).Take(take).ToList();
                        }
                    }
                }
                catch (Exception ex)
                {
                    //
                    return null;
                }
                return Json(mgrList, JsonRequestBehavior.AllowGet);
            }


    EmployeeViewModel
    --------------------------
    public class EmployeeViewModel
        {

          [Display(Name = "Employee Name")]
            public string EmployeeName { get; set; }
            public int EmployeeId { get; set; }    
           [Display(Name = "Manager")]
            [Required(ErrorMessage = "Manager is Required.")]
            public int ManagerId { get; set; }
            [Display(Name = "Manager")]
            public string ManagerName { get; set; }

    }

    Thanks
  8. Richard
    Richard avatar
    38 posts
    Member since:
    Mar 2011

    Posted 30 Apr 2014 in reply to Kumar Bharat Link to this post

    Thanks, Kumar!

    In reviewing your code, I don't see where the manager name is posted to the input control, or is that handled by the AutoComplete control itself, and your anonymous function takes care of the Id?  If that's the case, that would resolve my issue.  Currently I've worked around it with a hack.  I've concatenated the name and id with a delimiter and parse them out when the select action takes place.  Not what I wanted to do, but it does work.
  9. Kumar Bharat
    Kumar Bharat avatar
    9 posts
    Member since:
    Feb 2013

    Posted 02 May 2014 in reply to Richard Link to this post

    Hi Richard,

    I did a mistake on input control, it should be  input: $("#ManagerName").val(),

    Please see updated code

    CSHTML
    ---------

    @model TelerikMvc5App.ViewModel.EmployeeViewModel

    @Html.TextBoxFor(m => m.ManagerName) --- AutoComplete will bind here
    @Html.TextBoxFor(m => m.ManagerId)  --- OnSelect manager Id will be populated here

    JavaScript
    ---------------
     $(function () {

     var GetManagerURL = '@Url.Action("GetManager", "Employee", new { Area = "Employee" })'; // if you have cretaed are as "Employee"
     var GetManagerURL = '@Url.Action("GetManager", "Employee")'; // if you have not created any Area

    $("#ManagerName").kendoAutoComplete({
                placeholder: "Enter your manager name",
                minLength: 3, // enter min 3 char
                dataTextField: "EmployeeName",         
               
                template: '<span>#: EmployeeName # (Manager ID#: EmployeeId #)</span>', // if you need ant template
                select: onManagerSelect,           
                dataSource: {
                    transport: {
                        read: GetManagerURL,
                        parameterMap: function (data) {
                            $("#ManagerId").val(''); // this will clear manager id in every request or keypress
                            return {
                                input: $("#ManagerName").val(), // here manager name is posted to the input control
                                take: data.take,
                                skip: data.skip
                            };
                        }
                    },                
                    serverFiltering: true,
                    serverPaging: true,
                    pageSize: 10 // max list size
                }
            });

    });

      function onManagerSelect(e) {       
            var dataItem = this.dataItem(e.item.index());
            if (dataItem == 'undefined' || dataItem == null )
            {
                $("#ManagerId").val('');
                $("#ManagerName").val('');
            }
            else {           
                $("#ManagerId").val(dataItem.EmployeeId);
                $("#ManagerName").val(dataItem.EmployeeName);
            }       
        }

    Controller
    ---------------------------------
    public JsonResult GetManager(string input, int take, int skip)
            {
                List<EmployeeViewModel> mgrList = new List<EmployeeViewModel>();
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 1, EmployeeName = "Prashad" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 2, EmployeeName = "Ashreewad" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 3, EmployeeName = "Sathish" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 11, EmployeeName = "Prashad1" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 21, EmployeeName = "Ashreewad1" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 31, EmployeeName = "Sathish1" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 12, EmployeeName = "Prashad2" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 22, EmployeeName = "Ashreewad2" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 32, EmployeeName = "Sathish2" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 13, EmployeeName = "Prashad3" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 23, EmployeeName = "Ashreewad3" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 33, EmployeeName = "Sathish3" });
                try
                {
                    if (!string.IsNullOrWhiteSpace(input))
                    {

                        if (mgrList != null)
                        {
                            mgrList = mgrList.Where(e => e.EmployeeName.ToLower().StartsWith(input.ToLower())).Take(take).ToList();
                        }
                    }
                }
                catch (Exception ex)
                {
                    //
                    return null;
                }

                return Json(mgrList, JsonRequestBehavior.AllowGet);
            }

    EmployeeViewModel
    --------------------------
    public class EmployeeViewModel
        {

          [Display(Name = "Employee Name")]
            public string EmployeeName { get; set; }
            public int EmployeeId { get; set; }    
           [Display(Name = "Manager")]
            [Required(ErrorMessage = "Manager is Required.")]
            public int ManagerId { get; set; }
            [Display(Name = "Manager")]
            public string ManagerName { get; set; }

    }

    CSHTML
    ---------

    @model TelerikMvc5App.ViewModel.EmployeeViewModel

    @Html.TextBoxFor(m => m.ManagerName) --- AutoComplete will bind here
    @Html.TextBoxFor(m => m.ManagerId)  --- OnSelect manager Id will be populated here ( you may  use a hidden filed here)

    JavaScript
    ---------------
     $(function () {

     var GetManagerURL = '@Url.Action("GetManager", "Employee", new { Area = "Employee" })'; // if you have cretaed are as "Employee"
     var GetManagerURL = '@Url.Action("GetManager", "Employee")'; // if you have not created any Area

    $("#ManagerName").kendoAutoComplete({
                placeholder: "Enter your manager name",
                minLength: 3, // enter min 3 char
                dataTextField: "EmployeeName",         
               
                template: '<span>#: EmployeeName # (Manager ID#: EmployeeId #)</span>', // if you need ant template
                select: onManagerSelect,           
                dataSource: {
                    transport: {
                        read: GetManagerURL,
                        parameterMap: function (data) {
                            $("#ManagerId").val(''); // this will clear manager id in every request or keypress
                            return {
                                input: $("#ManagerName").val(), // here manager name is posted to the input control
                                take: data.take,
                                skip: data.skip
                            };
                        }
                    },                
                    serverFiltering: true,
                    serverPaging: true,
                    pageSize: 10 // max list size
                }
            });

    });

      function onManagerSelect(e) {       
            var dataItem = this.dataItem(e.item.index());
            if (dataItem == 'undefined' || dataItem == null )
            {
                $("#ManagerId").val('');
                $("#ManagerName").val('');
            }
            else {           
                $("#ManagerId").val(dataItem.EmployeeId);
                $("#ManagerName").val(dataItem.EmployeeName);
            }       
        }

    Controller
    ---------------------------------
    public JsonResult GetManager(string input, int take, int skip)
            {
                List<EmployeeViewModel> mgrList = new List<EmployeeViewModel>();
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 1, EmployeeName = "Prashad" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 2, EmployeeName = "Ashreewad" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 3, EmployeeName = "Sathish" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 11, EmployeeName = "Prashad1" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 21, EmployeeName = "Ashreewad1" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 31, EmployeeName = "Sathish1" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 12, EmployeeName = "Prashad2" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 22, EmployeeName = "Ashreewad2" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 32, EmployeeName = "Sathish2" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 13, EmployeeName = "Prashad3" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 23, EmployeeName = "Ashreewad3" });
                mgrList.Add(new EmployeeViewModel() { EmployeeId = 33, EmployeeName = "Sathish3" });
                try
                {
                    if (!string.IsNullOrWhiteSpace(input))
                    {

                        if (mgrList != null)
                        {
                            mgrList = mgrList.Where(e => e.EmployeeName.ToLower().StartsWith(input.ToLower())).Take(take).ToList();
                        }
                    }
                }
                catch (Exception ex)
                {
                    //
                    return null;
                }

                return Json(mgrList, JsonRequestBehavior.AllowGet);
            }

    EmployeeViewModel
    --------------------------
    public class EmployeeViewModel
        {

          [Display(Name = "Employee Name")]
            public string EmployeeName { get; set; }
            public int EmployeeId { get; set; }    
           [Display(Name = "Manager")]
            [Required(ErrorMessage = "Manager is Required.")]
            public int ManagerId { get; set; }
            [Display(Name = "Manager")]
            public string ManagerName { get; set; }

    }

    I hope it will work for you.

    Thanks
  10. Gareth
    Gareth avatar
    5 posts
    Member since:
    Oct 2012

    Posted 20 Jun 2014 Link to this post

    I still can't believe this isn't standard functionality in the autocomplete
  11. Ziming
    Ziming avatar
    9 posts
    Member since:
    Mar 2015

    Posted 15 Apr 2015 in reply to Richard Link to this post

    Hi Richard, FYI, I came cross the same issue and it seems now we can get all fields from the autocomplete, through the select event

         function onSelect(e) {
            var item = this.dataItem(e.item.index());
            $('#customer-name').val(item.Name);
        }

  12. Stu
    Stu avatar
    6 posts
    Member since:
    Apr 2015

    Posted 29 May 2015 Link to this post

    I am having the same exact issue, but am seeing another issue.  On the Autocomplete, I'm returning a list of objects through my controller using JSON.  My autocomplete is bound to a text field of my model.  I have the select event which is being fired.  I get the selected dataitem and can get any of the fields returned by the controller.  I set the corresponding Id of the model into my hidden field, but that Id field doesn't come back into my controller method in the model.  Here's the code I'm talking about:

    LocationAdd.vbhtml (custom popup editor template)

    @ModelType LocationVM
     
    <script>
        function onSelect_Loc(e) {
            var item = this.dataItem(e.item.index());
            alert(item.Id);
            $('#EsaId').val(item.Id);
            $('#CanvasPriority').val("PriTest");
        }
    </script>
     
    @Html.HiddenFor(Function(model) model.Id)
     
    @Html.LabelFor(Function(model) model.LocationName)
     @(Html.Kendo().AutoComplete() _
                        .Name("LocationName") _
                        .DataTextField("LocationName") _
                        .Events(Sub(e) e.Select("onSelect_Loc")) _
                        .DataSource(Sub(source)
                                            source.Read(Sub(read)
                                                                read.Action("GetAutoCompLocation", "Management").Data("onData_Loc")
                                                        End Sub) _
                                            .ServerFiltering(True)
                                    End Sub)
     )
     
    @Html.LabelFor(Function(model) model.CanvasPriority)
    @Html.TextBoxFor(Function(model) model.CanvasPriority)
     
    @Html.LabelFor(Function(model) model.AsssignedTo)
    @Html.TextBoxFor(Function(model) model.AsssignedTo)

    And in the onSelect event, I can write to other non-hidden input fields (like CanvasPriority above).  I see those values on the popup form, but when the model posts back to my controller, non of those fields (except the LocationName field) comes back in the model.  If I manually type into those fields and hit Update, I do see those fields in my model in the controller.

    So it seems any field written to within the Select event doesn't "stick" when sending model back to my controller.  Is there some other selector other than the simple $("#Id") that needs to be used?  I have been working on this for a week and am very frustrated at this point. 

  13. Georgi Krustev
    Admin
    Georgi Krustev avatar
    3707 posts

    Posted 02 Jun 2015 Link to this post

    Hello Stu,

    It is not very clear how the model is updated and send to the server.

    If you are using regular Html FORM element to post the values, then you will need to ensure that the every single input name attribute matches the corresponding Model fields. In this case, the process of matching the posted values to the Server Model is not related to Kendo UI by any means.

    If the inputs are bound to a ObservableObject model (for instance in grid editing) using MVVM value binding, then you will need to trigger the change event of the modified inputs in order to notify the related bindings.

    Regards,
    Georgi Krustev
    Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
Back to Top
UI for ASP.NET MVC is VS 2017 Ready