[Update: Want to watch this as a screencast rather than article?]
In this ASP.NET MVC Foundations article, we’re going to look at building an ASP.NET MVC page which allows users to create and edit objects in our domain. We’ll cover just the basics of using HTML helpers to map model properties to our HTML form and Model Binding to convert our HTML form back into our rich domain object.
We’ll start with a very basic store website (downloads here: BasicMvcForms_starter.zip and BasicMvcForms_final.zip)which has a database and some basic products already listed:
Notice that we have five products. There links to edit and create products. Currently, they don’t do much:
First we’ll need to add action methods. That’s easy enough. Here’s the create code:
And similarly, the edit code:
Now that we have action methods for edit and create, we’ll need the corresponding views. Use the Visual Studio tooling to create two empty views as follows:
In the resulting dialog, choose strongly-typed with Product and an empty view without referencing the scripts (we do this globally already). In practice, you might choose “edit” and “create” to help jump-start the Razor code. In this example, we’ll do that from scratch so empty is what we want.
Now our links to edit and create products work. However they don’t have any content in their views. We’ll use the HTML Helper methods to convert our product into forms ready for the editing.
It all starts with Html.BeginForm(). We’ll define a form using this MVC convention and helper method as follows:
Next, we use the Model property of the view and the HTML helper methods to define the input fields. Note that we’re using Html.TextBoxFor() and Html.LabelFor() to create the fields. Our Featured property is a Boolean, so we’ll use Html.CheckBoxFor() on that one.
Once we flesh out the other properties, we’ll be finished with the create view. And it turns out the edit view is identical. There are tricks to share them across actions (e.g. PartialViews) but for our simple example, we’ll just copy / paste between the two views.
Last thing we need is a submit button to submit the form.
Now we should have a nice usable form to create products (or edit them if you copy & pasted that view). Here’s the edit view:
The final step is to capture the form data on the controller methods and update the database. This is where it gets interesting.
We will define a second method for each action (create & edit) which accept the post. We do not want our “show the form to start editing” code to mix with the “save the data and move on” code. We’ll achieve this separation using two attributes HttpGet & HttpPost.
Notice the original Create method has the GET attribute. This displays the form to begin creating a product. The second one is more interesting. It adds the product to the DB and returns to the product list. It also only accepts POST requests. Notice that it accepts a Product parameter which is populated using model binding by ASP.NET MVC.
Learn this pattern! It’s super common in MVC. I’ll call it the Get+Post+Redirect pattern.
- HttpGet method shows form, returns View().
- HttpPost method accepts the model (which is populated using model binding)
- HttpPost method then updates the data and redirects to a new view.
Edit is similar. Often, websites use AutoMapper to do the manual copy / update you see here.
Now we have a fully functioning store (albeit a simple one). One glaring omission is validation. We’ll cover that in another post.
Conclusion
To wrap up, we took a basic MVC website and went through these steps:
- Added edit and create methods
- The new methods returned the correct model
- Added strongly-typed views for each method
- Used Html.BeginForm() and related Html.XXXXFor() methods to build out the form
- Added a submit button
- Implemented the Get+Post+Redirect pattern in edit and create.
- Take the rest of the day off. :)
Good luck with your websites and happy POSTing.
– Cheers
@mkennedy
Great post. Its also very import to check to make sure that the ModelState.IsValid in the POST action method to make sure that the form validation passed.
Good point. And if I didn’t make it in the post, thanks for adding it!
— Michael
Very nice video
Thank you!
Thanks … really very useful. I hope you can show us how the code looks like when using VB.NET especially when writing the code in the view using HTML Helper Classes. I am very much confused about using this notation when passing parameters:
@Html.LabelFor(model => model.name)
and
entities.Product.SingleOrDefault(p => p.ID = id)
Appreciate it if you can explain or point me to some reference on the net when I can find more info about this syntax.
Tarek.
Great Post…
Basic things to remember before proceeding big application development
Que super !!!! thanks !!!!!
hi, great post, I am trying to do a similar one but I get a Json result: http://stackoverflow.com/q/20845411/1480877 can you help me please. thanks
hi i m new to asp.net mvc development , could you respond to this http://stackoverflow.com/questions/20953574/create-form-for-complex-type-asp-net-mvc-5
Hi, Your article is really good to understand MVC. Could you post Model of this project ?
Hi, sure, the entire source code is posted near the top.
the link to the video isn’t working …. can you update the link to point to YouTube ?
Thanks Stephen! I fixed the link.
Best ASP.NET MVC tutorials i ever came accross,thank you Michael Kennedy
Thanks so much Pavan!
HI Michael, I want to create a search page based on multiple fields (asp razor). Also the fields should get dynamically populated from the database. Can you provide any idea so that I can start on the same.
It’s tricky but you’ll have to use @foreach (…) {} to dynamically add input boxes and such. Can be done.
Be aware that Html.BeginForm() without arguments posts to the action at the route that rendered the page. If you are returning views from some other location, specifying the action and controller, e.g. Html.BeginForm(“MyAction”, “MyController”, FormMethod.Post) will always post to the same URL. Without it, the form is posted to the controller action mapped to the route that rendered the view at runtime, not the controller action the view is associated with at compile time. If /AnotherController/Wrong rendered the “MyAction” view, the form would post to /AnotherController/Wrong.