DDL not reflecting changes in controller

10 posts, 1 answers
  1. Armin
    Armin avatar
    8 posts
    Member since:
    Nov 2012

    Posted 28 Aug Link to this post

    I have a complex solution where I need to change values of DDLs from the controller.

    Although I can get the inital values set - changes made by a post do not change the DDL selections.

    My Code:

    public class HomeController : Controller {
        public class ConfigEntryLine {
            public long ItemID { get; set; }
            public string ItemText { get; set; }
            public ConfigEntryLine() { }
            public ConfigEntryLine(long pItemIDVal, string pItemText) {
                ItemID = pItemIDVal;
                ItemText = pItemText;
            }
        }
     
        public class ConfigElement {
            public long ElementID { get; set; }
            public long SelectedID { get; set; }
     
            public List<ConfigEntryLine> Lines { get; set; }
            public ConfigElement() {
                Lines = new List<ConfigEntryLine>();
            }
            public static ConfigElement CreateElement(long pInitID) {
                ConfigElement ceRet = new ConfigElement();
                ceRet.ElementID = pInitID;
                if(pInitID == 1) {
                    for(int nX = 1; nX < 5; nX++) {
                        ceRet.Lines.Add(new ConfigEntryLine(nX, $"Outer Val: {nX}"));
                    }
                    ceRet.SelectedID = 2;
                }
                else if(pInitID == 2) {
                    for(int nX = 11; nX < 15; nX++) {
                        ceRet.Lines.Add(new ConfigEntryLine(nX, $"First Val: {nX}"));
                    }
                    ceRet.SelectedID = 12;
                }
                else {
                    for(int nX = 21; nX < 25; nX++) {
                        ceRet.Lines.Add(new ConfigEntryLine(nX, $"Second Val: {nX}"));
                    }
                    ceRet.SelectedID = 22;
                }
                return (ceRet);
            }
        }
     
        public class ProductConfigurationEntry {
            public string ActionName { get; set; }
            public string InfoMessage { get; set; }
            public ConfigElement OuterElement { get; set; }
            public List<ConfigElement> Elements { get; set; }
             
     
            public ProductConfigurationEntry() {
                Elements = new List<ConfigElement>();
            }
     
     
            public static ProductConfigurationEntry CreateEntry() {
                ProductConfigurationEntry pceRet = new ProductConfigurationEntry();
                pceRet.InfoMessage = "Clear";
                pceRet.OuterElement = ConfigElement.CreateElement(1);
                pceRet.Elements.Add(ConfigElement.CreateElement(2));
                pceRet.Elements.Add(ConfigElement.CreateElement(3));
                return (pceRet);
            }
        }
     
        [HttpGet]
        public ActionResult Index() {
            return View(ProductConfigurationEntry.CreateEntry());
        }
        [HttpPost]
        public ActionResult Index(ProductConfigurationEntry pEntry) {
            ProductConfigurationEntry pceRet = ProductConfigurationEntry.CreateEntry();
            if(pEntry.ActionName == "ONE") {    //resend outer made selection - keep first selected on client - change second from default to first
                pceRet.InfoMessage = $"{pEntry.OuterElement.SelectedID}, {pEntry.Elements[0].SelectedID}, {pEntry.Elements[1].SelectedID} resend outer made selection -keep first selected on client - change second from default to first";
                pceRet.OuterElement.SelectedID = pEntry.OuterElement.SelectedID;
                pceRet.Elements[0].SelectedID = pEntry.Elements[0].SelectedID;
                pceRet.Elements[1].SelectedID = 21;
            }
            if(pEntry.ActionName == "TWO") {    //set outer to first - set second to last - keep third default
                pceRet.InfoMessage = $"{pEntry.OuterElement.SelectedID}, {pEntry.Elements[0].SelectedID}, {pEntry.Elements[1].SelectedID} set outer to first - set second to first - keep third default";
                pceRet.OuterElement.SelectedID = 1;
                pceRet.Elements[0].SelectedID = 11;
            }
            return View(pceRet);
        }

    My View:

    @model WebApplication4.Controllers.HomeController.ProductConfigurationEntry
    @{
        ViewBag.Title = "Home Page";
    }
     
    <h2>@Model.InfoMessage</h2>
    @Html.ActionLink("Abbrechen", "Index", null, new { @class = "btn btn-success" })
    @using(Html.BeginForm()) {
        @Html.AntiForgeryToken()
        <div class="form-horizontal">
            <div class="form-group">
                @Html.HiddenFor(model => model.ActionName)         
            </div>
            <div class="form-group">
                @(Html.Kendo().DropDownListFor(m => m.OuterElement.SelectedID)
                .DataTextField("ItemText")
                .DataValueField("ItemID")
                .AutoBind(true)
                .HtmlAttributes(new { style = "width:280px;font-size:small" })
                .ValuePrimitive(true)
                .BindTo(Model.OuterElement.Lines)
                )
                @Model.OuterElement.SelectedID
            </div>
            <div class="form-group">
                <table class="table table-striped table-hover">
                    @{int nCnt = Model.Elements.Count;}
                    @for(int nX = 0; nX < nCnt; nX++) {
                        <tr>
                            <td class="last-col">
                                @Html.EditorFor(model => model.Elements[nX], new { htmlAttributes = new { @class = "form-control" } })
                                @Model.Elements[nX].SelectedID
                            </td>
                        </tr>
                    }
                </table>
                <div class="form-group" style="margin-right:0px">
     
                    <div class="pull-right">
                        <input id="SubHiddenButton" type="submit" style="display:none" value="Save" name="SubmitButton" />
                        <input id="SubButton" type="submit" value="Check 1" class="btn btn-success" name="SaveButton" onclick="return SaveData(this)" />
                        <input type="submit" value="Check 2" class="btn btn-info" name="CancelButton" onclick="return CheckSaveName(this)" />
                    </div>
                </div>
            </div>
        </div>
                        }
     
     
    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
        <script>
            function SaveData(theButton) {
                $("#ActionName").val("ONE");
                $("#SubmitButton").click();
            }
            function CheckSaveName(theButton) {
                $("#ActionName").val("TWO");
                $("#SubmitButton").click();
            }
        </script>
    }

    My Editor:

    @model WebApplication4.Controllers.HomeController.ConfigElement
    @Html.HiddenFor(model => model.ElementID)
    @(Html.Kendo().DropDownListFor(m => m.SelectedID)
        .DataTextField("ItemText")
        .DataValueField("ItemID")
        .AutoBind(true)
        .HtmlAttributes(new { style = "width:280px;font-size:small" })
        .ValuePrimitive(true)
        .BindTo(Model.Lines)
    )

    When I Get the page in every DDL the second line is selected - so far so good.

    After a post I get the new selected IDs - also correct.

    But changing the values on the server an passing back the changed data doesn_t change the selection in the DDLs.

    I display the "SelectedID" just to check - and YES this value reflects the changes - only the DDLs stay a their former selection.

    Is this a bug - or am I missing something, or?????

  2. Veselin Tsvetanov
    Admin
    Veselin Tsvetanov avatar
    621 posts

    Posted 30 Aug Link to this post

    Hi Armin,

    Attached you will find a simplified runnable solution based on the snippets sent. You will notice that after submitting the form, the model is redefined in the Controller and returned to the client. Both the selected values and the list of items in the DropDownList are properly altered.

    May I ask you to modify this sample, so it reproduces the issue observed at your end and send it back to us?

    Regards,
    Veselin Tsvetanov
    Progress Telerik
    Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
  3. Armin
    Armin avatar
    8 posts
    Member since:
    Nov 2012

    Posted 30 Aug Link to this post

    When I try to download the zip I get a timeout error.

    So I can't check your code.

    But anyhow - my sample code is complete - just for demo purposes I created a new empty project.
    This is by the way the reason, that all "data classes" are inside the home controller.

    So just take a default solution (with telerik MVC) - replace the home controller with my code provided - and do the same with the index view.
    Would be enough to reproduce the problem.

     

  4. Veselin Tsvetanov
    Admin
    Veselin Tsvetanov avatar
    621 posts

    Posted 31 Aug Link to this post

    Hi Armin,

    Thank you for the additional information provided. Based on that I was able to reproduce the issue discussed.

    After several tests, I noticed, that the default @Html.DropDownListFor() helper behaves in the same way in the discussed scenario (sample project attached). As the Kendo DropDownListFor follows the default approach used for such helpers, it is also not able to update the selected value.

    To overcome this issue, I would suggest you to save the currently selected value somewhere on the page, handle the dataBound event of the Kendo DropDownList and set its value() to the saved selected value:
    <div id="valueContainer">
        @Html.DisplayText("SelectedID")
    </div>

    @(Html.Kendo().DropDownListFor(m => m.SelectedID)
        .DataTextField("ItemText")
        .DataValueField("ItemID")
        .AutoBind(true)
        .HtmlAttributes(new { style = "width:280px;font-size:small" })
        .ValuePrimitive(true)
        .BindTo(Model.Lines)
        .Events(e => e.DataBound("onDataBound"))
    )

    function onDataBound(e) {
        var value = $('#valueContainer').text().trim();
        e.sender.value(value);
    }

    Attached you will find a sample solution implementing this workaround too.

    Regards,
    Veselin Tsvetanov
    Progress Telerik
    Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
  5. Armin
    Armin avatar
    8 posts
    Member since:
    Nov 2012

    Posted 31 Aug Link to this post

    So I gave this a try...

    And yes - it works - at least with one DDL.

    But like I expected this it doesn't work with the EditorFor element.

     

    So I changed my view to:

    @model WebApplication4.Controllers.HomeController.ProductConfigurationEntry
    @{
        ViewBag.Title = "Home Page";
    }
     
    <h2>@Model.InfoMessage</h2>
    @Html.ActionLink("Abbrechen", "Index", null, new { @class = "btn btn-success" })
    @using(Html.BeginForm()) {
        @Html.AntiForgeryToken()
        <div class="form-horizontal">
            <div class="form-group">
                @Html.HiddenFor(model => model.ActionName)         
            </div>
            <div id="valueContainer">
                @Html.DisplayText("OuterElement.SelectedID")
            </div>
            <div class="form-group">
                @(Html.Kendo().DropDownListFor(m => m.OuterElement.SelectedID)
                .DataTextField("ItemText")
                .DataValueField("ItemID")
                .AutoBind(true)
                .HtmlAttributes(new { style = "width:280px;font-size:small" })
                .ValuePrimitive(true)
                .BindTo(Model.OuterElement.Lines)
                 .Events(e => e.DataBound("onDataBound"))
                )
                @Model.OuterElement.SelectedID
            </div>
            <div class="form-group">
                <table class="table table-striped table-hover">
                    @{int nCnt = Model.Elements.Count;}
                    @for(int nX = 0; nX < nCnt; nX++) {
                        <tr>
                            <td class="last-col">
                                @Html.EditorFor(model => model.Elements[nX], new { htmlAttributes = new { @class = "form-control" } })
                                @Model.Elements[nX].SelectedID
                            </td>
                        </tr>
                    }
                </table>
                <div class="form-group" style="margin-right:0px">
     
                    <div class="pull-right">
                        <input id="SubHiddenButton" type="submit" style="display:none" value="Save" name="SubmitButton" />
                        <input id="SubButton" type="submit" value="Weiter" class="btn btn-success" name="SaveButton" onclick="return SaveData(this)" />
                        <input type="submit" value="Als Vorlage speichern" class="btn btn-info" name="CancelButton" onclick="return CheckSaveName(this)" />
                    </div>
                </div>
            </div>
        </div>
                        }
     
     
    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
        <script>
            function SaveData(theButton) {
                $("#ActionName").val("ONE");
                $("#SubmitButton").click();
            }
            function CheckSaveName(theButton) {
                $("#ActionName").val("TWO");
                $("#SubmitButton").click();
            }
            function onDataBound(e) {
                var value = $('#valueContainer').text().trim();
                e.sender.value(value);
            }
        </script>
    }

    And my ConfigElement.cshtml to:

    @model WebApplication4.Controllers.HomeController.ConfigElement
    @Html.HiddenFor(model => model.ElementID)
    <div id="valueContainer1">
        @Html.DisplayText("SelectedID")
    </div>
     
    @(Html.Kendo().DropDownListFor(m => m.SelectedID)
        .DataTextField("ItemText")
        .DataValueField("ItemID")
        .AutoBind(true)
        .HtmlAttributes(new { style = "width:280px;font-size:small" })
        .ValuePrimitive(true)
        .BindTo(Model.Lines)
         .Events(e => e.DataBound("onDataBound2"))
    )
    <script>
        function onDataBound2(e) {
            var value = $('#valueContainer1').text().trim();
            e.sender.value(value);
        }
    </script>

    Here the first DDL changes - and the second get's unselected (since the event handler tries to set it to a value earlier found on the page).
    In simple words - due to the fact that you use a "named placeholder" (valueContainer) this will not work in the case where I have a dynamic number of DDLs.

     

  6. Veselin Tsvetanov
    Admin
    Veselin Tsvetanov avatar
    621 posts

    Posted 04 Sep Link to this post

    Hello Armin,

    In order the suggested to work with a custom editor, you will have to implement a dedicated dataBound event handler for each created instance of the DropDownList:
    <div id='@("valueContainer" + Model.ElementID.ToString())'>
        @Html.DisplayText("SelectedID")
    </div>
     
    <script>
        window['onDataBound' + @(Model.ElementID.ToString())] = function (e) {
            var value = $('#valueContainer' + @(Model.ElementID.ToString())).text().trim();
            e.sender.value(value);
        }
    </script>
     
    @(Html.Kendo().DropDownListFor(m => m.SelectedID)
        .DataTextField("ItemText")
        .DataValueField("ItemID")
        .AutoBind(true)
        .HtmlAttributes(new { style = "width:280px;font-size:small" })
        .ValuePrimitive(true)
        .BindTo(Model.Lines)
        .Events(e => e.DataBound("onDataBound" + Model.ElementID.ToString()))
    )

    Attached you will find a sample project, implementing the above suggestion.

    Regards,
    Veselin Tsvetanov
    Progress Telerik
    Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
  7. Armin
    Armin avatar
    8 posts
    Member since:
    Nov 2012

    Posted 04 Sep Link to this post

    Hi Veselin,

     

    thank you for your efforts - I thought what I want is pretty basic - but the suggested solution becomes more and more complicated.

    After some analyses I finally got a solution - a very simple one!

    I only had to change my controller code to clear the modelstat just before I return the view.

    ...
    ModelState.Clear();
    return View(....

    With this everthing works as expected (no need for extra handlers).

     

  8. Answer
    Veselin Tsvetanov
    Admin
    Veselin Tsvetanov avatar
    621 posts

    Posted 04 Sep Link to this post

    Hello Armin,

    Thank you for shearing with us this simple and appropriate approach, that would properly clear the state of the ViewModel. I believe, that it will be of use for other developers too.

    Regards,
    Veselin Tsvetanov
    Progress Telerik
    Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
  9. Armin
    Armin avatar
    8 posts
    Member since:
    Nov 2012

    Posted 04 Sep Link to this post

    Sounds like earning some telerik points :-)
  10. Veselin Tsvetanov
    Admin
    Veselin Tsvetanov avatar
    621 posts

    Posted 06 Sep Link to this post

    Hello Armin,

    Your pints have been updated accordingly. Thank you again!

    Regards,
    Veselin Tsvetanov
    Progress Telerik
    Try our brand new, jQuery-free Angular 2 components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
Back to Top