The argument for using custom view models is that they can be tailored precisely to each strongly typed view, providing a cleaner approach for display and updating data from views. The downside though is that there is some additional work involved. However, after further reading on the subject - would recommend both Steve Michelotti's post on MVC view model patterns and Jorit Salverda's maintainable MVC series - on balance now I've been using them more and more on projects, it seems the extra steps are worthwhile.
To illustrate this approach let's take again a simple example, where we have a domain model that represents a user. Each user has a user name and a role. The application using them needs to present a form for the update of a user's details, with the option to send them a welcome email.
The following code illustrates the code for the domain model.
1 public class User
2 {
3 public string Username { get; set; }
4 public Role Role { get; set; }
5 }
And then I've created two further models in the UI layer - a view model that contains all the data for presentation on the form (the user object itself, and a SelectList for the various role options the administrator of the application can choose from. The second form model is created via model binding following the form post, where the user object is extracted and updated, and additional information also passed in a strongly typed manner is processed.
1 public class UserViewModel
2 {
3 public User User { get; set; }
4 public SelectList Roles { get; set; }
5 }
6
7 public class UserFormModel
8 {
9 public User User { get; set; }
10 public bool SendEmail { get; set; }
11 }
The controller code illustrates how the view models are created and information from the form model extracted.
1 public ActionResult Add()
2 {
3 return View(new UserViewModel
4 {
5 Roles = new SelectList(usersService.GetRoleList(), "Id", "Name"),
6 });
7 }
8
9 public ActionResult Edit(int id)
10 {
11 User user = usersService.GetUserById(id);
12 return View(new UserViewModel
13 {
14 User = user,
15 Roles = new SelectList(usersService.GetRoleList(), "Id", "Name", user.Role.Id),
16 });
17 }
18
19 [AcceptVerbs(HttpVerbs.Post)]
20 public ActionResult Edit(UserFormModel userFormModel)
21 {
22 if (ModelState.IsValid)
23 {
24 try
25 {
26 usersService.SaveUser(userFormModel.User, userFormModel.ResetPassword, GetUser());
27 }
28 catch (RuleException ex)
29 {
30 ex.CopyToModelState(ModelState);
31 }
32 }
33 if (ModelState.IsValid)
34 {
35 TempData["Message"] = "User account " + (userFormModel.User.Id == 0 ? "created" : "updated") + ".";
36 return RedirectToAction("Index");
37 }
38 else
39 {
40 return View((userFormModel.User.Id == 0 ? "Add" : "Edit"), new UserViewModel
41 {
42 User = userFormModel.User,
43 Roles = new SelectList(usersService.GetRoleList(), "Id", "Name", userFormModel.User.Role.Id),
44 });
45 }
46 }
And finally this strongly typed view code demonstrates how the form fields are named to support model binding following the post.
1 <% using (Html.BeginForm()) {%>
2
3 <fieldset>
4 <h2>Account Details</h2>
5 <p>
6 <label for="User.Username">User name:</label>
7 <span class="field">
8 <%= Html.TextBox("User.Username", Model.User.Username)%>
9 </span>
10 </p>
11 <p>
12 <label for="User.Role.Id">Role:</label>
13 <span class="field">
14 <%= Html.DropDownList("User.Role.Id", Model.Roles)%>
15 </span>
16 </p>
17 <p>
18 <label for="SendEmail">Reset Password:</label>
19 <span class="field">
20 <%= Html.CheckBox("SendEmail", false)%>
21 </span>
22 </p>
23 <p>
24 <input type="submit" value="Save" class="indent" />
25 </p>
26 <%= Html.Hidden("User.Id",Model.User.Id) %>
27 </fieldset>
28
29 <% } %>
Without this approach it would be necesssary to pass the additional data to the view - the list of roles - in a loosely typed manner in the ViewData. And the additional information required from the form post - the flag to send the welcome email - would need to be passed as an extra paramenter to the controller action responding to the form post.
With hindsight I think this is clearly a cleaner approach and am using it for the MVC projects I'm working on now. In particular the method of creating a custom class for both the form display and the form post, provides a very elegent approach in my opinion.
great
ReplyDelete