I have users fill out their profile information somewhere on my site using the AspNetSqlProfileProvider that came with the IDE.
I allow my users to edit their profile at any time by going to the edit profile page where the form to edit their profile auto-populates. I currently do this by using ViewBag to send each individual part of a user's profile from my controller to the view (address and city for example):
Controller get:
ViewBag.address = CustomProfile.GetUserProfile(User.Identity.Name).Address;
ViewBag.city = CustomProfile.GetUserProfile(User.Identity.Name).City;
View:
<div class="editor-field">
@Html.TextBox("Address", (string)ViewBag.address)
</div>
<div class="editor-field">
@Html.TextBox("City", (string)ViewBag.city)
</div>
Controller post:
public ActionResult ChangeProfile( FormCollection favorites )
{
CustomProfile profile = CustomProfile.GetUserProfile();
profile.Address = favorites["Address"];
profile.City = favorites["City"];
profile.Save();
return RedirectToAction("Profile");
}
The above works fine for editing profiles, it is very smooth from my user's perspective (although, from reading questions/answers online it appears I should be using a ViewModel, not exactly sure how to make the transition - have been failing).
When my user's go to 'checkout' from the website (they've been shopping), they are presented with a screen that allows them to input final information that allows them to make their purchase. Let's call this the, "Order Page." These details include name, address, credit card info, etc. Some of these pieces of information are the same as the information within the profile page of the website.
I want to be able to auto-populate some of the details in this Order Page (name, address) while leaving some of the fields blank (credit card - so the user has to fill these details in each time they visit the order page).
The way the Order page works is as the MVC Store Tutorial describes. When the Order Page is displayed it displays the form using:
@Html.EditorForModel()
Which is great and allows you to edit the details specified within the Order model and will allow for the data verification stuff (name is required, address is required, credit card has to be numbers, email is properly formatted, etc), but I cannot figure out how to populate specific fields within this Order Page with details from my user's profile.
I have attempted building a new ViewModel that uses just the information contained within my profile, but I do not have a clear grasp of what is needed because I am not getting the end-result I am desiring.
I have considered using just the Order Model as my Profile information, but that is not sufficient because I want to have different information available in both.
I have considered using the Profile information as the Order Model, but I want my users to have the flexibility to store profile information separately from whatever information they use to actually place orders.
I think what I need to know specifically to fix my problem is how do I auto-populate specific fields while using "@Html.EditorForModel()" ?
Any other help with my overall situation would be great, I am very open to any advice that would simplify my flow (it feels like I am making it harder on myself than I need to, and I am at a point where collaboration would feel like a breath of fresh air).
I was able to follow only the first half of your question. It's the one in which you have showed some code and the one with which I can help you. The second half was a complete mist for me.
So when you want to design a view think of what fields this view needs to display/edit. And design a view model for it:
public class ChangeProfileViewModel
{
[Required]
public string Address { get; set; }
[Required]
public string City { get; set; }
}
and then have your GET controller action that is supposed to render this view populate the view model:
public ActionResult ChangeProfile()
{
CustomProfile profile = CustomProfile.GetUserProfile(User.Identity.Name);
ChangeProfileViewModel model = new ChangeProfileViewModel
{
Address = profile.Address,
City = profile.City
};
return View(model);
}
then you design the corresponding view which will be strongly typed to this view model:
@model ChangeProfileViewModel
@using (Html.BeginForm())
{
@Html.EditorForModel()
<button type="submit">OK</button>
}
and finally you have a POST controller action that will handle the submission of this form:
[HttpPost]
public ActionResult ChangeProfile(ChangeProfileViewModel model)
{
if (!Model.IsValid)
{
// there were validation errors => redisplay the view
return View(model);
}
// validation succeeded => process the results
CustomProfile profile = CustomProfile.GetUserProfile();
profile.Address = model.Address;
profile.City = model.City;
profile.Save();
return RedirectToAction("Profile");
}
Now what we observe here is that in both our GET and POST actions we have repetitive mapping code between our domain model (CustomProfile
) and our view model (ChangeProfileViewModel
). To solve this issue I may recommend you using AutoMapper. It could simplify your GET action to:
public ActionResult ChangeProfile()
{
CustomProfile profile = CustomProfile.GetUserProfile(User.Identity.Name);
ChangeProfileViewModel model = Mapper.Map<CustomProfile, ChangeProfileViewModel>(profile);
return View(model);
}
or with a custom action filter to even:
[AutoMap(typeof(CustomProfile), typeof(ChangeProfileViewModel))]
public ActionResult ChangeProfile()
{
CustomProfile profile = CustomProfile.GetUserProfile(User.Identity.Name);
return View(profile);
}
and your POST action to:
[HttpPost]
public ActionResult ChangeProfile(ChangeProfileViewModel model)
{
if (!Model.IsValid)
{
// there were validation errors => redisplay the view
return View(model);
}
// validation succeeded => process the results
CustomProfile profile = CustomProfile.GetUserProfile();
Mapper.Map<ChangeProfileViewModel, CustomProfile>(model, profile);
profile.Save();
return RedirectToAction("Profile");
}
What's important to know about view models is that you should always use them for each view and design them in such a way so that they contain only the specific information that this view needs to handle. Leave to the mapping layer handle the conversion between your domain entities and your view models.