Page to create object and sub object - asp.net-mvc-3

I'm sure there have been tons of people asking this type of question but I can't quite figure out how to word it.
I will try to explain. I am working to model an ethernet network where devices have ip addresses. I've setup my entity framework models so that the ip and subnet are stored in a separate table to ensure uniqueness across the system.
I'd like the user to be able to create a device and its associated IP at the same time if the IP they want is not already in a dropdown list.
I setip a partial of the IP Address page RenderPartial on the device page and I get this error:
Here is the question, How do I fix this error:
The model item passed into the dictionary is of type PcnWeb.Models.Device, but this dictionary requires a model item of type PcnWeb.Models.IPAddress.
Here are my models:
IP Address Model:
namespace PcnWeb.Models
{
public class IPAddress
{
public virtual ICollection<Device> Devices { get; set; }
[Key]
public int ipAddressRecId { get; set; }
public Nullable<int> ipOctet1 { get; set; }
public Nullable<int> ipOctet2 { get; set; }
public Nullable<int> ipOctet3 { get; set; }
public Nullable<int> ipOctet4 { get; set; }
public Nullable<int> smOctet1 { get; set; }
public Nullable<int> smOctet2 { get; set; }
public Nullable<int> smOctet3 { get; set; }
public Nullable<int> smOctet4 { get; set; }
}
}
And the Device Model:
namespace PcnWeb.Models
{
public class Device
{
[Key]
public int deviceRecId { get; set; }
public int ipAddressRecId { get; set; }
[Required, StringLength(64)]
[Unique]
public string Name { get; set; }
[StringLength(256)]
public string Comment { get; set; }
public virtual IPAddress IPAddress { get; set; }
}
}
I would have thought that it would be pretty easy to have the associated device creation page with an inline ipaddress creation page.
Here's the Device page:
#model PcnWeb.Models.Device
#{
ViewBag.Title = "Create a Device";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Device</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ipAddressRecId, "IPAddress")
</div>
<div class="editor-field">
#Html.DropDownList("ipAddressRecId", String.Empty)
#Html.ValidationMessageFor(model => model.ipAddressRecId)
</div>
#{
Html.RenderPartial("~/Views/IP_Address/_Create.cshtml");
}
<div class="editor-label">
#Html.LabelFor(model => model.Name, "Name")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Comment, "Comment")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Comment)
#Html.ValidationMessageFor(model => model.Comment)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Here is the IP Address Partial:
EDIT: Sorry I forgot to include this
#model PcnWeb.Models.IPAddress
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>IPAddress</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ipOctet1, "ipOctet1")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ipOctet1)
#Html.ValidationMessageFor(model => model.ipOctet1)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ipOctet2, "ipOctet2")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ipOctet2)
#Html.ValidationMessageFor(model => model.ipOctet2)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ipOctet3, "ipOctet3")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ipOctet3)
#Html.ValidationMessageFor(model => model.ipOctet3)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ipOctet4, "ipOctet4")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ipOctet4)
#Html.ValidationMessageFor(model => model.ipOctet4)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.smOctet1, "smOctet1")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.smOctet1)
#Html.ValidationMessageFor(model => model.smOctet1)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.smOctet2, "smOctet2")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.smOctet2)
#Html.ValidationMessageFor(model => model.smOctet2)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.smOctet3, "smOctet3")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.smOctet3)
#Html.ValidationMessageFor(model => model.smOctet3)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.smOctet4, "smOctet4")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.smOctet4)
#Html.ValidationMessageFor(model => model.smOctet4)
</div>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
So from all this, it looks great to me, the validation works client side. I'll have to write some javascript to hide the IP address partial unless they select new from the dropdown list.
Here is the question again, How do I fix this error:
The model item passed into the dictionary is of type PcnWeb.Models.Device, but this dictionary requires a model item of type PcnWeb.Models.IPAddress.

This errors means that there is mismatch between model type in your partial view and type of model passed to this view. But as i can see you trying to render the partial view without model passing.
Your code example worked for me.
So, to solve this problem you can do the next steps.
Ensure that the view path is right;).
Try so 'send' model to your partial view explicity like
main view
#model PcnWeb.Models.Device
//some code here
#Html.Partial("path_to_view", Model)
partial view.
#model PcnWeb.Models.Device
#Html.DropdownListFor(x=>x.ipAddressRecId, YourDlistSource) //or anything you need
this works for me.
Alternatively if you need edit submodel in partial view you can do this
#model PcnWeb.Models.Device
//some code here
#Html.Partial("path_to_view", Model.IPAddress)//pass the submodel to partial view
then your partial view must be with another type.
#model PcnWeb.Models.IPAddress
//some code here
Answer on comment. To resolve object reference exception try initialize your submodel in constructor.
public class Device
{
///properties
public Device()
{
IPAddress = new IPAddress();
}
}

