ASP.NET MVC 4 Helpers, Forms and Validation
Download Web Camps Training Kit
In ASP.NET MVC 4 Models and Data Access Hands-on Lab, you have been loading and displaying data from the database. In this Hands-on Lab, you will add to the Music Store application the ability to edit that data.
With that goal in mind, you will first create the controller that will support the Create, Read, Update and Delete (CRUD) actions of albums. You will generate an Index View template taking advantage of ASP.NET MVC's scaffolding feature to display the albums' properties in an HTML table. To enhance that view, you will add a custom HTML helper that will truncate long descriptions.
Afterwards, you will add the Edit and Create Views that will let you alter the albums in the database, with the help of form elements like dropdowns.
Lastly, you will let users delete an album and also you will prevent them from entering wrong data by validating their input.
This Hands-on Lab assumes you have basic knowledge of ASP.NET MVC. If you have not used ASP.NET MVC before, we recommend you to go over ASP.NET MVC Fundamentals Hands-on Lab.
This lab walks you through the enhancements and new features previously described by applying minor changes to a sample Web application provided in the Source folder.
Note
All sample code and snippets are included in the Web Camps Training Kit, available at Microsoft-Web/WebCampTrainingKit Releases. The project specific to this lab is available at ASP.NET MVC 4 Helpers, Forms and Validation.
Objectives
In this Hands-On Lab, you will learn how to:
- Create a controller to support CRUD operations
- Generate an Index View to display entity properties in an HTML table
- Add a custom HTML helper
- Create and customize an Edit View
- Differentiate between action methods that react to either HTTP-GET or HTTP-POST calls
- Add and customize a Create View
- Handle the deletion of an entity
- Validate user input
Prerequisites
You must have the following items to complete this lab:
- Microsoft Visual Studio Express 2012 for Web or superior (read Appendix A for instructions on how to install it).
Setup
Installing Code Snippets
For convenience, much of the code you will be managing along this lab is available as Visual Studio code snippets. To install the code snippets run .\Source\Setup\CodeSnippets.vsi file.
If you are not familiar with the Visual Studio Code Snippets, and want to learn how to use them, you can refer to the appendix from this document "Appendix B: Using Code Snippets".
Exercises
The following exercises make up this Hands-On Lab:
- Creating the Store Manager controller and its Index view
- Adding an HTML Helper
- Creating the Edit View
- Adding a Create View
- Handling Deletion
- Adding Validation
- Using Unobtrusive jQuery at Client Side
Note
Each exercise is accompanied by an End folder containing the resulting solution you should obtain after completing the exercises. You can use this solution as a guide if you need additional help working through the exercises.
Estimated time to complete this lab: 60 minutes
Exercise 1: Creating the Store Manager controller and its Index view
In this exercise, you will learn how to create a new controller to support CRUD operations, customize its Index action method to return a list of albums from the database and finally generating an Index View template taking advantage of ASP.NET MVC's scaffolding feature to display the albums' properties in an HTML table.
Task 1 - Creating the StoreManagerController
In this task, you will create a new controller called StoreManagerController to support CRUD operations.
Open the Begin solution located at Source/Ex1-CreatingTheStoreManagerController/Begin/ folder.
You will need to download some missing NuGet packages before continue. To do this, click the Project menu and select Manage NuGet Packages.
In the Manage NuGet Packages dialog, click Restore in order to download missing packages.
Finally, build the solution by clicking Build | Build Solution.
Note
One of the advantages of using NuGet is that you don't have to ship all the libraries in your project, reducing the project size. With NuGet Power Tools, by specifying the package versions in the Packages.config file, you will be able to download all the required libraries the first time you run the project. This is why you will have to run these steps after you open an existing solution from this lab.
Add a new controller. To do this, right-click the Controllers folder within the Solution Explorer, select Add and then the Controller command. Change the Controller Name to StoreManagerController and make sure the option MVC controller with empty read/write actions is selected. Click Add.
Add Controller Dialog
A new Controller class is generated. Since you indicated to add actions for read/write, stub methods for those, common CRUD actions are created with TODO comments filled in, prompting to include the application specific logic.
Task 2 - Customizing the StoreManager Index
In this task, you will customize the StoreManager Index action method to return a View with the list of albums from the database.
In the StoreManagerController class, add the following using directives.
(Code Snippet - ASP.NET MVC 4 Helpers and Forms and Validation - Ex1 using MvcMusicStore)
using System.Data; using System.Data.Entity; using MvcMusicStore.Models;
Add a field to the StoreManagerController to hold an instance of MusicStoreEntities.
(Code Snippet - ASP.NET MVC 4 Helpers and Forms and Validation - Ex1 MusicStoreEntities)
public class StoreManagerController : Controller { private MusicStoreEntities db = new MusicStoreEntities();
Implement the StoreManagerController Index action to return a View with the list of albums.
The Controller action logic will be very similar to the StoreController's Index action written earlier. Use LINQ to retrieve all albums, including Genre and Artist information for display.
(Code Snippet - ASP.NET MVC 4 Helpers and Forms and Validation - Ex1 StoreManagerController Index)
// // GET: /StoreManager/ public ActionResult Index() { var albums = this.db.Albums.Include(a => a.Genre).Include(a => a.Artist) .OrderBy(a => a.Price); return this.View(albums.ToList()); }
Task 3 - Creating the Index View
In this task, you will create the Index View template to display the list of albums returned by the StoreManager Controller.
Before creating the new View template, you should build the project so that the Add View Dialog knows about the Album class to use. Select Build | Build MvcMusicStore to build the project.
Right-click inside the Index action method and select Add View. This will bring up the Add View dialog.
Adding a View from within the Index method
In the Add View dialog, verify that the View Name is Index. Select the Create a strongly-typed view option, and select Album (MvcMusicStore.Models) from the Model class drop-down. Select List from the Scaffold template drop-down. Leave the View engine to Razor and the other fields with their default value and then click Add.
Adding an Index View
Task 4 - Customizing the scaffold of the Index View
In this task, you will adjust the simple View template created with ASP.NET MVC scaffolding feature to have it display the fields you want.
Note
The scaffolding support within ASP.NET MVC generates a simple View template which lists all fields in the Album model. Scaffolding provides a quick way to get started on a strongly typed view: rather than having to write the View template manually, scaffolding quickly generates a default template and then you can modify the generated code.
Review the code created. The generated list of fields will be part of the following HTML table that Scaffolding is using for displaying tabular data.
@model IEnumerable<MvcMusicStore.Models.Album> @{ ViewBag.Title = "Index"; } <h2>Index</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th> @Html.DisplayNameFor(model => model.GenreId) </th> <th> @Html.DisplayNameFor(model => model.ArtistId) </th> <th> @Html.DisplayNameFor(model => model.Title) </th> <th> @Html.DisplayNameFor(model => model.Price) </th> <th> @Html.DisplayNameFor(model => model.AlbumArtUrl) </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.GenreId) </td> <td> @Html.DisplayFor(modelItem => item.ArtistId) </td> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> <td> @Html.DisplayFor(modelItem => item.AlbumArtUrl) </td> <td> @Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) | @Html.ActionLink("Details", "Details", new { id=item.AlbumId }) | @Html.ActionLink("Delete", "Delete", new { id=item.AlbumId }) </td> </tr> } </table>
Replace the <table> code with the following code to display only the Genre, Artist, Album Title, and Price fields. This deletes the AlbumId and Album Art URL columns. Also, it changes GenreId and ArtistId columns to display their linked class properties of Artist.Name and Genre.Name, and removes the Details link.
<table> <tr> <th></th> <th>Genre</th> <th>Artist</th> <th>Title</th> <th>Price</th> </tr> @foreach (var item in Model) { <tr> <td> @Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) | @Html.ActionLink("Delete", "Delete", new { id=item.AlbumId }) </td> <td> @Html.DisplayFor(modelItem => item.Genre.Name) </td> <td> @Html.DisplayFor(modelItem => item.Artist.Name) </td> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> </tr> } </table>
Change the following descriptions.
@model IEnumerable<MvcMusicStore.Models.Album> @{ ViewBag.Title = "Store Manager - All Albums"; } <h2>Albums</h2>
Task 5 - Running the Application
In this task, you will test that the StoreManager Index View template displays a list of albums according to the design of the previous steps.
Press F5 to run the Application.
The project starts in the Home page. Change the URL to /StoreManager to verify that a list of albums is displayed, showing their Title, Artist and Genre.
Browsing the list of albums
Exercise 2: Adding an HTML Helper
The StoreManager Index page has one potential issue: Title and Artist Name properties can both be long enough to throw off the table formatting. In this exercise you will learn how to add a custom HTML helper to truncate that text.
In the following figure, you can see how the format is modified because of the length of the text when you use a small browser size.
Browsing the list of Albums with not truncated text
Task 1 - Extending the HTML Helper
In this task, you will add a new method Truncate to the HTML object exposed within ASP.NET MVC Views. To do this, you will implement an extension method to the built-in System.Web.Mvc.HtmlHelper class provided by ASP.NET MVC.
Note
To read more about Extension Methods, please visit this msdn article. https://msdn.microsoft.com/library/bb383977.aspx.
Open the Begin solution located at Source/Ex2-AddingAnHTMLHelper/Begin/ folder. Otherwise, you might continue using the End solution obtained by completing the previous exercise.
If you opened the provided Begin solution, you will need to download some missing NuGet packages before continue. To do this, click the Project menu and select Manage NuGet Packages.
In the Manage NuGet Packages dialog, click Restore in order to download missing packages.
Finally, build the solution by clicking Build | Build Solution.
Note
One of the advantages of using NuGet is that you don't have to ship all the libraries in your project, reducing the project size. With NuGet Power Tools, by specifying the package versions in the Packages.config file, you will be able to download all the required libraries the first time you run the project. This is why you will have to run these steps after you open an existing solution from this lab.
Open StoreManager's Index View. To do this, in the Solution Explorer expand the Views folder, then the StoreManager and open the Index.cshtml file.
Add the following code below the @model directive to define the Truncate helper method.
@model IEnumerable<MvcMusicStore.Models.Album> @helper Truncate(string input, int length) { if (input.Length <= length) { @input } else { @input.Substring(0, length)<text>...</text> } } @{ ViewBag.Title = "Store Manager - All Albums"; } <h2>Albums</h2>
Task 2 - Truncating Text in the Page
In this task, you will use the Truncate method to truncate the text in the View template.
Open StoreManager's Index View. To do this, in the Solution Explorer expand the Views folder, then the StoreManager and open the Index.cshtml file.
Replace the lines that show the Artist Name and Album's Title. To do this, replace the following lines.
<tr> <td> @Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) | @Html.ActionLink("Delete", "Delete", new { id=item.AlbumId }) </td> <td> @Html.DisplayFor(modelItem => item.Genre.Name) </td> <td> @Truncate(item.Artist.Name, 25) </td> <td> @Truncate(item.Title, 25) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> </tr>
Task 3 - Running the Application
In this task, you will test that the StoreManager Index View template truncates the Album's Title and Artist Name.
Press F5 to run the Application.
The project starts in the Home page. Change the URL to /StoreManager to verify that long texts in the Title and Artist column are truncated.
Truncated Titles and Artist Names
Exercise 3: Creating the Edit View
In this exercise, you will learn how to create a form to allow store managers to edit an Album. They will browse the /StoreManager/Edit/id URL (id being the unique id of the album to edit), thus making an HTTP-GET call to the server.
The Controller Edit action method will retrieve the appropriate Album from the database, create a StoreManagerViewModel object to encapsulate it (along with a list of Artists and Genres), and then pass it off to a View template to render the HTML page back to the user. This page will contain a <form> element with textboxes and dropdowns for editing the Album properties.
Once the user updates the Album form values and clicks the Save button, the changes are submitted via an HTTP-POST call back to /StoreManager/Edit/id. Although the URL remains the same as in the last call, ASP.NET MVC identifies that this time it is an HTTP-POST and therefore executes a different Edit action method (one decorated with [HttpPost]).
Task 1 - Implementing the HTTP-GET Edit Action Method
In this task, you will implement the HTTP-GET version of the Edit action method to retrieve the appropriate Album from the database, as well as a list of all Genres and Artists. It will package this data up into the StoreManagerViewModel object defined in the last step, which will then be passed to a View template to render the response with.
Open the Begin solution located at Source/Ex3-CreatingTheEditView/Begin/ folder. Otherwise, you might continue using the End solution obtained by completing the previous exercise.
If you opened the provided Begin solution, you will need to download some missing NuGet packages before continue. To do this, click the Project menu and select Manage NuGet Packages.
In the Manage NuGet Packages dialog, click Restore in order to download missing packages.
Finally, build the solution by clicking Build | Build Solution.
Note
One of the advantages of using NuGet is that you don't have to ship all the libraries in your project, reducing the project size. With NuGet Power Tools, by specifying the package versions in the Packages.config file, you will be able to download all the required libraries the first time you run the project. This is why you will have to run these steps after you open an existing solution from this lab.
Open the StoreManagerController class. To do this, expand the Controllers folder and double-click StoreManagerController.cs.
Replace the HTTP-GET Edit action method with the following code to retrieve the appropriate Album as well as the Genres and Artists lists.
(Code Snippet - ASP.NET MVC 4 Helpers and Forms and Validation - Ex3 StoreManagerController HTTP-GET Edit action)
public ActionResult Edit(int id) { Album album = this.db.Albums.Find(id); if (album == null) { return this.HttpNotFound(); } this.ViewBag.GenreId = new SelectList(this.db.Genres, "GenreId", "Name", album.GenreId); this.ViewBag.ArtistId = new SelectList(this.db.Artists, "ArtistId", "Name", album.ArtistId); return this.View(album); }
Note
You are using System.Web.Mvc SelectList for Artists and Genres instead of the System.Collections.Generic List.
SelectList is a cleaner way to populate HTML dropdowns and manage things like current selection. Instantiating and later setting up these ViewModel objects in the controller action will make the Edit form scenario cleaner.
Task 2 - Creating the Edit View
In this task, you will create an Edit View template that will later display the album properties.
Create the Edit View. To do this, right-click inside the Edit action method and select Add View.
In the Add View dialog, verify that the View Name is Edit. Check the Create a strongly-typed view checkbox and select Album (MvcMusicStore.Models) from the View data class drop-down. Select Edit from the Scaffold template drop-down. Leave the other fields with their default value and then click Add.
Adding an Edit view
Task 3 - Running the Application
In this task, you will test that the StoreManager Edit View page displays the properties' values for the album passed as parameter.
Press F5 to run the Application.
The project starts in the Home page. Change the URL to /StoreManager/Edit/1 to verify that the properties' values for the album passed are displayed.
Browsing Album's Edit view
Task 4 - Implementing drop-downs on the Album Editor Template
In this task, you will add drop-downs to the View template created in the last task, so that the user can select from a list of Artists and Genres.
Replace all the Album fieldset code with the following:
<fieldset> <legend>Album</legend> @Html.HiddenFor(model => model.AlbumId) <div class="editor-label"> @Html.LabelFor(model => model.Title) </div> <div class="editor-field"> @Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) </div> <div class="editor-label"> @Html.LabelFor(model => model.Price) </div> <div class="editor-field"> @Html.EditorFor(model => model.Price) @Html.ValidationMessageFor(model => model.Price) </div> <div class="editor-label"> @Html.LabelFor(model => model.AlbumArtUrl) </div> <div class="editor-field"> @Html.EditorFor(model => model.AlbumArtUrl) @Html.ValidationMessageFor(model => model.AlbumArtUrl) </div> <div class="editor-label"> @Html.LabelFor(model => model.Artist) </div> <div class="editor-field"> @Html.DropDownList("ArtistId", (SelectList) ViewData["Artists"]) @Html.ValidationMessageFor(model => model.ArtistId) </div> <div class="editor-label"> @Html.LabelFor(model => model.Genre) </div> <div class="editor-field"> @Html.DropDownList("GenreId", (SelectList) ViewData["Genres"]) @Html.ValidationMessageFor(model => model.GenreId) </div> <p> <input type="submit" value="Save" /> </p> </fieldset>
Note
An Html.DropDownList helper has been added to render drop-downs for choosing Artists and Genres. The parameters passed to Html.DropDownList are:
- The name of the form field ("ArtistId").
- The SelectList of values for the drop-down.
Task 5 - Running the Application
In this task, you will test that the StoreManager Edit View page displays drop-downs instead of Artist and Genre ID text fields.
Press F5 to run the Application.
The project starts in the Home page. Change the URL to /StoreManager/Edit/1 to verify that it displays drop-downs instead of Artist and Genre ID text fields.
Browsing Album's Edit view, this time with dropdowns
Task 6 - Implementing the HTTP-POST Edit action method
Now that the Edit View displays as expected, you need to implement the HTTP-POST Edit Action method to save the changes made to the Album.
Close the browser if needed, to return to the Visual Studio window. Open StoreManagerController from the Controllers folder.
Replace HTTP-POST Edit action method code with the following (note that the method that must be replaced is overloaded version that receives two parameters):
(Code Snippet - ASP.NET MVC 4 Helpers and Forms and Validation - Ex3 StoreManagerController HTTP-POST Edit action)
[HttpPost] public ActionResult Edit(Album album) { if (ModelState.IsValid) { this.db.Entry(album).State = EntityState.Modified; this.db.SaveChanges(); return this.RedirectToAction("Index"); } this.ViewBag.GenreId = new SelectList(this.db.Genres, "GenreId", "Name", album.GenreId); this.ViewBag.ArtistId = new SelectList(this.db.Artists, "ArtistId", "Name", album.ArtistId); return this.View(album); }
Note
This method will be executed when the user clicks the Save button of the View and performs an HTTP-POST of the form values back to the server to persist them in the database. The decorator [HttpPost] indicates that the method should be used for those HTTP-POST scenarios. The method takes an Album object. ASP.NET MVC will automatically create the Album object from the posted <form> values.
The method will perform these steps:
If model is valid:
- Update the album entry in the context to mark it as a modified object.
- Save the changes and redirect to the index view.
If the model is not valid, it will populate the ViewBag with the GenreId and ArtistId, then it will return the view with the received Album object to allow the user perform any required update.
Task 7 - Running the Application
In this task, you will test that the StoreManager Edit View page actually saves the updated Album data in the database.
Press F5 to run the Application.
The project starts in the Home page. Change the URL to /StoreManager/Edit/1. Change the Album title to Load and click on Save. Verify that album's title actually changed in the list of albums.
Updating an Album
Exercise 4: Adding a Create View
Now that the StoreManagerController supports the Edit ability, in this exercise you will learn how to add a Create View template to let store managers add new Albums to the application.
Like you did with the Edit functionality, you will implement the Create scenario using two separate methods within the StoreManagerController class:
- One action method will display an empty form when store managers first visit the /StoreManager/Create URL.
- A second action method will handle the scenario where the store manager clicks the Save button within the form and submits the values back to the /StoreManager/Create URL as an HTTP-POST.
Task 1 - Implementing the HTTP-GET Create action method
In this task, you will implement the HTTP-GET version of the Create action method to retrieve a list of all Genres and Artists, package this data up into a StoreManagerViewModel object, which will then be passed to a View template.
Open the Begin solution located at Source/Ex4-AddingACreateView/Begin/ folder. Otherwise, you might continue using the End solution obtained by completing the previous exercise.
If you opened the provided Begin solution, you will need to download some missing NuGet packages before continue. To do this, click the Project menu and select Manage NuGet Packages.
In the Manage NuGet Packages dialog, click Restore in order to download missing packages.
Finally, build the solution by clicking Build | Build Solution.
Note
One of the advantages of using NuGet is that you don't have to ship all the libraries in your project, reducing the project size. With NuGet Power Tools, by specifying the package versions in the Packages.config file, you will be able to download all the required libraries the first time you run the project. This is why you will have to run these steps after you open an existing solution from this lab.
Open StoreManagerController class. To do this, expand the Controllers folder and double-click StoreManagerController.cs.
Replace the Create action method code with the following:
(Code Snippet - ASP.NET MVC 4 Helpers and Forms and Validation - Ex4 StoreManagerController HTTP-GET Create action)
// // GET: /StoreManager/Create public ActionResult Create() { this.ViewBag.GenreId = new SelectList(this.db.Genres, "GenreId", "Name"); this.ViewBag.ArtistId = new SelectList(this.db.Artists, "ArtistId", "Name"); return this.View(); }
Task 2 - Adding the Create View
In this task, you will add the Create View template that will display a new (empty) Album form.
Right-click inside the Create action method and select Add View. This will bring up the Add View dialog.
In the Add View dialog, verify that the View Name is Create. Select the Create a strongly-typed view option and select Album (MvcMusicStore.Models) from the Model class drop-down and Create from the Scaffold template drop-down. Leave the other fields with their default value and then click Add.
Adding the Create View
Update the GenreId and ArtistId fields to use a drop-down list as shown below:
... <fieldset> <legend>Album</legend> <div class="editor-label"> @Html.LabelFor(model => model.GenreId, "Genre") </div> <div class="editor-field"> @Html.DropDownList("GenreId", String.Empty) @Html.ValidationMessageFor(model => model.GenreId) </div> <div class="editor-label"> @Html.LabelFor(model => model.ArtistId, "Artist") </div> <div class="editor-field"> @Html.DropDownList("ArtistId", String.Empty) @Html.ValidationMessageFor(model => model.ArtistId) </div> <div class="editor-label"> @Html.LabelFor(model => model.Title) </div> ...
Task 3 - Running the Application
In this task, you will test that the StoreManager Create View page displays an empty Album form.
Press F5 to run the Application.
The project starts in the Home page. Change the URL to /StoreManager/Create. Verify that an empty form is displayed for filling the new Album properties.
Create View with an empty form
Task 4 - Implementing the HTTP-POST Create Action Method
In this task, you will implement the HTTP-POST version of the Create action method that will be invoked when a user clicks the Save button. The method should save the new album in the database.
Close the browser if needed, to return to the Visual Studio window. Open StoreManagerController class. To do this, expand the Controllers folder and double-click StoreManagerController.cs.
Replace HTTP-POST Create action method code with the following:
(Code Snippet - ASP.NET MVC 4 Helpers and Forms and Validation - Ex4 StoreManagerController HTTP- POST Create action)
[HttpPost] public ActionResult Create(Album album) { if (ModelState.IsValid) { this.db.Albums.Add(album); this.db.SaveChanges(); return this.RedirectToAction("Index"); } this.ViewBag.GenreId = new SelectList(this.db.Genres, "GenreId", "Name", album.GenreId); this.ViewBag.ArtistId = new SelectList(this.db.Artists, "ArtistId", "Name", album.ArtistId); return this.View(album); }
Note
The Create action is pretty similar to the previous Edit action method but instead of setting the object as modified, it is being added to the context.
Task 5 - Running the Application
In this task, you will test that the StoreManager Create View page lets you create a new Album and then redirects to the StoreManager Index View.
Press F5 to run the Application.
The project starts in the Home page. Change the URL to /StoreManager/Create. Fill all the form fields with data for a new Album, like the one in the following figure:
Creating an Album
Verify that you get redirected to the StoreManager Index View that includes the new Album just created.
New Album Created
Exercise 5: Handling Deletion
The ability to delete albums is not yet implemented. This is what this exercise will be about. Like before, you will implement the Delete scenario using two separate methods within the StoreManagerController class:
- One action method will display a confirmation form
- A second action method will handle the form submission
Task 1 - Implementing the HTTP-GET Delete Action Method
In this task, you will implement the HTTP-GET version of the Delete action method to retrieve the album's information.
Open the Begin solution located at Source/Ex5-HandlingDeletion/Begin/ folder. Otherwise, you might continue using the End solution obtained by completing the previous exercise.
If you opened the provided Begin solution, you will need to download some missing NuGet packages before continue. To do this, click the Project menu and select Manage NuGet Packages.
In the Manage NuGet Packages dialog, click Restore in order to download missing packages.
Finally, build the solution by clicking Build | Build Solution.
Note
One of the advantages of using NuGet is that you don't have to ship all the libraries in your project, reducing the project size. With NuGet Power Tools, by specifying the package versions in the Packages.config file, you will be able to download all the required libraries the first time you run the project. This is why you will have to run these steps after you open an existing solution from this lab.
Open StoreManagerController class. To do this, expand the Controllers folder and double-click StoreManagerController.cs.
The Delete controller action is exactly the same as the previous Store Details controller action: it queries the album object from the database using the id provided in the URL and returns the appropriate View. To do this, replace the HTTP-GET Delete action method code with the following:
(Code Snippet - ASP.NET MVC 4 Helpers and Forms and Validation - Ex5 Handling Deletion HTTP-GET Delete action)
// // GET: /StoreManager/Delete/5 public ActionResult Delete(int id) { Album album = this.db.Albums.Find(id); if (album == null) { return this.HttpNotFound(); } return this.View(album); }
Right-click inside the Delete action method and select Add View. This will bring up the Add View dialog.
In the Add View dialog, verify that the View name is Delete. Select the Create a strongly-typed view option and select Album (MvcMusicStore.Models) from the Model class drop-down. Select Delete from the Scaffold template drop-down. Leave the other fields with their default value and then click Add.
Adding a Delete View
The Delete template shows all the fields from the model. You will show only the album's title. To do this, replace the content of the view with the following code:
@model MvcMusicStore.Models.Album @{ ViewBag.Title = "Delete"; } <h2>Delete Confirmation</h2> <h3> Are you sure you want to delete the album title <strong>@Model.Title </strong> ? </h3> @using (Html.BeginForm()) { <p> <input type="submit" value="Delete" /> | @Html.ActionLink("Back to List", "Index") </p> }
Task 2 - Running the Application
In this task, you will test that the StoreManager Delete View page displays a confirmation deletion form.
Press F5 to run the Application.
The project starts in the Home page. Change the URL to /StoreManager. Select one album to delete by clicking Delete and verify that the new view is uploaded.
Deleting an Album
Task 3- Implementing the HTTP-POST Delete Action Method
In this task, you will implement the HTTP-POST version of the Delete action method that will be invoked when a user clicks the Delete button. The method should delete the album in the database.
Close the browser if needed, to return to the Visual Studio window. Open StoreManagerController class. To do this, expand the Controllers folder and double-click StoreManagerController.cs.
Replace HTTP-POST Delete action method code with the following:
(Code Snippet - ASP.NET MVC 4 Helpers and Forms and Validation - Ex5 Handling Deletion HTTP-POST Delete action)
// // POST: /StoreManager/Delete/5 [HttpPost] public ActionResult Delete(int id, FormCollection collection) { Album album = this.db.Albums.Find(id); this.db.Albums.Remove(album); this.db.SaveChanges(); return this.RedirectToAction("Index"); }
Task 4 - Running the Application
In this task, you will test that the StoreManager Delete View page lets you delete an Album and then redirects to the StoreManager Index View.
Press F5 to run the Application.
The project starts in the Home page. Change the URL to /StoreManager. Select one album to delete by clicking Delete. Confirm the deletion by clicking Delete button:
Deleting an Album
Verify that the album was deleted since it does not appear in the Index page.
Exercise 6: Adding Validation
Currently, the Create and Edit forms you have in place do not perform any kind of validation. If the user leaves a required field blank or type letters in the price field, the first error you will get will be from the database.
You can add validation to the application by adding Data Annotations to your model class. Data Annotations allow describing the rules you want applied to your model properties, and ASP.NET MVC will take care of enforcing and displaying appropriate message to users.
Task 1 - Adding Data Annotations
In this task, you will add Data Annotations to the Album Model that will make the Create and Edit page display validation messages when appropriate.
For a simple Model class, adding a Data Annotation is just handled by adding a using statement for System.ComponentModel.DataAnnotation, then placing a [Required] attribute on the appropriate properties. The following example would make the Name property a required field in the View.
using System.ComponentModel.DataAnnotations;
namespace SuperheroSample.Models
{
public class Superhero
{
[Required]
public string Name { get; set; }
public bool WearsCape { get; set; }
}
}
This is a little more complex in cases like this application where the Entity Data Model is generated. If you added Data Annotations directly to the model classes, they would be overwritten if you update the model from the database. Instead, you can make use of metadata partial classes which will exist to hold the annotations and are associated with the model classes using the [MetadataType] attribute.
Open the Begin solution located at Source/Ex6-AddingValidation/Begin/ folder. Otherwise, you might continue using the End solution obtained by completing the previous exercise.
If you opened the provided Begin solution, you will need to download some missing NuGet packages before continue. To do this, click the Project menu and select Manage NuGet Packages.
In the Manage NuGet Packages dialog, click Restore in order to download missing packages.
Finally, build the solution by clicking Build | Build Solution.
Note
One of the advantages of using NuGet is that you don't have to ship all the libraries in your project, reducing the project size. With NuGet Power Tools, by specifying the package versions in the Packages.config file, you will be able to download all the required libraries the first time you run the project. This is why you will have to run these steps after you open an existing solution from this lab.
Open the Album.cs from the Models folder.
Replace Album.cs content with the highlighted code, so that it looks like the following:
Note
The line [DisplayFormat(ConvertEmptyStringToNull=false)] indicates that empty strings from the model won't be converted to null when the data field is updated in the data source. This setting will avoid an exception when the Entity Framework assigns null values to the model before Data Annotation validates the fields.
(Code Snippet - ASP.NET MVC 4 Helpers and Forms and Validation - Ex6 Album metadata partial class)
namespace MvcMusicStore.Models { using System.ComponentModel; using System.ComponentModel.DataAnnotations; public class Album { [ScaffoldColumn(false)] public int AlbumId { get; set; } [DisplayName("Genre")] public int GenreId { get; set; } [DisplayName("Artist")] public int ArtistId { get; set; } [Required(ErrorMessage = "An Album Title is required")] [DisplayFormat(ConvertEmptyStringToNull = false)] [StringLength(160, MinimumLength = 2)] public string Title { get; set; } [Range(0.01, 100.00, ErrorMessage = "Price must be between 0.01 and 100.00")] [DataType(DataType.Currency)] public decimal Price { get; set; } [DisplayName("Album Art URL")] [DataType(DataType.ImageUrl)] [StringLength(1024)] public string AlbumArtUrl { get; set; } public virtual Genre Genre { get; set; } public virtual Artist Artist { get; set; } } }
Note
This Album partial class has a MetadataType attribute which points to the AlbumMetaData class for the Data Annotations. These are some of the Data Annotation attributes you are using to annotate the Album model:
- Required - Indicates that the property is a required field
- DisplayName - Defines the text to be used on form fields and validation messages
- DisplayFormat - Specifies how data fields are displayed and formatted.
- StringLength - Defines a maximum length for a string field
- Range - Gives a maximum and minimum value for a numeric field
- ScaffoldColumn - Allows hiding fields from editor forms
Task 2 - Running the Application
In this task, you will test that the Create and Edit pages validate fields, using the display names chosen in the last task.
Press F5 to run the Application.
The project starts in the Home page. Change the URL to /StoreManager/Create. Verify that the display names match the ones in the partial class (like Album Art URL instead of AlbumArtUrl)
Click Create, without filling the form. Verify that you get the corresponding validation messages.
Validated fields in the Create page
You can verify that the same occurs with the Edit page. Change the URL to /StoreManager/Edit/1 and verify that the display names match the ones in the partial class (like Album Art URL instead of AlbumArtUrl). Empty the Title and Price fields and click Save. Verify that you get the corresponding validation messages.
Validated fields in the Edit page
Exercise 7: Using Unobtrusive jQuery at Client Side
In this exercise, you will learn how to enable MVC 4 Unobtrusive jQuery validation at client side.
Note
The Unobtrusive jQuery uses data-ajax prefix JavaScript to invoke action methods on the server rather than intrusively emitting inline client scripts.
Task 1 - Running the Application before Enabling Unobtrusive jQuery
In this task, you will run the application before including jQuery in order to compare both validation models.
Open the Begin solution located at Source/Ex7-UnobtrusivejQueryValidation/Begin/ folder. Otherwise, you might continue using the End solution obtained by completing the previous exercise.
If you opened the provided Begin solution, you will need to download some missing NuGet packages before continue. To do this, click the Project menu and select Manage NuGet Packages.
In the Manage NuGet Packages dialog, click Restore in order to download missing packages.
Finally, build the solution by clicking Build | Build Solution.
Note
One of the advantages of using NuGet is that you don't have to ship all the libraries in your project, reducing the project size. With NuGet Power Tools, by specifying the package versions in the Packages.config file, you will be able to download all the required libraries the first time you run the project. This is why you will have to run these steps after you open an existing solution from this lab.
Press F5 to run the application.
The project starts in the Home page. Browse /StoreManager/Create and click Create without filling the form to verify that you get validation messages:
Client validation disabled
In the browser, open the HTML source code:
... <div class="editor-label"> <label for="Price">Price</label> </div> <div class="editor-field"> <input class="text-box single-line" id="Price" name="Price" type="text" value="" /> <span class="field-validation-valid" id="Price_validationMessage"></span> </div> <div class="editor-label"> <label for="AlbumArtUrl">Album Art URL</label> </div> <div class="editor-field"> <input class="text-box single-line" id="AlbumArtUrl" name="AlbumArtUrl" type="text" value="" /> <span class="field-validation-valid" id="AlbumArtUrl_validationMessage"></span> </div> <p> <input type="submit" value="Create" /> </p> </fieldset> </form><script type="text/javascript"> //<![CDATA[ if (!window.mvcClientValidationMetadata) { window.mvcClientValidationMetadata = []; } window.mvcClientValidationMetadata.push({"Fields":[{"FieldName":"GenreId","ReplaceValidationMessageContents":true,"ValidationMessageId":"GenreId_validationMessage","ValidationRules":[{"ErrorMessage":"The Genre field is required.","ValidationParameters":{},"ValidationType":"required"},{"ErrorMessage":"The field Genre must be a number.","ValidationParameters":{},"ValidationType":"number"}]},{"FieldName":"ArtistId","ReplaceValidationMessageContents":true,"ValidationMessageId":"ArtistId_validationMessage","ValidationRules":[{"ErrorMessage":"The Artist field is required.","ValidationParameters":{},"ValidationType":"required"},{"ErrorMessage":"The field Artist must be a number.","ValidationParameters":{},"ValidationType":"number"}]},{"FieldName":"Title","ReplaceValidationMessageContents":true,"ValidationMessageId":"Title_validationMessage","ValidationRules":[{"ErrorMessage":"An Album Title is required","ValidationParameters":{},"ValidationType":"required"},{"ErrorMessage":"The field Title must be a string with a minimum length of 2 and a maximum length of 160.","ValidationParameters":{"min":2,"max":160},"ValidationType":"length"}]},{"FieldName":"Price","ReplaceValidationMessageContents":true,"ValidationMessageId":"Price_validationMessage","ValidationRules":[{"ErrorMessage":"Price must be between 0.01 and 100.00","ValidationParameters":{"min":0.01,"max":100},"ValidationType":"range"},{"ErrorMessage":"Price is required","ValidationParameters":{},"ValidationType":"required"},{"ErrorMessage":"The field Price must be a number.","ValidationParameters":{},"ValidationType":"number"}]},{"FieldName":"AlbumArtUrl","ReplaceValidationMessageContents":true,"ValidationMessageId":"AlbumArtUrl_validationMessage","ValidationRules":[{"ErrorMessage":"The field Album Art URL must be a string with a maximum length of 1024.","ValidationParameters":{"max":1024},"ValidationType":"length"}]}],"FormId":"form0","ReplaceValidationSummary":false,"ValidationSummaryId":"validationSummary"}); //]]> </script> ...
Task 2 - Enabling Unobtrusive Client Validation
In this task, you will enable jQuery unobtrusive client validation from Web.config file, which is by default set to false in all new ASP.NET MVC 4 projects. Additionally, you will add the necessary scripts references to make jQuery Unobtrusive Client Validation work.
Open Web.Config file at project root, and make sure that the ClientValidationEnabled and UnobtrusiveJavaScriptEnabled keys values are set to true.
... <configuration> <appSettings> <add key="webpages:Version" value="2.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="PreserveLoginUrl" value="true" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings> ...
Note
You can also enable client validation by code at Global.asax.cs to get the same results:
HtmlHelper.ClientValidationEnabled = true;
Additionally, you can assign ClientValidationEnabled attribute into any controller to have a custom behavior.
Open Create.cshtml at Views\StoreManager.
Make sure the following script files, jquery.validate and jquery.validate.unobtrusive, are referenced in the view through the "~/bundles/jqueryval" bundle.
... @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
Note
All these jQuery libraries are included in MVC 4 new projects. You can find more libraries in the /Scripts folder of you project.
In order to make this validation libraries work, you need to add a reference to the jQuery framework library. Since this reference is already added in the _Layout.cshtml file, you do not need to add it in this particular view.
Task 3 - Running the Application Using Unobtrusive jQuery Validation
In this task, you will test that the StoreManager create view template performs client side validation using jQuery libraries when the user creates a new album.
Press F5 to run the application.
The project starts in the Home page. Browse /StoreManager/Create and click Create without filling the form to verify that you get validation messages:
Client validation with jQuery enabled
In the browser, open the source code for Create view:
... <div class="editor-label"> <label for="Title">Title</label> </div> <div class="editor-field"> <input class="text-box single-line" data-val="true" data-val-length="The field Title must be a string with a minimum length of 2 and a maximum length of 160." data-val-length-max="160" data-val-length-min="2" data-val-required="An Album Title is required" id="Title" name="Title" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span> </div> <div class="editor-label"> <label for="Price">Price</label> </div> <div class="editor-field"> <input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number." data-val-range="Price must be between 0.01 and 100.00" data-val-range-max="100" data-val-range-min="0.01" data-val-required="Price is required" id="Price" name="Price" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span> </div> <div class="editor-label"> <label for="AlbumArtUrl">Album Art URL</label> </div> <div class="editor-field"> <input class="text-box single-line" data-val="true" data-val-length="The field Album Art URL must be a string with a maximum length of 1024." data-val-length-max="1024" id="AlbumArtUrl" name="AlbumArtUrl" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="AlbumArtUrl" data-valmsg-replace="true"></span> </div> ...
Note
For each client validation rule, Unobtrusive jQuery adds an attribute with data-val-rulename="message". Below is a list of tags that Unobtrusive jQuery inserts into the html input field to perform client validation:
- Data-val
- Data-val-number
- Data-val-range
- Data-val-range-min / Data-val-range-max
- Data-val-required
- Data-val-length
- Data-val-length-max / Data-val-length-min
All the data values are filled with model Data Annotation. Then, all the logic that works at server side can be run at client side. For example, Price attribute has the following data annotation in the model:
[Required(ErrorMessage = "Price is required")] [Range(0.01, 100.00, ErrorMessage = "Price must be between 0.01 and 100.00")] public object Price { get; set; }
After using Unobtrusive jQuery, the generated code is:
<input data-val="true" data-val-number="The field Price must be a number." data-val-range="Price must be between 0.01 and 100.00" data-val-range-max="100" data-val-range-min="0.01" data-val-required="Price is required" id="Album_Price" name="Album.Price" type="text" value="0" />
Summary
By completing this Hands-On Lab you have learned how to enable users to change the data stored in the database with the use of the following:
- Controller actions like Index, Create, Edit, Delete
- ASP.NET MVC's scaffolding feature for displaying properties in an HTML table
- Custom HTML helpers to improve user experience
- Action methods that react to either HTTP-GET or HTTP-POST calls
- A shared editor template for similar View templates like Create and Edit
- Form elements like drop-downs
- Data annotations for Model validation
- Client Side Validation using jQuery Unobtrusive library
Appendix A: Installing Visual Studio Express 2012 for Web
You can install Microsoft Visual Studio Express 2012 for Web or another "Express" version using the Microsoft Web Platform Installer. The following instructions guide you through the steps required to install Visual studio Express 2012 for Web using Microsoft Web Platform Installer.
Go to [https://go.microsoft.com/?linkid=9810169](https://go.microsoft.com/?linkid=9810169). Alternatively, if you already have installed Web Platform Installer, you can open it and search for the product "Visual Studio Express 2012 for Web with Windows Azure SDK".
Click on Install Now. If you do not have Web Platform Installer you will be redirected to download and install it first.
Once Web Platform Installer is open, click Install to start the setup.
Install Visual Studio Express
Read all the products' licenses and terms and click I Accept to continue.
Accepting the license terms
Wait until the downloading and installation process completes.
Installation progress
When the installation completes, click Finish.
Installation completed
Click Exit to close Web Platform Installer.
To open Visual Studio Express for Web, go to the Start screen and start writing "VS Express", then click on the VS Express for Web tile.
VS Express for Web tile
Appendix B: Using Code Snippets
With code snippets, you have all the code you need at your fingertips. The lab document will tell you exactly when you can use them, as shown in the following figure.
Using Visual Studio code snippets to insert code into your project
To add a code snippet using the keyboard (C# only)
- Place the cursor where you would like to insert the code.
- Start typing the snippet name (without spaces or hyphens).
- Watch as IntelliSense displays matching snippets' names.
- Select the correct snippet (or keep typing until the entire snippet's name is selected).
- Press the Tab key twice to insert the snippet at the cursor location.
Start typing the snippet name
Press Tab to select the highlighted snippet
Press Tab again and the snippet will expand
To add a code snippet using the mouse (C#, Visual Basic and XML) 1. Right-click where you want to insert the code snippet.
- Select Insert Snippet followed by My Code Snippets.
- Pick the relevant snippet from the list, by clicking on it.
Right-click where you want to insert the code snippet and select Insert Snippet
Pick the relevant snippet from the list, by clicking on it