Jason James Jason James - 4 months ago 21
Javascript Question

ASP.Net MVC page posting to controller and not returning record ID in address bar

I have a Razor page that displays a list of meetings. Each meeting has a id which is an attribute of a link assigned to each meeting which when clicked loads another Razor View showing the meeting details. This view calls a childAction which loads a partial page, displaying dropdown containing all available attendees and a

<div>
which will show a form if new visitors need to be created. The URL of the details page looks like

http://localhost:53533/Meetings/AddVisitors/18


I can select a visitor from the dropdown list and I have a working method that create a new record linking the visitor to the meeting. The methods returns the view having added the record, but the details are not shown in the refreshed table and the meeting id has gone from the page URL.

The method to show the page is:

public ActionResult AddVisitors(int? id)
{
var meetings = db.Meetings.Include("Host").Include("VisitorsMeetings").Where(x => x.id == id).FirstOrDefault();
return View(meetings);
}


The method that creates the visitor/meeting record is:

[HttpPost]
public ActionResult AddVisitors(VisitorList model)
{
VisitorMeeting newVisitorMeeting = new VisitorMeeting
{
MeetingId = model.meetingId,
VisitorId = model.SelectedId,
Arrived = false,
Departed = false,
BadgePrinted = false
};
db.VisitorMeetings.Add(newVisitorMeeting);
db.SaveChanges();

var meetings = db.Meetings.Include("Host").Include("VisitorsMeetings").Where(x => x.id == model.meetingId).FirstOrDefault();

return AddVisitors(model.meetingId);
}


The view is:

@model VisitorManager.Models.Meeting
....
<div>
<h4>Meeting</h4>
<dl class="dl-horizontal">
<dt>@Html.DisplayNameFor(model => model.Host.Name)</dt>
<dd>@Html.DisplayFor(model => model.Host.Name)</dd>
<dt>@Html.DisplayNameFor(model => model.StartTime)</dt>
<dd>@Html.DisplayFor(model => model.StartTime)</dd>
... // more properties of Meeting
</dl>
</div>

