What ActionResult should you return to update just the ActionLink text? - asp.net-mvc-4

I'm using MVC4 with Entity Framework and like many people I'm new to MVC and trying to get my head around the design patterns.
I have a partial view that displays a list of sessions followed by actionlinks allowing the authenticated member to book into the sessions.
Note: for clarity, I've chopped out most of the code, if a member is booked into a session, it displays "Booked" instead of the action link.
#using OnlineBookings.Website.Models
#{ DateTime currentDate = DateTime.MinValue.Date; }
<form method="post" action="~/Controllers/BookSessionController.cs">
#foreach (SessionsWithBookingInformation s in Model)
{
<p>#s.StartTime.ToString("t")
#s.Description
#Html.ActionLink(
"Book",
"BookSession",
new { sessionId = s.SessionId }
)
</p>
}
</form>
This then displays as part of a larger view:
The actionlinks pass the guid of the session to be booked through to the following function in my controller, which retrieves the memberId from the cookie and uses Entity Framework to create a booking for that member and session.
public ActionResult BookSession(Guid sessionId)
{
using (var db = new OnlineBookingsEntities())
{
// see if the member id is stored in a cookie
if (Request.Cookies["memberId"] != null)
{
var memberId = new Guid(Request.Cookies["memberId"].Value);
db.Bookings.Add(new Booking
{
BookingId = Guid.NewGuid(),
MemberId = memberId,
SessionId = sessionId,
BookingTime = DateTime.Now
});
db.SaveChanges();
}
}
// this refreshes the entire page
/// is there a better way to just replace the actionlink they clicked on?
return RedirectToAction("Index", "Home");
}
All this is working nicely and bookings are being effectively recorded.
But, I'm trying to figure is if the return from the BookSession function can just update the actionlink text.
Ideally, on success, I want to replace the ActionLink in my partial view with the word "Booked" and on failure I want to replace it with the failure condition like "Session full".
Or I could just update my partial view, because that will do the same thing.
Am I missing something simple here? Or, am I barking up entirely the wrong tree?

Your question is great and really well explained, but it's also a little vague since it's a bit of a "What should I do?" question. Here are a few options that might help you develop a solution.
Redisplay the same view. Return whichever view the user was on for them to submit the link. This will look like a simple refresh.
return View();
Submit the request via AJAX and update via a partial view. Put an id tag on a span or similar HTML element with an individual booking's details inside. Submit the request with AJAX, perhaps via #Ajax.ActionLink, and have your action return a partial view.
return PartialView("_OnlineBookingPartial", model);
Once your partial view is returned, update the specific booking with the data returned.
Use AJAX again, but return JSON. Another way might be that you use AJAX again but instead you return JSON and do something with it. You could, for example, return text in which you would replace Book with; i.e. "Session full" or "Booked!".
return new JsonResult
{
Data = "Booked!"
}
Personally, I'd probably use AJAX to update with a non-AJAX (non-Javascript) fallback.
You can do this by using #Ajax.ActionLink and checking if the request is AJAX or not inside your controller action.
if (Request.IsAjaxRequest) {
return PartialView("_OnlineBookingPartial", model);
}
return View();
This means that if the browser has Javascript enabled and supports AJAX, it will be used and the whole process will be seamless and instant for the user. If Javascript is disabled, the page will simply refresh.

Related

MVC 4 website back press return the same form with old values

I am new in MVC 4 web development and i am creating a control panel. I have developed a Add user page and submit information in database successfully.
But after submit when i press back button it will show previous form.
i am using redirection the page to same page after submit form.
here is the code to redirect
public ActionResult AdminPanel(RegisterUserModel user)
{
if (ModelState.IsValid) // Check the model state for any validation errors
{
if (user.AddUserToDB(user.username, user.password, user.fullName,user.contactNo,user.COAId)) // Calls the Login class checkUser() for existence of the user in the database.
{
TempData["SuccessMessage"] = "User Added Sucessfully!";
ModelState.Clear();
return Redirect("AdminPanel");
}
else
{
ViewBag.SuccessMessage = "User Not Added";
return View();
}
}
SelectList clientsList = GetClinetList();
ViewBag.clientsList = clientsList;
return View(); // Return the same view with validation errors.
}
I have tried many examples but issue not resolved yet so kindly give my suggesstions
If you don't want the user to be able to see the previous content when clicking back, then you must indicate that content should not be cached by the browser and must revalidate with the origin server
A summary of this behaviour is here - http://blog.55minutes.com/2011/10/how-to-defeat-the-browser-back-button-cache/
You could create a nocache attribute, like this one - https://stackoverflow.com/a/10011896/1538039, and apply it to your controller methods.

Post back Errors in MVC 4

