У меня есть контроллер, который я звоню через JQuery AJAX. Он берет данные на моей странице и возвращает частичное представление. Цель этого представления - предварительно заполнить некоторые поля ввода данными, которые я извлекаю из источника данных.
Проблема в том, что, когда частичное представление отображается в браузере, входные значения поля формы не заполняются значениями, которые я добавил в модель представления.
Я могу поставить точку останова на строке Return
и увидеть, что vm
правильно заполнен моими свойствами, как я хотел бы.
Но когда я просматриваю ответ на вкладке сети в Chrome Developer Tools, значения остаются пустыми.
У кого-нибудь есть идеи, почему?
Контроллер
[HttpPost]
public async Task<IActionResult> ViewEditRaceOption(RaceViewModel race)
{
var vm = race;
var option = race.RaceOptionData.FirstOrDefault(o => o.Number == race.EditOption);
vm.OptionAffiliatedDiscountValue = option.AffiliatedDiscountValue;
vm.OptionColour = option.OptionColour;
vm.OptionEmbedMapURL = option.EmbedMapURL;
vm.OptionEntryPremium = option.Premium;
vm.OptionEntryPrice= option.AffiliatedDiscountValue;
vm.OptionMaxEntries= option.MaxEntries;
vm.OptionName= option.Name;
vm.OptionRaceDistance = option.RaceDistance;
vm.OptionStartTime = option.StartTime;
vm.OptionCertificateFileName = option.CourseMeasurementCertificateFileName;
return PartialView("OptionModal", vm);
}
Главный вид (содержит Ajax JS), усеченный
@using TechsportiseOnline.Helpers
@model TechsportiseOnline.ViewModels.RaceViewModel
@{
ViewData["Title"] = "Race";
}
<script>
$(function () {
$("#racedatepicker").datepicker({
dateFormat: "d MM yy",
changeMonth: true,
changeYear: true
});
});
</script>
@if (Model.FormAction == "Create")
{
<h2>Create a new race</h2>
}
else
{
<h2>@Model.RaceData.Name | <span class="text-muted">Edit Race</span></h2>
<h4>@Model.RaceData.RaceDate.ToString("dd MMMM yyyy")</h4>
}
<form asp-action="@Model.FormAction" enctype="multipart/form-data" id="race">
<input type="hidden" name="FormAction" value="@Model.FormAction" />
<input id="DeleteOption" name="DeleteOption" type="hidden" value="0" />
<input id="EditOption" name="EditOption" type="hidden" value="0" />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="modal fade bd-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true" id="optionsmodal">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h2 class="modal-title">Race Option</h2>
<input type="hidden" name="NewOption" value="false" id="NewOption" />
</div>
<div class="modal-body">
@{Html.RenderPartial("_StatusMessage", Model.Message);}
<div id="optionmodal">
@{Html.RenderPartial("OptionModal", Model);}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default cancelmodal">Cancel</button>
<button type="button" class="btn btn-primary" id="addoption">Add</button>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">Race Details</div>
<div class="panel-body">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label asp-for="RaceData.Name" class="control-label"></label>
<br /><small class="text-muted">The name or title of your race</small>
<input asp-for="RaceData.Name" class="form-control" />
<span asp-validation-for="RaceData.Name" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label asp-for="RaceData.ContactName" class="control-label"></label>
<br /><small class="text-muted">The name of the Race Director whom people should contact with queries</small>
<input asp-for="RaceData.ContactName" class="form-control" />
<span asp-validation-for="RaceData.ContactName" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label asp-for="RaceData.ContactEmail" class="control-label"></label>
<br /><small class="text-muted">The email address of the Race Director whom people should contact with queries</small>
<input asp-for="RaceData.ContactEmail" class="form-control" />
<span asp-validation-for="RaceData.ContactEmail" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label asp-for="RaceData.ContactNumber" class="control-label"></label>
<br /><small class="text-muted">The phone number of the Race Director whom people should contact with queries</small>
<input asp-for="RaceData.ContactNumber" class="form-control" />
<span asp-validation-for="RaceData.ContactNumber" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label asp-for="RaceData.RaceDate" class="control-label"></label>
<div class="input-group">
<span class="input-group-addon"><i class="icon-calendar"></i> </span>
<input name="RaceData.RaceDate" value="@Model.RaceData.RaceDate.ToString("dd MMMM yyyy")" type="text" class="form-control" id="racedatepicker" autocomplete="off">
</div>
<span asp-validation-for="RaceData.RaceDate" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label asp-for="RaceData.Description" class="control-label"></label>
<br /><small class="text-muted">As much description as you want to add for your race to give your participants as much as they need to know</small>
<textarea asp-for="RaceData.Description" class="form-control" rows="10" id="description">@Model.RaceData.Description</textarea>
<span asp-validation-for="RaceData.Description" class="text-danger"></span>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">Race Options</div>
<div class="panel-body">
<div id="container">
Create your different Race Options here, if you want to have more than one distance/race in the same event. You must have at least 1 Race Option.
<div id="dvRaceOptionsResults">
@{Html.RenderPartial("RaceOptions", Model);}
</div>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target=".bd-example-modal-lg">Add Race Option</button>
</div>
</div>
</div>
</div>
TRUNCATED HERE
<div class="form-group">
@if (Model.FormAction == "Create")
{
<input type="submit" value="Create" class="btn btn-primary" />
}
else
{
<input type="submit" value="Save" class="btn btn-primary" />
}
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
</form>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.9/validator.min.js"></script>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$(document).on('click', '.cancelmodal', function () {
console.log("cancel modal")
var premiumrace = document.getElementById("RaceData_Premium").value
document.getElementById("OptionName").value = ""
document.getElementById("OptionRaceDistance").value = ""
document.getElementById("OptionMaxEntries").value = 0
document.getElementById("OptionStartTime").value = "19:00"
document.getElementById("OptionEmbedMapURL").value = ""
document.getElementById("OptionColour").value = "White"
document.getElementById("OptionEmbedMapURL").value = ""
if (premiumrace == true) {
document.getElementById("OptionEntryPremium").value = false
document.getElementById("OptionEntryPrice").value = "0"
document.getElementById("OptionAffiliatedDiscountValue").value = "2.00"
}
$("#optionsmodal").modal("hide")
});
</script>
<script type="text/javascript">
$(document).on('click', '.editmodal', function () {
var optionnumber = $(this).attr('number');
document.getElementById("EditOption").value = optionnumber;
var form = document.getElementById('race');
var formData = new FormData(form);
$.ajax({
url: '@Url.Action("ViewEditRaceOption", "Races")',
type: 'POST',
data: formData,
dataType: 'html',
processData: false,
contentType: false,
success: function(response) {
if (response) { // check if data is defined
$("#optionmodal").html(response);
console.log(response);
$("#optionsmodal").modal("show");
}
}
});
document.getElementById("EditOption").value = 0;
});
</script>
<script type="text/javascript">
$(document).on('click', '.editoption', function () {
var optionnumber = $(this).attr('number');
document.getElementById("EditOption").value = optionnumber;
var form = document.getElementById('race');
var formData = new FormData(form);
$.ajax({
url: '@Url.Action("EditRaceOption", "Races")',
type: 'POST',
data: formData,
dataType: 'html',
processData: false,
contentType: false,
success: function(response) {
if (response) { // check if data is defined
$("#dvRaceOptionsResults").html(response);
}
}
});
document.getElementById("EditOption").value = 0;
});
</script>
<script type="text/javascript">
$(document).on('click', '.removeoption', function () {
var optionnumber = $(this).attr('number');
document.getElementById("DeleteOption").value = optionnumber;
$.ajax({
url: '@Url.Action("RemoveRaceOption", "Races")',
type: 'POST',
data: $("#race").serialize(),
dataType: 'html',
success: function(response) {
if (response) { // check if data is defined
$("#dvRaceOptionsResults").html(response);
}
}
});
document.getElementById("DeleteOption").value = 0;
});
</script>
<script type="text/javascript">
$("#addoption").click(function () {
document.getElementById("NewOption").value = "true";
var form = document.getElementById('race');
var formData = new FormData(form);
$.ajax({
url: '@Url.Action("AddRaceOption", "Races")',
type: 'POST',
data: formData,
dataType: 'html',
processData: false,
contentType: false,
success: function(response) {
if (response) { // check if data is defined
$("#optionsmodal").modal("hide");
$("#dvRaceOptionsResults").html(response);
document.getElementById("OptionName").value = null;
document.getElementById("OptionRaceDistance").value = "";
}
}
});
document.getElementById("NewOption").value = "false";
});
</script>
<script type="text/javascript">
tinymce.init({
selector: 'textarea',
height: 400,
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor textcolor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table contextmenu paste code help'
],
toolbar: 'preview | formatselect | bold italic underline strikethrough forecolor | alignleft aligncenter alignright alignjustify | bullist numlist | image link media | removeformat ',
content_css: [
'//fonts.googleapis.com/css?family=Lato:300,300i,400,400i',
'//www.tinymce.com/css/codepen.min.css']
});
// Prevent Bootstrap dialog from blocking focusin
$(document).on('focusin', function (e) {
if ($(e.target).closest(".mce-window").length) {
e.stopImmediatePropagation();
}
});
</script>
<script type="text/javascript" src="~/js/autocomplete/jquery.easy-autocomplete.min.js"></script>
<link rel="stylesheet" href="~/js/autocomplete/easy-autocomplete.min.css">
<script type="text/javascript">
var options = {
url: "../../resources/countries.json",
getValue: "name",
list: {
match: {
enabled: true
}
},
theme: "square"
};
$("#countries").easyAutocomplete(options);
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCZMX9gUpf7jFKwPe6r6dtqmrv-EnnXsU4&libraries=places&callback=initAutocomplete"
async defer></script>
<script type="text/javascript">
var placeSearch, autocomplete;
var componentForm = {
street_number: 'short_name',
route: 'long_name',
administrative_area_level_1: 'short_name',
administrative_area_level_2: 'short_name',
postal_town: 'short_name',
country: 'long_name',
postal_code: 'short_name'
};
function initAutocomplete() {
// Create the autocomplete object, restricting the search to geographical
// location types.
autocomplete = new google.maps.places.Autocomplete(
/** type {!HTMLInputElement} */(document.getElementById('autocomplete')),
{ types: ['geocode'] });
// When the user selects an address from the dropdown, populate the address
// fields in the form.
autocomplete.addListener('place_changed', fillInAddress);
}
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
for (var component in componentForm) {
document.getElementById(component).value = '';
document.getElementById(component).disabled = false;
}
// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}
}
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
var circle = new google.maps.Circle({
center: geolocation,
radius: position.coords.accuracy
});
autocomplete.setBounds(circle.getBounds());
});
}
}
</script>
}
Частичное представление
@model TechsportiseOnline.ViewModels.RaceViewModel
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label value="OptionName" class="control-label">Option Name</label>
<br /><small class="text-muted">The name of this Race Option</small>
<input asp-for="OptionName" name = "OptionName" placeholder="Your 10k" type="text" class="form-control" aria-label="Name">
<span asp-validation-for="OptionName" class="text-danger"></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label name="OptionRaceDistance" value="Race Distance" class="control-label">Distance</label>
<br /><small class="text-muted">Choose a race distance, used for Age Grading </small>
<select asp-for="OptionRaceDistance" required class="form-control">
<option value="" selected>--select--</option>
<option value="M1">1 mile</option>
<option value="KM5">5 km</option>
<option value="KM6">6 km</option>
<option value="M4">4 miles</option>
<option value="KM8">8 km</option>
<option value="M5">5 miles</option>
<option value="KM10">10 km</option>
<option value="KM12">12 km</option>
<option value="KM15">15 km</option>
<option value="M10">10 miles</option>
<option value="KM20">20 km</option>
<option value="Half">Half Marathon</option>
<option value="KM25">25 km</option>
<option value="KM30">30 km</option>
<option value="Marathon">Marathon</option>
<option value="KM50">50 km</option>
<option value="M50">50 miles</option>
<option value="KM100">100 km</option>
<option value="KM150">150 km</option>
<option value="M100">100 miles</option>
<option value="KM200">200 km</option>
<option value="Other">Other</option>
</select><br />
<span asp-validation-for="OptionRaceDistance" class="text-danger"></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label name="OptionMaxEntries" value="Maximum Entries" class="control-label">Maximum Entries</label>
<br /><small class="text-muted">The maximum capacity of the race</small>
<input asp-for="OptionMaxEntries" class="form-control" type="number" />
<span asp-validation-for="OptionMaxEntries" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label name="OptionStartTime" value="Start Time" class="control-label">Race Start Time</label>
<br /><small class="text-muted">Start time in HH:MM</small>
<input asp-for="OptionStartTime" value="19:00" asp-format="{0:hh:mm}" class="form-control" type="time" />
<span asp-validation-for="OptionStartTime" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="control-label">Colour</label>
<br /><small class="text-muted">Choose the colour for this option. </small>
<select asp-for="OptionColour" class="form-control">
<option value="White" selected>White</option>
<option value="Red">Red</option>
<option value="Blue">Blue</option>
<option value="Yellow">Yellow</option>
<option value="Green">Green</option>
<option value="Silver">Silver</option>
<option value="Orange">Orange</option>
</select><br />
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="control-label">Course Measurement Certificate</label>
<br /><small class="text-muted">PDF file of certificate for this race option</small>
<input asp-for="OptionCourseMeasurementCertificateFile" type="file" class="form-control" />
<span asp-validation-for="OptionCourseMeasurementCertificateFile" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="control-label">Course Map URL</label>
<br /><small class="text-muted">Provide a URL to a Garmin, Strava or other course mapping tool</small>
<input asp-for="OptionEmbedMapURL" class="form-control" />
<span asp-validation-for="OptionEmbedMapURL" class="text-danger"></span>
</div>
</div>
</div>
@if (Model.RaceData.CanBePremium)
{
<div class="row">
<div class="col-md-4">
<div class="form-group">
<div class="checkbox">
<label>
<input asp-for="OptionEntryPremium" /> Premium Race Option
<br /><small class="text-muted">This is a premium race option and will cost @Model.BaseCurrency.Symbol@Model.BaseFee.ToString("0.00") per race entry</small>
</label>
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label value="OptionEntryPrice" class="control-label">Entry Price</label>
<br /><small class="text-muted">The price of the normal race entry</small>
<input asp-for="OptionEntryPrice" type="number" class="form-control" aria-label="Amount" placeholder="10.00" asp-format="{0:0.00}" >
<span asp-validation-for="OptionEntryPrice" class="text-danger"></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="control-label">Affiliation Discount</label>
<br /><small class="text-muted">Value of discount for affiliation</small>
<input asp-for="OptionAffiliatedDiscountValue" type="number" class="form-control" aria-label="Amount" value="2.00" asp-format="{0:0.00}" >
<span asp-validation-for="OptionAffiliatedDiscountValue" class="text-danger"></span>
</div>
</div>
</div>
}