<h4>Visitor List</h4>
<div>
<table class="table">
<tr>
<th>Visitor Name</th>
<th>Company</th>
...
<th></th>
</tr>
@foreach (var item in Model.VisitorsMeetings)
{
<tr>
<td>@Html.DisplayFor(modelItem => item.Visitor.VisitorName</td>
<td>@Html.DisplayFor(modelItem => item.Visitor.Company)</td>
<td>@Html.DisplayFor(modelItem => item.Arrived)</td>
....
<td>@Html.ActionLink("Delete", "Delete", new { id = item.VisitorId })</td>
</tr>
}
</table>
</div>

// Add partial view to show drop down of all visitors
@Html.Action("VisitorList", "Meetings")


@section Scripts{

<script type="text/javascript">
var dialog = $('#dialog-mask');
$('#addVisitor').click(
function () {
$('#VisitorModal').load('@Url.Action("Add", "Meetings")');
//$('#VisitorModal').find('form')[0].reset();
//dialog.find('form')[0].reset();
dialog.show();
}
);

$('#cancel').on('click', function () {
dialog.hide();
return false;
});

$('#VisitorModal').on('submit', '#visitorform', function () {
var data = $(this).serialize();
var url = '@Url.Action("AddVisitors", "Meetings")';
var text = $('#title').val() + ' ' + $('#firstname').val() + ' ' + $('#surname').val() + ' (' + $('#company').val() + ')'; // a value from the form that you want as the option text
$.post(url, data, function (response) {
if (response) {
$('#VisitorID').append($('<option></option>').val(response).text(text)).val(response);
} else {
dialog.hide();
// Oops
}
}).fail(function () {
// Oops
dialog.hide();
});
// ... close the modal
//$('#VisitorModal').hide();
//$('#VisitorModal').find('form')[0].reset();
dialog.hide();
return false; // cancel the default submit
});
</script>

}


The PartialView is:

@using VisitorManager.Models
@model VisitorList
@{
Layout = null;
}
@using (Html.BeginForm("AddVisitors", "Meetings"))
{
@Html.AntiForgeryToken()


<hr />
<div class="form-horizontal">
<h4>Add A Visitor</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.Label("Visitor Name", htmlAttributes: new { @class = "control-label col-md-4" })
<div class="col-md-4">
@Html.DropDownListFor(m => m.SelectedId, Model.Data, "- Please select -", htmlAttributes: new { @class = "form-control", @id="VisitorID" })
</div>
<button type="button" id="addVisitor" class="btn btn-success">Create new Visitor</button>
</div>

@Html.HiddenFor(m=>m.meetingId)
<div class="form-group">
<div class="col-md-offset-4 col-md-8">
<input type="submit" value="Add Visitor" class="btn btn-default" />
</div>
</div>

</div>
}

<div id="dialog-mask">
<div id="dialog" style="width:500px; height:auto;">
<div id="VisitorModal"></div>
</div>
</div>


VisitorList Action:

[ChildActionOnly]
public ActionResult VisitorList()
{
var dropDownData = db.Visitors.OrderBy(z => z.Company).ThenBy(y=>y.LastName).ThenBy(x=>x.FirstName)
.Select(d => new SelectListItem
{
Text = d.Title + " " + d.FirstName + " " + d.LastName + " (" + d.Company + ")",
Value = d.id.ToString()
});


var model = new VisitorList
{
Data = dropDownData,
meetingId = 18
};

return PartialView(model);
}


VisitorList viewmodel:

public class VisitorList
{
public IEnumerable<SelectListItem> Data { get; set; }
public int SelectedId { get; set; }
public int meetingId { get; set; }
}


Meeting model:

public class Meeting
{
public int id { get; set; }


[Display(Name="Start Time", Prompt = "Please supply a start date and time.")]
[Required(ErrorMessage = "Please supply a start date and time.")]
[DataType(DataType.DateTime)]
public DateTime StartTime { get; set; }

[Display(Name="End Time", Prompt = "Please supply an end date and time.")]
[Required(ErrorMessage = "Please supply an end date and time.")]
[DataType(DataType.DateTime)]
public DateTime EndTime { get; set; }

//[Display(Name="Meeting Location")]
//public string Location { get; set; }

[Display(Name="Meeting Purpose")]
public string MeetingPurpose { get; set; }

public DateTime DateCreated { get; set; }

[Required]
[ForeignKey("Host")]
[Display(Name="Meeting Host")]
public int HostId { get; set; }
[Display(Name="Meeting Host")]
public virtual Host Host { get; set; }

[Required]
[ForeignKey("Room")]
[Display(Name = "Meeting Room")]
public int RoomId { get; set; }
public virtual Room Room { get; set; }

public ICollection<VisitorMeeting> VisitorsMeetings { get; set; }
}


Visitor Model:

public class Visitor
{
public int id { get; set; }
[Display(Name="Visitor Title")]
[Required]
public string Title { get; set; }

[Display(Name="Visitor Firstname")]
[Required]
public string FirstName { get; set; }

[Display(Name="Visitor Lastname")]
[Required]
public string LastName { get; set; }

[Display(Name="Company Name")]
[Required]
public string Company { get; set; }

[NotMapped]
[Display(Name="Visitor Name")]
public string VisitorName
{
get
{
return Title + " " + FirstName + " " + LastName;
}
}
public ICollection<VisitorMeeting> VisitorsMeetings { get; set; }
}


VisitorMeeting Model:

public class VisitorMeeting
{
public int Id { get; set; }
[Display(Name="Visitor Arrived")]
[Required]
public bool Arrived { get; set; }

[Display(Name="Arrival Time")]
public DateTime? ArrivalTime { get; set; }

[Display(Name="Visitor Departed")]
[Required]
public bool Departed { get; set; }

[Display(Name="Departure Time")]
public DateTime? DepartureTime { get; set; }

[Display(Name="Badge Printed")]
[Required]
public bool BadgePrinted { get; set; }

[Required]
[ForeignKey("Meeting")]
public int MeetingId { get; set; }
public virtual Meeting Meeting{ get; set; }

[Required]
[ForeignKey("Visitor")]
public int VisitorId { get; set; }
public virtual Visitor Visitor { get; set; }
}


Can anyone tell me How I keep the meeting ID in the AddVisitors URL and why the complete added visitor row isn't correctly showing in the list of visitors displayed in the partial view called by the child action?

Many thanks.

Answer

There is several options to do so.

I advise you to use post-redirect-get pattern as it will safe for user to refresh page any time:

[HttpPost]
public ActionResult AddVisitors(VisitorList model)
{
    // ...

    return this.Redirect("AddVisitors", new { id = model.meetingId});
}

Another way is to generate appropriate post url. Look like you use default route mapping, so valid url should be generated:

@using (Html.BeginForm("AddVisitors", "Meetings", new { id = Model.meetingId }))