Reputation: 168
I have a Core 2 MVC web app which I am trying to add some Ajax into.
I have a Create view which loads a standard web form. What makes this a bit different to what I have learned/done so far is that I would like to add some semi-dynamic content to the page.
The ViewModel for the page is a bunch of simple properties, but part of it is also a nested collection.
I am trying to dynamically add values into the nested collection via a modal, and then reload the partial view to reflect what I have added without causing a page reload - I only want to reload the partial view and close the Modal.
I can see the JS is actioning the request and firing the call to the controller and getting a 200 response but it is not closing the modal or reloading the partial view with the data from the Modal.
There is something I am definitely not getting here. Can anyone advise?
ViewModel
public class RaceViewModel
{
public Race RaceData { get; set; }
public ObservableCollection<CurrencyDetails> Currencies { get; set; }
public CurrencyDetails BaseCurrency { get; set; }
public CurrencyDetails RaceCurrency { get; set; }
public ObservableCollection<RaceOptions> RaceOptionData { get; set; }
[DisplayFormat(DataFormatString = "{0:0.00")]
public decimal BaseFee { get; set; }
}
Main view (Irrelevant sections omitted)
@using TechsportiseOnline.Helpers
@model TechsportiseOnline.ViewModels.RaceViewModel
@{
ViewData["Title"] = "Create";
}
<h2>Create a new race</h2>
<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">New Race Option</h2>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label value="Name" class="control-label">Option Name</label>
<br /><small class="text-muted">The name of this Race Option</small>
<input name="Name" placeholder="Your 10k" type="text" class="form-control" aria-label="Name">
@*<span asp-validation-for="RaceData.Name" class="text-danger"></span>*@
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label name="RaceDistance" value="Race Distance" class="control-label">Distance</label>
<br /><small class="text-muted">Choose a race distance, used for Age Grading </small>
<select name="RaceDistance" 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="RaceData.RaceDistance" class="text-danger"></span>*@
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label name="MaxEntries" value="Maximum Entries" class="control-label">Maximum Entries</label>
<br /><small class="text-muted">The maximum capacity of the race</small>
<input name="MaxEntries" class="form-control" />
@*<span asp-validation-for="RaceData.MaxEntries" class="text-danger"></span>*@
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label name="Start Time" value="Start Time" class="control-label">Race Start Time</label>
<br /><small class="text-muted">Start time in HH:MM</small>
<input name="Start Time" value="19:00" asp-format="{0:hh:mm}" class="form-control" type="time" />
@*<span asp-validation-for="RaceData.RaceStartTime" class="text-danger"></span>*@
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label value="Entry Price" class="control-label">Entry Price</label>
<br /><small class="text-muted">The price of the normal race entry</small>
<input name="Entry Price" type="text" class="form-control" aria-label="Amount" placeholder="10.00" asp-format="{0:0.00}">
@*<span asp-validation-for="RaceData.EntryPrice" 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 being an affiliated runner</small>
<input name="AffiliatedDiscountValue" type="text" class="form-control" aria-label="Amount" value="2.00" asp-format="{0:0.00}">
@*<span asp-validation-for="AffiliatedDiscountValue" class="text-danger"></span>*@
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="addoption" >Add</button>
</div>
</div>
</div>
</div>
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></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>
</div>
</div>
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$("#addoption").click(function(){
$.ajax({
url: '@Url.Action("GetRaceOptions", "Races")',
type: 'POST',
success: function(data) {
if (data) { // check if data is defined
$("#dvRaceOptionsResults").html(data);
}
}
});
});
</script>
}
Partial View
@model TechsportiseOnline.ViewModels.RaceViewModel
<table>
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
@if (Model.RaceOptionData != null)
{
foreach (var option in Model.RaceOptionData)
{
<tr>
<td>@option.Name</td>
</tr>
}
}
</tbody>
</table>
Controller Method
[HttpPost]
public async Task<IActionResult> GetRaceOptions(RaceViewModel race)
{
var viewModel = race;
return PartialView("RaceOptions", viewModel);
}
Upvotes: 0
Views: 1456
Reputation: 1022
This is your VideModel
right?
public class RaceViewModel
{
...
}
From what I understand, your AJAX request is getting routed to method GetRaceOptions
which receives a RaceViewModel
parameter. Unfortunately, your AJAX POST Request is not sending the required RaceViewModel
object. You have to pass it in the data body
.
Make sure your JS data object
has the same property name as the ViewModel
so Model Binding will work properly.
Example:
data: {BaseFee: 842350, property2: 'abc'}; //and so on
$.ajax({
url: '@Url.Action("GetRaceOptions", "Races")',
type: 'POST',
data: data,
dataType : 'html', //expect html back
success: function(data) {
if (data) { // check if data is defined
$("#dvRaceOptionsResults").html(data);
}
}
Upvotes: 1
Reputation: 41
You are calling ajax without data:
parameter. So you are not posting data to your controller. Than controller is trying to return the partial view with empty viewModel.
If you want to save newly added options you will need to do something like this.
create data object and send it as data parameter in ajax.
modify the controller to save new options and return old + new options
[HttpPost]
public async Task<IActionResult> GetRaceOptions(RaceViewModel race)
{
//save options from RaceViewModel
//get old options + newly added options
var viewModel = old + newly added data
return PartialView("RaceOptions", viewModel);
}
You need to close modal in the success part of ajax
success: function(data) {
if (data) { // check if data is defined
$("#dvRaceOptionsResults").html(data);
$('#modalId').modal('hide');
}
Upvotes: 0