Related

updating relational database in asp .net mvc

I am developing a simple blog application to teach myself C# and asp .net mvc3.
I am stuck at a stage where I need to update comments to a post.
Comment class in my code is as follows:
public class Comment
{
public int CommentID { get; set; }
public string Name { get; set; }
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[DataType(DataType.MultilineText)]
public string CommentBody { get; set; }
public int BlogID { get; set; }
public virtual Blog Blog { get; set; }
}
I have a comment form on the blog details page which takes the name, email and comment. The code is as follow:
<div class="editor-label">
#Html.LabelFor(model => model.Comment.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Comment.Name)
#Html.ValidationMessageFor(model => model.Comment.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Comment.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Comment.Email)
#Html.ValidationMessageFor(model => model.Comment.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Comment.CommentBody)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Comment.CommentBody)
#Html.ValidationMessageFor(model => model.Comment.CommentBody)
</div>
<p>
<input type="submit" value="Add Comment" />
</p>
I am not sure how to pass the blogid with this so that the comment gets updated with the correct blogid.
thanks.
You could use a hidden field inside the form:
#Html.HiddenFor(x => x.Comment.BlogID)
#Html.HiddenFor(model => model.Comment.BlogID)

Partialview not returning model for a sortedlist

I am very, very new to MVC, so please bear with my question, I have to work with the following structure.
I have the following models, Facility and Address. Facility contains a SortedList of Address, I have reduced the number of properties for clarity,
public class AppFacility
{
public AppFacility()
{
this.Addresses = new SortedList<string, AppAddress>();
}
public SortedList<string, AppAddress> Addresses { get; set; }
[DisplayNameAttribute("Facility ID")]
public int FacilityID { get; set; }
[Required(ErrorMessage = "Facility Name is a required field")]
[DisplayNameAttribute("Facility Name")]
public string FacilityName { get; set; }
[DisplayNameAttribute("Doing Business As")]
public string Dba { get; set; }
[DisplayNameAttribute("Nbr. Of Employees")]
public int NbrOfEmployees { get; set; }
}
public class AppAddress
{
[DisplayNameAttribute("City")]
public string City { get; set; }
[DisplayNameAttribute("State")]
public string State { get; set; }
[DisplayNameAttribute("Street Name")]
public string StreetName { get; set; }
}
Controller:
[HttpPost]
public ActionResult FacilityCreate(AppFacility objFacility)
{
facilityManager = new Manager.AppFacilityManager();
if (facilityManager.InsertAppFacility(objFacility))
{
return RedirectToAction("FacilityInfo", new { id = objFacility.FacilityID });
}
return View((AppFacility)Session["FacilityObject"]);
}
View:
FacilityCreate
#model Model.CORE.BO.AppFacility
<table width="100%" align="center" border="0" class="SectionTables">
<tr>
<td class="SubtitleHeader">
Facility
</td>
</tr>
<tr>
<td class="SubtitleHeader1">
Enter Facility Information
</td>
</tr>
<tr>
<td align="center">
#Html.Partial(p_CreateEditAppFacility, Model)
</td>
</tr>
Partial view:
p_CreateEditAppFacility:
This contains the p_CreateEditAddress partial view as well.
#model Model.CORE.BO.AppFacility
#using (Html.BeginForm("FacilityCreate", "Form", FormMethod.Post,
new { id = "saveForm", name = "saveForm" }))
{
#Html.ValidationSummary(true)
<div class="main">
<fieldset>
<legend>Operator Information</legend>
<div class="editor-label">
#Html.Label("Facility Name (Business Name of Operator to Appear): ")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FacilityName)
#Html.ValidationMessageFor(model => model.FacilityName)
</div>
<div class="editor-label">
#Html.Label("Owner's Business Name (If different from Business Name of Operator): ")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Dba)
#Html.ValidationMessageFor(model => model.Dba)
</div>
<div class="editor-label">
#Html.Label("No of Employees:")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.NbrOfEmployees)
#Html.ValidationMessageFor(model => model.NbrOfEmployees)
</div>
</fieldset>
<fieldset>
<legend>Address Information</legend>
#{
int i = 0;
foreach (KeyValuePair<string, Model.CORE.BO.AppAddress> addressRow in Model.Addresses)
{
<div class="editor-field">
#Html.Partial(p_CreateEditAddress, addressRow.Value, new ViewDataDictionary(Html.ViewDataContainer.ViewData) { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = string.Format("objFacility.Addresses[{0}]", i) } })
</div>
i++;
}
}
</fieldset>
<p>
<input id="SaveFacility" name="SaveInfo" type="submit" value="Save Information" />
</p>
</div>
}
PartialView:
p_CreateEditAddress
#model Model.CORE.BO.AppAddress
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.StreetName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.StreetName)
#Html.ValidationMessageFor(model => model.StreetName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.City)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.City)
#Html.ValidationMessageFor(model => model.City)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.State)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.State)
#Html.ValidationMessageFor(model => model.State)
</div>
My question is that in the Controller the objFacility.Addresses does not get the values entered for the model AppAddress, it is always null. The AppFacility gets populated though.
The html behind looks like this for p_CreateEditAddress
<div class="editor-field">
<input class="text-box single-line"
id="objFacility_Addresses_0__StreetName"
name="objFacility.Addresses[0].StreetName" type="text" value="" />
<span class="field-validation-valid"
data-valmsg-for="objFacility.Addresses[0].StreetName" data-valmsg-replace="true"></span>
</div>
Please help.
You just need to change your Partial View call to take the correct prefix. You don't give it the Model name as the default model binder will be creating an instance of your class and looking for fields that match the names of the items in the Request.Form collection. It knows nothing about the variable name you've gave your model, just the properties of your Model's class.
Try this (I put line breaks in for readability):
#Html.Partial(p_CreateEditAddress, addressRow.Value,
new ViewDataDictionary(Html.ViewDataContainer.ViewData) {
TemplateInfo = new System.Web.Mvc.TemplateInfo {
HtmlFieldPrefix = string.Format("Addresses[{0}]", i)
}
})
I guess the generated HTML is not proper for model binding.
It should not be objFacility.Addresses[0].StreetName but Addresses[0].StreetName.

