Different view for Ajax postback without duplicating controller - c#

I have an "Add to Cart" button that if the browser supports JS + Ajax (and doesn't have it turned off) it POSTS using Ajax back to the site, however if they don't support it, or have it turned off it does the manual style POST.
What I am hoping to accomplish is two views - one when the user posts back using a regular POST and one when it comes from a AJAX POST. That way I can show an in-line message (partial) or a full screen.
I would prefer not having to duplicate the controller/action code twice, it just seems non-elegant.
Is there any recommended solutions or patterns for this type of issue?

John,
You can use the IsAjaxRequest method on the request to determine this. You would apply it to your scenario thusly:
public ActionResult AddToCart(YourCartViewmodel cartViewmodel)
{
if (ModelState.IsValid)
{
// do the standard/common db stuff here
if(Request.IsAjaxRequest())
{
return PartialView("myPartialView");
}
else
{
return View("standardView");
}
}
/* always return full 'standard' postback if model error */
return View(cartViewmodel);
}
altho not perhaps giving a complete solution, this should give you a good start...

You can have two different actions in your controller. One for regular post and one for AJAX.
public ActionResult AddToCart(Viewmodel vm)
{
if (ModelState.IsValid)
{
DoStuff(vm);
return View("ViewForRegularPost");
}
/* error */
return View(vm);
}
and
public ActionResult JsonAddToCart(Viewmodel vm)
{
if (ModelState.IsValid)
{
DoStuff(vm);
return View("ViewForJS");
}
/* error */
return View(vm);
}
Instead of repeating your controller code, have a separate method for actual controller code.
public void DoStuff(Viewmodel vm)
{
//TODO : Actual controller code goes here
}

Related

How to send a text message or a link from controller to view?

I have got a submit button for a form, this controller actionresult method gets called when the button is pressed. Depending on the return of the _shortUrlProcessor.CreateShortURL method. I want to either display a message in red or a create a link under the mentioned submit button. What is the proper way of handling this in MVC? (see comments in code for more clarification as well)
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ShortenURL(ShortURLModel model)
{
if(ModelState.IsValid)
{
if(_shortUrlProcessor.CreateShortURL(model.originalURL, model.shortURL))
{
ViewBag.ShortenURLSuccess(model.shortURL); //< ---- send as a localhost:port/model.shortURL link
}
else
{
ViewBag.ShortenURLSuccess("Could not create link"); //<----send as a text label (which would be shown in something like a <div/>)
}
}
return View();
}
Putting this in the view is the way to go, and here is an example using the viewbag approach:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ShortenURL(ShortURLModel model)
{
if(ModelState.IsValid)
{
if(_shortUrlProcessor.CreateShortURL(model.originalURL, model.shortURL))
{
ViewBag.ShortenURLSuccess=true;
ViewBag.ShortenURL=model.shortURL;
}
else
{
ViewBag.ShortenURLSuccess=false;
}
}
return View();
}
And then in the view:
#if (ViewBag.ShortenURLSuccess)
{
Go here
}
else
{
<div class="error">Could not create link</div>
}
If you need more information for the url or the message, you can put those in viewbag variables as well.
it is worth noting that a more popular way to do this is with model binding, but this approach works fine for what it is. You can use the submitted model and move away from the viewbag to be a bit more efficient.

How to Prevent Validators from Running on HttpGet?

After reading this very helpful answer I modified a pair of methods to allow them both to accept the same view model:
[ActionName("AddressCorrection"), HttpGet]
public IActionResult AddressCorrectionGet(AddressCorrectionViewModel model)
{
return View(model); // was return View();
}
[ActionName("AddressCorrection"), HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddressCorrectionPost(AddressCorrectionViewModel model)
{
if (ModelState.IsValid)
{
return View("Index", new ApplicationViewModel { SuccessMessage = "That worked." });
}
model.ErrorMessage = "Something went wrong";
return View(model);
}
Problem is, calling return View(model); in AddressCorrectionGet now treats the invocation as a POST of sorts. Specifically, the validators on AddressCorrection.cshtml run. Instead of seeing a blank form ready for input, I see a form with a bunch of “required fields missing” messages.
How do I prevent the View from running the validators in this case? Or in the more general case, how does the View know that it should vs. should not run validators (I was thinking this was simply based on whether the Request method was GET vs POST. Clearly wrong.), and how can I explicitly tell the view to run or not run the validators?
Use ModelState.Clear(); before return View(model);

Why MVC keeps route values in URL without passing them directly

I have a question.
Let's say I have this routes:
/Guest/{var1}/{var2}
/Guest/{var1}/{var2}/edit
When I'm on the /Guest/123/321 page, I have a link to /Guest/123/321/edit?id=1.
There is a form on the page /Guest/123/321/edit?id=1, which posts itself on the same address.
Let's say that my Actions looks like:
public ActionResult Index(int var1, int var2)
{
/* here is some a business logic */
return View(model);
}
[HttpGet]
public ActionResult Edit(int id)
{
/* here is some a business logic */
return View(model);
}
[HttpPost]
public ActionResult Edit(EditModel model)
{
/* here is some a business logic */
return RedirectToAction("Index");
}
The question is why do I have URL /Guest/123/321 after RedirectToAction("Index"), after I submit the form? I mean - it's awesome. It reduces the code a lot. I just don't like to use methods, that I don't understand. :)
I always thought, that I should pass something line new { var1 = 123, var2 = 321 } to RedirectToAction in order to keep the URL.
This is a confusing part of MVC that was previously reported as a bug1 because many people don't find this behavior to be natural. But according to Microsoft, this behavior is by design.
Unfortunately, the Codeplex issue URL was taken down and is not in the Internet archive.
The behavior is that route values are reused from the current request when they are not supplied explicitly.
There are some cases where it works well, such as localizing the URL, but in other cases such as when using Areas, you have to manually clear the value in the ActionLink to be able to access the default area.
#Html.ActionLink("Application name", "Index", "Home", new { area = "" }, null)

C# MVC 4 ControllerName attribute

I'm working on providing friendly names for my MVC 4 controllers and I want to do something like the [ActionName="My-Friendly-Name"] style, but for the whole controller.
I couldn't find any information about such an attribute, so how would I go about doing that? Also, will I need to add a new MapRoute to handle it?
EDIT:
For example, I'd like to route the following url:
http://mysite.com/my-reports/Details/5
be routed to the following controller:
[ControllerClass="my-reports"] // This attribute is made up. I'd like to know how to make this functionality
public class ReportsController : Controller
{
//
// GET: /Reports/
public ActionResult Index()
{
return View();
}
public ViewResult Details(int id)
{
Report report = db.Reports.Single(g => g.Id == id);
return View(report);
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Report item)
{
try
{
if (ModelState.IsValid)
{
item.Id = Guid.NewGuid();
_context.Reports.AddObject(item);
_context.SaveChanges();
return RedirectToAction("Index");
}
return View(item);
}
catch (Exception)
{
return View(item);
}
}
}
Add a custom route that will match your specific name:
routes.MapRoute(
"MyCustomRoute",
"My-Friendly-Name/{action}/{id}",
new { controller = "ReportsController", action = "Index", id = "" }
);
Now every url that contains "My-Friendly-Name" for the controller will use your new route.
Checkout this post, particularly where it talks about Route formatting.
using Microsoft.AspNetCore.Mvc;
[ControllerName("SomeOtherName")] // This attribute is not made up.
public class ReportsController : Controller
{
...
I don't know if there's answer to your question, but even if there were one, why in the world would you do such a thing? Why not do standard way : call the controller class anything you like (as long as it makes sense) and have it called by the framework for you? Why create two artificial naming conventions and map between them? I see not a single gain, but multiple cons. Such as - its harder to read, harder to maintain, harder to understand, it is also insignificant, but still, a strain on performance ...
Update
Please look into this posting, I think they did solve the problem that you're talking about.
How to achieve a dynamic controller and action method in ASP.NET MVC?
Please let me know if it was of any help to you.

Can I share a view in asp.net mvc?

My controller has 2 actions:
Results()
Index()
I want to share the view named index.aspx between these 2 actions.
See my previous post for more information
When I build my link to the page, I assume I cannot send it to Index action as it is expecting a FormCollection type and hence I create a Results action
public ActionResult Results(ClientSearch data, int? page)
{
FormCollection collection = new FormCollection();
collection.Add("FNAme", data.FName);
collection.Add("Lane", data.Lane);
collection.Add("Zip", data.Zip);
collection.Add("Phone", data.Phone);
return Index(page, collection);
}
Not sure I completely understand your question, but if you want to use the same View on different ActionResults, you can:
public ActionResult One() {
// do stuff
return View("Index", myModel);
}
public ActionResult Two() {
// do stuff
return View("Index", myOtherModel); // Same View
}
Just make sure you are providing the same Type for the View (if the View needs a Type at all).
Of course you can. It's up to controller to decide how to react and what view to serve back.
Now that I've read your question to the end :)), well, you can get away with two actions of the same name. The one will be accepting GET commands (initial load of the page), the other will be serving POST requests, perform the necessary action and redirect back to the same View.
public MyController
{
[AcceptVerbs (HttpVerbs.Get)]
public ActionResult Index ()
{
return View ();
}
[AcceptVerbs (HttpVerbs.Post)]
public ActionResult Index (ClientSearch data, int? page)
{
// Process form post
return RedirectToAction ("Index");
}
}

Categories

Resources