ASP.Net MVC Dynamic input bound to same controller property

不羁的心 提交于 2020-01-17 01:35:07

问题


I have 2 controller fields say Type and Data. Depending on value selected for Type (Date or Text), I want to display Data field dynamically as either a text input or a custom timepicker input. Since only one will be rendered at any time, I need to bind with the same property name (Data). This is what I am trying:

@if (Model.Type == "Date")
{
   // custom timepicker control goes here
   <input asp-for="Data" class="form-control timepicker"/>
}
else
{
   <input asp-for="Data" class="form-control text-input" type="text"/>
}

On page load only text input is rendered, and it shows/hides based on Type selected. The timepicker input is never displayed (the html is not generated at all).

Is there a way to achieve this in MVC?


回答1:


You can not have two <input> elements with the same name. If a <form> containing multiple inputs with the same name is posted, the MVC model binder will only bind one value from the last input.

To achieve what you want, you have two options:

  • Either have only one input with name="Data" of type="text" in the View, and let the timepicker write the time as a string to this input. Then in the controller, parse this input value depending on the selected Type.

  • Or have two inputs with name="TextData" and name="TimeData", and disable and hide one of these inputs using JS depending on the selected Type. In the controller, read the value from the right input depending on the selected Type. This is arguably the cleaner solution.

In MVC5 the second solution would look like this (I am not familiar with MVC-Core):

@model MyViewModel
@using (Html.BeginForm("Submit", "MyController", FormMethod.Post)) {
    @Html.EditorFor(m => m.Type)
    @Html.EditorFor(m => m.TextData, new { @class = "text-input"})
    @Html.EditorFor(m => m.TimeData, new { @class = "timepicker"})
}

<script type="text/javascript">
    function toggleInput_() {
         if ($('#@Html.IdFor(m => m.Type)').val() === 'Text') {
             $('#@Html.IdFor(m => m.TextData)').prop('disabled', false).show();
             $('#@Html.IdFor(m => m.TimeData)').prop('disabled', true).hide();
         }
         else {
             $('#@Html.IdFor(m => m.TextData)').prop('disabled', true).hide();
             $('#@Html.IdFor(m => m.TimeData)').prop('disabled', false).show();
         }
    }

    $(document).ready(function() {
        $('#@Html.IdFor(m => m.Type)').on('change', function() {
            toggleInput_(); // toggle when drop down changes
        });

        toggleInput_(); // toggle initially on page load
    });
</script>

Controller:

[HttPost]
public ActionResult Submit(MyViewModel postData) {

    string textValue = null;
    DateTime? timeValue = null;

    if (postData.Type == "Text") {
        textValue = postData.TextData;
    }
    else {
        timeValue = postData.TimeData;
    }

    // ...
}



回答2:


ASP MVC already has this functionality built in with Editor Templates. By following the convention, you can specify a template to be used for any type (including user-defined complex types) which will be rendered with @Html.EditorFor().

In a nutshell, just place two partial views in your ~/Views/Shared/EditorTemplatesfolder, one with model type DateTime and the other string. The correct partial view will be rendered when using @Html.EditorFor(m => m.Property) based on the type of Property.

Note: the default editor for a string property will already be an input with type="text", so you don't necessarily need to specify that template.

See this link for a tutorial on Editor templates (and Display templates): https://exceptionnotfound.net/asp-net-mvc-demystified-display-and-editor-templates/



来源:https://stackoverflow.com/questions/53615641/asp-net-mvc-dynamic-input-bound-to-same-controller-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!