In regular Asp.Net, when you want to post an error, for ex, a duplicate ID, I use Panels for controls, error message labels, etc. I was wondering, how to achieve the same using MVC 4.
Currently, on my Index page, I have an Id, Name & Address column and a Submit button. My HTTPPOST ActionResult here:
[HttpPost]
public ActionResult Index(Person p)
{
if (ModelState.IsValid)
{
PersonInfo pi = new PersonInfo();
var duplicate = from d in db.PersonInfoes
where d.Id == p.Id
select d;
if (duplicate.Any())
{
return View("Duplicate");
}
else
{....}
When I enter a duplicate Id, upon submitting the page, I need to post to the same view ("Index") page to let user know that there is an Id already, but currently, I am redirecting to a different view to let user know as a duplicate, and I dont think this is the right way to do this. I am learning MVC, btw and hence this doubt. My duplicate cshtml here:
#{
ViewBag.Title = "Duplicate";
}
<h2>Duplicate</h2>
<h3>Duplicate ID found !!</h3>
<p>Please correct the ID and re-enter !</p>
<br/><br/>
#Html.ActionLink("Back to Data Entry","Index")
Any pointers would help.
You can return to the same view. You should consider adding an error to the model state dictionary so that you can show that in the UI.
[HttpPost]
public ActionResult Index(Person p)
{
if (ModelState.IsValid)
{
var duplicate= db.PersonInfoes.Where(s=>s.Id== p.Id);
if (duplicate.Any())
{
ModelState.AddModelError(string.Empty,"Duplicate found");
return View(p);
}
// to do : Your existing code
}
return View(p);
}
Just make sure you are calling the ValidationSummary method in your Index view to show this error message.
#model Person
#using (Html.BeginForm())
{
#Html.ValidationSummary(false)
<!-- Your form fields goes here-->
<input type="submit" />
}

How to get a url after returning the view?

I am new to MVC4, I am using more then one form in one View. When i am posting the form i am getting all the formcollecion values in controller. When i am returning the different view also, its working fine. My problem is after returning view, the url showing that post method name.when i try to refresh this page its showing error.
Initially the url was : Admin/Activities
after posting the form : Admin/UpdateActivity
I need url like this after returning the view : Admin/Activities
how to get it, Please help me. Thanks in advance.
I dont have any separate view for this ActionResult. This is my code:
[HttpPost]
public ActionResult UpdateActivity(FormCollection coll)
{
................
ViewBag.updateAlert = "Activity updated sucessfully";
return View("Activities");
}
#using (Html.BeginForm("UpdateActivity", "Admin", FormMethod.Post,new { #id = "formID" }))
Change your form attribute as it. It will automatically return to the view Activities.
Note: if your form on Admin/Activities.
After updating the record,you can Redirect to that action,instead of returning the same view again, and instead of ViewBag you will have to use TempData as ViewBag will be null after redirecting:
[HttpPost]
public ActionResult UpdateActivity(FormCollection coll)
{
................
TempData["updateAlert"] = "Activity updated sucessfully";
return RedirectToAction("Activities","Admin");
}

ASP MVC 4 Ajax.ActionLink for delete using AntiForgeryToken

I have a number of index pages where the row has an actionlink like so
#Ajax.ActionLink("Delete", "Delete", "AdverseEvent", new { id = Model.AdverseEventId }, new AjaxOptions { HttpMethod = "Post",OnSuccess = "rowDeleted", LoadingElementId="ajaxRequest_processing", Confirm = String.Format("Are you sure you want to delete adverse event for participant {0} at {1} ?", Model.ParticipantId, Model.EventTime) }, new { #class = "deleteAction" })
An actionlink is a great way to use progressive enhancement, because of course there is also a delete action, with get and post methods to perform the delete for those with javascript disabled.
I need to add an AntiForgeryToken. For an Ajax.BeginForm helper, Jon White's code works beautifully:
$.ajaxPrefilter(function (options, localOptions, jqXHR) {
var type = options.type.toLowerCase();
if (type === 'post') {
var token = GetAntiForgeryToken();
jqXHR.setRequestHeader(token.name, token.value);
}
});
When this gets executed within an actionlink, I assume because the index table is not wrapped in a form, I get the error message:
The required anti-forgery form field "__RequestVerificationToken" is not present
So i could wrap the whole table in a form posting back to the delete action, but this is then not very neat if I want to use other ajax.actionlinks to different actions within the table. I could wrap each actionlink in its own form, each with its own antiforgery token, but this is a significant amount of extra markup, and will leave dozens of elements on the page with identical values and name. The other option would be to use the ActionLink OnBegin method to wrap the button in a form, but the unobtrusive ajax library does not seem to pass any reference to the element causing the ajax get/post (foolishly in my opinion - you can upvote this issue on codeplex).
Any thoughts on a neat solution? Thank you.
You can add the token into the page and then use Ajax to send the field over in another call.
see How to include the #Html.AntiForgeryToken() when deleting an object using a Delete link

Generating a modal jQuery partial view with MVC4 does not work

I like the way MVC4 manage the new logon screen: it is possible to have a modal dialog showed.
I tried to reproduce the same behavior in the same solution for another view. But it doesn't work because Request.QueryString["content"] is null. I don't know why. In fact, there is an action method called ContextDependentView (generated by the MVC template) where the trick occurred. Here it is:
private ActionResult ContextDependentView()
{
string actionName = ControllerContext.RouteData.GetRequiredString("action");
if (Request.QueryString["content"] != null)
{
ViewBag.FormAction = "Json" + actionName;
return PartialView();
}
else
{
ViewBag.FormAction = actionName;
return View();
}
}
If the value of Request.QueryString["content"] is not null then we display a partial view (modal jQuery) otherwise it is a classic view.
Can someone help me understand why this is not working?
PS: another thread already exists but without any solution.
The login and register links are bound to a click handler in AjaxLogin.js which then adds content=1 in loadAndShowDialog