Please bear with my noobness, I'm super new to the MVC pattern.
What I'm trying to do
I am building a profile information page for registered users on my site. This page would list data about the user, such as date of birth, telephone number, subscription status, etc.. You get the idea. I would also like to have a form to let users change their password, email address, personal information on the same page.
My problem
The user's data comes from my controller via a passed model variable:
public ActionResult Profil() { var model = db.Users.First(e => e.UserName == WebSecurity.CurrentUserName); return View(model); }
The output looks like this in my view:
<label>Phone number: </label> @if (Model.PhoneNumber != null) { @Model.PhoneNumber } else { <span class="red">You haven't set up your phone number yet. </span> }
The form in which the user could change his info would use another model, ProfileModel. So basiccaly I need to use two models in my view, one for outputting information and one for posting data. I thought that using a partial view I can achieve this, but I get this error:
The model item passed into the dictionary is of type 'Applicense.Models.User', but this dictionary requires a model item of type 'Applicense.Models.ProfileModel'.
Here's what my call to the partial view looks like:
@using (Html.BeginForm()) { @Html.AntiForgeryToken() @Html.ValidationSummary() @Html.Partial("_ModifyProfileInfo") }
Here's the partial view:
@model Applicense.Models.ProfileModel <ul> <li> @Html.LabelFor(m => m.Email) @Html.EditorFor(m => m.Email) </li> <li> @Html.LabelFor(m => m.ConfirmEmail) @Html.EditorFor(m => m.ConfirmEmail) </li> <input type="submit" value="Update e-mail" /> </ul>
And finally here's my ProfileModel:
public class ProfileModel { [Required] [DataType(DataType.EmailAddress)] [Display(Name = "New e-mail address")] public string Email { get; set; } [DataType(DataType.EmailAddress)] [Display(Name = "Confirm new e-mail address")] [Compare("Email", ErrorMessage = "The e-mail and it's confirmation field do not match.")] public string ConfirmEmail { get; set; } }
Am I missing something? What's the proper way to do this?
Edit: I remade my code reflecting Nikola Mitev's answer, but now I have another problem. Here's the error I get:
Object reference not set to an instance of an object. (@Model.UserObject.LastName)
This only occurs when I'm posting the changed e-mail address values. Here's my ViewModel (ProfileModel.cs):
public class ProfileModel { public User UserObject { get; set; } [Required] [DataType(DataType.EmailAddress)] [Display(Name = "Új e-mail cím")] public string Email { get; set; } [DataType(DataType.EmailAddress)] [Display(Name = "Új e-mail cím megerősítése")] [Compare("Email", ErrorMessage = "A két e-mail cím nem egyezik.")] public string ConfirmEmail { get; set; } [DataType(DataType.EmailAddress)] [Display(Name= "E-mail cím")] public string ReferEmail { get; set; } }
Controller:
public ActionResult Profil() { var User = db.Users.First(e => e.UserName == WebSecurity.CurrentUserName); var ProfileViewModel = new ProfileModel { UserObject = User }; return View(ProfileViewModel); }
And finally here's my user.cs
model class:
[Table("UserProfile")] public class User { [Key] [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] public int UserId { get; set; } [Column("UserName")] public string UserName { get; set; } [Column("Email")] [Required] public string Email { get; set; } [Column("FirstName")] public string FirstName { get; set; } [Column("LastName")] public string LastName { get; set; } [Column("PhoneNumber")] public string PhoneNumber { get; set; } ... You get the idea of the rest...
I'm thinking it's happening because the model is trying to put data in each required
columns into the database.
Edit2: The httppost method of my Profil action:
[HttpPost] [Authorize] [ValidateAntiForgeryToken] public ActionResult Profil(ProfileModel model) { if (ModelState.IsValid) { //insert into database return Content("everything's good"); } else { //outputs form errors return View(model); } }
Partial("nameOfPartial", Model) giving a model like that does not work. it still clashes with the model in the main view. the partial view contains a form, will the renderaction method get the post when i am using that? well to save the entry you need to have a post method in your partial view.
To create a partial view, right click on Shared folder -> select Add -> click on View.. Note: If the partial view will be shared with multiple views, then create it in the Shared folder; otherwise you can create the partial view in the same folder where it is going to be used.
In MVC we cannot pass multiple models from a controller to the single view.
You can use multiple models in a single view by creating a common model for all the models that are to be used in a single view. To achieve this, refer to the following steps. First, create a new model (common for all models) and refer all other models that are to be used in the same view.
The best way to handle this situation is to use and pass viewModel to your Profile controller, viewModel is wrapper class for multiple objects that you want to pass to your view.
public class ProfileUserViewModel { public ProfileModel ProfileModelObject {get; set;} public UserModel UserModelObject {get; set;} }
Your controller should look like:
public ActionResult Profil() { var profileModel = db.Users.First(e => e.UserName == WebSecurity.CurrentUserName); var userModel = //fetch from db. var pmViewModel = new ProfileUserViewModel { ProfileModelObject = profileModel, UserModelObject = userModel }; return View(pmViewModel); }
And finally your view :
@model Applicense.Models.ProfileUserViewModel <label>Phone number: </label> @if (Model.ProfileModelObject.PhoneNumber != null) { @Model.PhoneNumber } else { <span class="red">You haven't set up your phone number yet. </span> }
There is an overload of @Html.Partial
which allows you to send ViewData
as defined in your controller - this is the method I generally use for partial views. In your controller define ViewData["mypartialdata"]
as ViewDataDictionary
. Then in your view
@Html.Partial("_ModifyProfileInfo",ViewData["mypartialdata"])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With