问题
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"
oftype="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 selectedType
.Or have two inputs with
name="TextData"
andname="TimeData"
, and disable and hide one of these inputs using JS depending on the selectedType
. In the controller, read the value from the right input depending on the selectedType
. 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/EditorTemplates
folder, 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