Asp.net MVC does not display the template editing

I have the classes:
public class PersonDetailsModel
{
public string FistName
{
get;
set;
}
public string LastName
{
get;
set;
}
public string Email
{
get;
set;
}
}
public class RegisterCoupleModel
{
public PersonDetailsModel Groom
{
get;
set;
}
public PersonDetailsModel Bride
{
get;
set;
}
public string UrlKeyword
{
get;
set;
}
public string ReCaptcha
{
get;
set;
}
}
folder Shared > EditorTemplates
PersonDetailsModel.cshtml
#model BindSolution.AndMarried.ViewModel.PersonDetailsModel
<div class="editor-label">
#Html.LabelFor(m => m.FistName)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.FistName)
#Html.ValidationMessageFor(m => m.FistName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.LastName)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.LastName)
#Html.ValidationMessageFor(m => m.LastName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Email)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.Email)
#Html.ValidationMessageFor(m => m.Email)
</div>
In my View:
#Html.EditorForModel()
Only the UrlKeyword and ReCapcha fields are displayed!
Why Asp.net MVC not use templantes in shared folder to display my nested type PersonDetailsModel ?
You know, you could have just edited your other question. :)
Anyway, i don't think that will work. You either need to control the entire template yourself, or let MVC do it all itself. (i could be wrong)
Create an editor template for RegisterCoupleModel:
#model BindSolution.AndMarried.ViewModel.RegisterCoupleModel
#Html.EditorFor(model => model.Groom)
#Html.EditorFor(model => model.Bride)
#Html.EditorFor(model => model.UrlKeyword)
#Html.EditorFor(model => model.ReCapcha)
Or you could use a [UIHint("PersonDetailsModel"] attribute on your Groom and Bride properties in your ViewModel.
Read up on the remarks section in EditorForModel for how this all works.

How would my model be in MVC3 if I needed to reference a foreign key, for example CountryID?

Here's what I have so far Model and View:
public class RegisterModel
{
[Required]
[Display(Name = "Usuario")]
public string UserName { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Correo")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Contraseña")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirme Su Contraseña")]
[Compare("Password", ErrorMessage = "Sus contraseñas no son las mismas.")]
public string ConfirmPassword { get; set; }
[Required]
[Display(Name = "Direccion")]
public string Address { get; set; }
[Required]
[Display(Name = "Telefono Fijo")]
public string Telephone { get; set; }
[Required]
[Display(Name = "Celular")]
public string MobilePhone { get; set; }
[Required]
[Display(Name = "Fecha de Nacimiento")]
public DateTime DateOfBirth { get; set; }
[Required]
[Display(Name = "Sexo")]
public bool Sex { get; set; }
[Required]
[Display(Name = "Pais")]
public int Country { get; set; }
}
#model Foo.WebUI.Models.RegisterModel
#{
ViewBag.Title = "Register";
}
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<div id="registerform">
<h2>Cree Su Cuenta</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<h3>Informacion de Usuario</h3>
<div class="editor-field">
#Html.LabelFor(model => model.UserName)
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.Email)
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.Password)
#Html.EditorFor(model => model.Password)
#Html.ValidationMessageFor(model => model.Password)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.ConfirmPassword)
#Html.EditorFor(model => model.ConfirmPassword)
#Html.ValidationMessageFor(model => model.ConfirmPassword)
</div>
<h3>Informacion Personal</h3>
<div class="editor-field">
#Html.LabelFor(model => model.Address)
#Html.EditorFor(model => model.Address)
#Html.ValidationMessageFor(model => model.Address)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.Telephone)
#Html.EditorFor(model => model.Telephone)
#Html.ValidationMessageFor(model => model.Telephone)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.MobilePhone)
#Html.EditorFor(model => model.MobilePhone)
#Html.ValidationMessageFor(model => model.MobilePhone)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.DateOfBirth)
#Html.EditorFor(model => model.DateOfBirth)
#Html.ValidationMessageFor(model => model.DateOfBirth)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.Sex)
#Html.EditorFor(model => model.Sex)
#Html.ValidationMessageFor(model => model.Sex)
</div>
<div class="editor-field">
#Html.LabelFor(model => model.Country)
#Html.EditorFor(model => model.Country)
#Html.ValidationMessageFor(model => model.Country)
</div>
<p>
<input type="submit" value="Create" />
</p>
}
</div>
Now, since my User will belong to a single country, in the database it has a foreign key reference to the Country table. In my model, I set the Country property to be of type int. Is this correct?
How would I correctly set this up so a dropdownlist is shown for choosing a country in the View but a numerical value is saved for persistance to the database?
Your CountryId can remain an int (or any type you use for your ids). The Select List can be initialized with the appropriate value field for the selection, and the type will match without the need for conversion.
Your model will have to be part of a viewmodel that provides to the view what is needed to make the drop down list working (mostly a SelectList ).
public class RegisterViewModel
{
public RegisterModel register { get; set; }
public SelectList SelectCountriesList { get; set; }
}
The initilisation of this ViewModel will depend on how you work in your controller.

