When creating a new record in the data source with a Create view, you have to implement two action methods. The first is a method using HTTP GET to render the Create view in the browser, filling select lists and other controls that need data. The second method is an HTTP POST method that receives data from the client through an HTTP POST request.
The post from the client can be done in several ways, for instance with JavaScript or a form post. In this example, you will use a form post to call back to the server when the user clicks a Submit button.
The HTTP POST action method can fetch data from several places in the posted data: the header, the query string, and the body of the request. The data is then matched against properties in a model object, which is a parameter of the action method. The action can also handle simple types such as int and string, without them being encapsulated in a model object.
There is a naming convention that you need to be aware of, to properly match posted form data with properties in model objects and other parameters. The rule states that the element names in the form data must match the property names to be matched.
The default behavior of a view using an enum is to display it as a text field. This is not the best way to display a selected item in a list of values. In this section, you will remove the Video class’s GenreId property, and add a new property of the enum type Genres called Genre. This makes it easier to work with enum data, especially when working with a SQL Server database entity model.
You will also add the enum as a property to a new view model called VideoCreateEditViewModel, which can be used both when creating a new video and when editing one.
using AspNetCore22Intro.Models;
public Genres Genre { get; set; }
new Video { Id = 1, Genre = Models.Genres.Animated, Title = "Shreck" },
Genre = video.Genre.ToString()
The complete code for the Video class, after the changes:
public class Video
{
public int Id { get; set; }
public string Title { get; set; }
public Genres Genre { get; set; }
}
The complete code for the MockVideoData constructor, after the changes:
public MockVideoData()
{
_videos = new List<Video>
{
new Video { Id = 1, Genre = Models.Genres.Animated,
Title = "Shreck" },
new Video { Id = 2, Genre = Models.Genres.Animated,
Title = "Despicable Me" },
new Video { Id = 3, Genre = Models.Genres.Animated,
Title = "Megamind" }
};
}
The complete HomeController class after the changes:
public class
HomeController : Controller
{
private readonly IVideoData _videos;
public HomeController(IVideoData videos)
{
_videos = videos;
}
public ViewResult Index()
{
var model = _videos.GetAll().Select(video =>
new VideoViewModel
{
Id = video.Id,
Title = video.Title,
Genre = video.Genre.ToString()
});
return View(model);
}
public IActionResult Details(int id)
{
var model = _videos.Get(id);
if (model == null) return RedirectToAction("Index");
return View(new VideoViewModel
{
Id = model.Id,
Title = model.Title,
Genre = model.Genre.ToString()
});
}
}
The HTTP GET Create action method renders the Create view to the browser, displaying the necessary controls to create a new video and to post the form to the server.
public IActionResult Create()
{
return View();
}
@using AspNetCore22Intro.Models
@model AspNetCore22Intro.Entities.Video
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<form asp-action="Create" method="post">
<tr>
<td><label asp-for="Title"></label></td>
<td><input asp-for="Title"/></td>
</tr>
<tr>
<td><label asp-for="Genre"></label></td>
<td><select asp-for="Genre"
asp-items="Html.GetEnumSelectList<Genres>()"></select>
</td>
</tr>
<input type="submit" value="Create" />
<a asp-action="Index">Back to List</a>
The complete code for the HTTP GET Create action:
public IActionResult Create()
{
return View();
}
The complete markup for the Create view:
@using AspNetCore22Intro.Models
@model AspNetCore22Intro.Entities.Video
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
ViewData["Title"] = "Create";
}
<h2>Create Video</h2>
<form asp-action="Create" method="post">
<table>
<tr>
<td><label asp-for="Title"></label></td>
<td><input asp-for="Title" /></td>
</tr>
<tr>
<td><label asp-for="Genre"></label></td>
<td><select asp-for="Genre"
asp-items="Html.GetEnumSelectList<Genres>()"></select>
</td>
</tr>
</table>
<input type="submit" value="Create" />
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
This view model will be used when the controller receives a post from a video’s Edit or Create view.
using AspNetCore22Intro.Models;
public Genres Genre { get; set; }
The complete code for the VideoCreateEditViewModel class:
public class VideoCreateEditViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public Genres Genre { get; set; }
}
A <form> element is used when a user should enter data in a view. There are a few steps that are performed when a user posts data. The first you already know: the user sends an HTTP request to the HTTP GET action in the controller, which fetches the necessary data after which the view is rendered.
To handle the form’s post back to the server, an HTTP POST version of the action method is called with the form values. The names of the form controls are matched against the model properties or parameters available in the action’s parameter list.
The POST action then uses that data to create, update, or delete data in the data source.
When passing data from the view to the action, MVC will, by default, match all properties in the form with properties in the model. This can be risky, especially if you use an entity class as the model. In many scenarios, you don’t want to receive all data the form sends to the action. So how do you tell MVC to use only the values of interest? You create a separate view model, like you did in the previous section.
Let’s implement the HTTP POST Create action in the HomeController class.
public IActionResult Create(VideoCreateEditViewModel model) {
return View();
}
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(VideoCreateEditViewModel model)
{
return View();
}
using AspNetCore22Intro.Entities;
var video = new Video
{
Title = model.Title,
Genre = model.Genre
};
void Add(Video newVideo);
newVideo.Id = _videos.Max(v => v.Id) + 1;
private readonly List<Video> _videos;
public void Add(Video newVideo)
{
newVideo.Id = _videos.Max(v => v.Id) + 1;
_videos.Add(newVideo);
}
_videos.Add(video);
return RedirectToAction("Details", new { id = video.Id });
services.AddSingleton<IVideoData, MockVideoData>();
The complete code for the IVideoData interface:
public interface IVideoData
{
IEnumerable<Video> GetAll();
Video Get(int id);
void Add(Video newVideo);
}
The complete code for the Add method in the MockVideoData class:
public void Add(Video newVideo)
{
newVideo.Id = _videos.Max(v => v.Id) + 1;
_videos.Add(newVideo);
}
The complete code for the Create actions:
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(VideoCreateEditViewModel model)
{
var video = new Video
{
Title = model.Title,
Genre = model.Genre
};
_videos.Add(video);
return RedirectToAction("Details", new { id = video.Id });
}
The complete code for the ConfigureServices method in the Startup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton(provider => Configuration);
services.AddSingleton<IMessageService, ConfigurationMessageService>();
services.AddSingleton<IVideoData, MockVideoData>();
}
Join our mailing list to receive the latest news and updates from our team.
Don't worry, your information will not be shared.
50% Complete
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.