Simple Introduction to MVC Ajax (Post 3)
Now we have partial views loading forms using Ajax and we can call controller methods using Ajax it's time to put everything together.
We're going to start with a blank scenario to make it easier to follow. This makes this a very long post but it puts together everything we learned.
Code files
I managed to coerce PHP into creating a new route on my website. You can see the full code for the classes/views in this demo as follows:
- Cat.cs
- CatController.cs
- Grudge.cs
- GrudgeController.cs
- Details.cshtml
- _Create.cshtml
- _GrudgesForCat.cshtml
Setting up the test environment
Cats are spiteful creatures, full of barely concealed malice. It's one of their most loveable qualities, let's make a view to show the details of our cat and the grudges it holds.
Let's add a class to represent our cat (Pocos/Cat.cs):
public class Cat
{
public int Id { get; set; }
public string Name { get; set; }
public int AngerLevel { get; set; }
public DateTime BirthDate { get; set; }
public List<Grudge> Grudges { get; set; }
}
Where the Grudge class is represented by the following (Pocos/Grudge.cs):
public class Grudge
{
public int Id { get; set; }
public string TargetName { get; set; }
public string Reason { get; set; }
}
To view the details of our cat we need a controller. In this demo I'm using the Session as our backing data store. This is terrible and should never be done, however this isn't about how to data bind. Add the controller (Controllers/CatController.cs):
public ActionResult Details()
{
Cat cat = new Cat
{
Id = 1,
BirthDate = new DateTime(2005, 10, 3),
Name = "Mr. Tibbles",
AngerLevel = 100,
Grudges = new List<Grudge> {
new Grudge
{
Id = 25,
TargetName = "All Humans" ,
Reason = "Opposable thumbs"
}
}
};
Session["Cat"] = cat;
return View(cat);
}
Finally a view for our cat, unfortunately the syntax highlighter mangles this code. We won't worry about showing Grudges here (Views/Cat/Details.cshtml):
@model SandboxMvc.Pocos.Cat
<h2>Cat</h2><div>
<dl class="dl-horizontal">
<dt>@Html.DisplayNameFor(model => model.Name)</dt>
<dd>@Html.DisplayFor(model => model.Name)</dd>
<dt>@Html.DisplayNameFor(model => model.AngerLevel)</dt>
<dd>@Html.DisplayFor(model => model.AngerLevel)</dd>
<dt>@Html.DisplayNameFor(model => model.BirthDate)</dt>
<dd>@Html.DisplayFor(model => model.BirthDate)</dd>
</dl></div>
The Book of Grudges
We need a partial view to display our cat's grudges.
We're going to include an Html.Action
in our view so we need a method in the Controller (Controllers/GrudgeController.cs) to call:
public PartialViewResult _GrudgesForCat(int id)
{
List<Grudge> grudges = ((Cat)Session["Cat"]).Grudges;
return PartialView(grudges);
}
We also need to include the Partial View in our Details view:
<div id="BookOfGrudges">
@Html.Action(actionName: "_GrudgesForCat",
controllerName: "Grudge",
routeValues: new { id = Model.Id })
</div>
And finally we need a PartialView for this action (Views/Grudge/_GrudgesForCat.cshtml):
@model List<SandboxMvc.Pocos.Grudge>
@for (int i = 0; i < Model.Count; i++)
{
@Html.DisplayFor(m => m[i].TargetName) <br/>
@Html.DisplayFor(m => m[i].Reason) <br/><br/>
}
When you run this you should be able to see a representation of the Cat we created, with associated Grudge(s).
The list grows
It would be nice to create new grudges (if you're a cat). We'd like to do that without leaving our page.
We're going to include a partial view of a form to create new grudges. Modify the Details view to add the following:
<div id="AddToGrudgeList">
@Html.Action(actionName: "_Create",
controllerName: "Grudge",
routeValues: new { id = Model.Id });
</div>
We then need the backing Action on our Grudge controller for both Get and Post:
public PartialViewResult _Create (int id)
{
return PartialView();
}
[HttpPost]
public ActionResult _Create(Grudge model)
{
((Cat)Session["Cat"]).Grudges.Add(model);
return RedirectToAction(actionName: "_GrudgesForCat", routeValues: new { id = 9 });
}
The create view is a fairly long form but look a bit like this. It's important that you specify the action name and controller (Views/Grudge/_Create.cshtml):
@model SandboxMvc.Pocos.Grudge
@using (Html.BeginForm(actionName: "_Create", controllerName: "Grudge"))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Grudge</h4>
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
[FORM INPUTS]
</div>
<input type="submit" value="Create" class="btn btn-default" />
</div>
}
When we click to create a grudge the action "_Create" is called and the Grudge is added to the list. However the result opens in a new page rather than updating our Book of Grudges.
Make it more... Ajax
Everything we've seen so far has been normal MVC using HTML only. All actions are called on the server by performing a full post/get.
Make sure you have Ajax installed in your project (see post 1).
In order to dynamically update our Book of Grudges using Ajax we need to make one change, change the @Html.BeginForm
to @Ajax.BeginForm
(Views/Grudges/_Create.cshtml):
@model SandboxMvc.Pocos.Grudge
@using (Ajax.BeginForm(actionName: "_Create",
controllerName: "Grudge",
ajaxOptions: new AjaxOptions { UpdateTargetId = "BookOfGrudges" }))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
[FORM]
<input type="submit" value="Create" class="btn btn-default" />
</div>
}
That's it, now your Book of Grudges is updated using partial page updates.
Conclusion
Over the past 3 posts I've walked through how to get started with Ajax in MVC 5. This series addresses some of the main pain points involved in getting this working with Partial Views and forms.
The Ajax included in MVC makes it possible to have a fully featured Ajax application without having to touch a line of Javascript, a positive because js is a language only people with a true passion for ducks could love.
If you have any questions or tips please add a comment; I'd be interested to hear the many, many things I got wrong in this series.
The solution view of the finished project is shown here:
Cat picture from Wikipedia.