How do I bind relational data to a model in ASP.net MVC?

I am trying to make an editor for an object in ASP.net MVC 3. It looks something like this:
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.foo)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.foo)
#Html.ValidationMessageFor(model => model.foo)
</div>
#if (Model.Items.Count > 0)
{
<table>
#foreach (var ii in Model.Items)
{ #Html.EditorFor(item => ii) }
</table>
}
In this example, Items is a list of another kind of object. The problem is, when the model is posted back from being edited in the view, the data changes to Model.Items aren't there, while the data changes to Name and foo work. How can I make it so that the data for Items binds correctly?
Model class:
public class HomeControllerModel
{
public string Name { get; set; }
public string foo { get; set; }
public List<string> Items { get; set; }
public HomeControllerModel()
{
Items = new List<string>();
}
}
Controller class:
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
var model = new HomeControllerModel();
model.Name = "LukLed";
model.foo = "bar";
model.Items.Add("AAA");
model.Items.Add("BBB");
model.Items.Add("CCC");
return View(model);
}
[HttpPost]
public ActionResult Index(HomeControllerModel model)
{
return View(model);
}
View:
#using MvcApplication4.Controllers
#model HomeControllerModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>
<form action="/Home/Index" method="post">
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.foo)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.foo)
#Html.ValidationMessageFor(model => model.foo)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Items)
</div>
<input type="submit" value="Submit" />
</form>
</div>
</body>
</html>
You don't have to iterate through Items.

Resources