In a C# MVC WebApp, I have a CallDetailViewModel that contains a list of CallerViewModels and it has a List of PhoneNumberViewModels. I'm trying to link them all together properly.
Not shown here, but I am also trying to both load existing values and add new/remove values, so I don't know what's being sent to the controller ahead of time.
I've tried following this 2012 guide that has a very similar problem I found online, but no luck yet: Code Project Article
I also tried moving the List of PhoneNumberViewModels to the CallDetailViewModel, and while I was able to pass the phone numbers to my controller I didn't have a clear way to link them to the appropriate CallerViewModel.
I want to be able to add and remove PhoneNumbers from Callers and Callers from the CallDetail.
I've removed my buttons and AJAX regarding those for now, as it's not my main problem.
Here are my simplified ViewModels and Views:
ViewModels
CallDetailViewModel.cs
namespace PROJECT_NAME.ViewModels
{
public class CallDetailsViewModel
{
public Guid Id { get; set; }
public string EnteredByEmail { get; set; }
public List<CallerViewModel> CallerViewModels { get; set; }
}
}
CallerViewModel.cs
namespace PROJECT_NAME.ViewModels
{
public class CallerViewModel
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public List<PhoneNumberViewModel> PhoneNumberViewModels { get; set; }
}
}
PhoneNumberViewModel.cs
namespace PROJECT_NAME.ViewModels
{
public class PhoneNumberViewModel
{
public Guid Id { get; set; }
public string Number { get; set; }
}
}
Views
CallDetail.cshtml
#using PROJECT_NAME.ViewModels
#model CallDetailsViewModel
<div class="container">
#using (Html.BeginForm("SubmitCallDetails", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Id)
<div class="well">
#* Call Details *#
<div class="row">
<fieldset">
<legend>Call Details</legend>
</fieldset>
<div class="form-group">
#Html.LabelFor(m => m.EnteredByEmail, new {#class = "control-label"})
#Html.TextBoxFor(m => m.EnteredByEmail, new {#class = "form-control"})
</div>
</div>
#* Caller Details *#
<div class="row">
<fieldset>
<legend>Callers</legend>
</fieldset>
</div>
#* Render each existing caller. Each caller gets it's own well to create a visual seperation between them. *#
#if (Model.CallerViewModels.Count == 0)
{
<div class="well">
#{ Html.RenderPartial("_PartialCallerInfo", new CallerViewModel());}
</div>
}
#foreach (var callerViewModel in Model.CallerViewModels)
{
<div class="well">
#{ Html.RenderPartial("_PartialCallerInfo", callerViewModel); }
</div>
}
</div>
<div class="row">
<div class="form-group">
<button class="btn btn-danger" type="reset">Reset</button>
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</div>
}
</div>
_PartialCallerInfo.cshtml
#using PROJECT_NAME.ViewModels
#model CallerViewModel
#using (Html.BeginCollectionItem("CallerViewModels"))
{
<div class="row">
#Html.HiddenFor(m => m.Id)
<div class="form-group">
#Html.LabelFor(m => m.FirstName, new { #class = "control-label" })
#Html.TextBoxFor(m => m.FirstName, new { #class = "form-control"})
</div>
</div>
#if (Model.PhoneNumberViewModels.Count == 0)
{
#{ Html.RenderPartial("_PartialCallerPhoneNumber", new PhoneNumberViewModel());}
}
#foreach (var phoneNumberViewModel in Model.PhoneNumberViewModels)
{
#{ Html.RenderPartial("_PartialCallerPhoneNumber", phoneNumberViewModel); }
}
}
_PartialCallerPhoneNumber.cshtml
#using PROJECT_NAME.ViewModels
#model PhoneNumberViewModel
#using (Html.BeginCollectionItem("PhoneNumberViewModels"))
{
<div class="row">
#Html.HiddenFor(m => m.Id)
<div class="form-group">
#Html.LabelFor(m => m.Number, new { #class = "control-label" })
#Html.TextBoxFor(m => m.Number, new { #class = "form-control"})
</div>
</div>
}
Related
The data entered into second partial view not come into model but first partial view data come. I want both partial view data into model. Please guide me how to do it as i am new in .net core.
controller
[HttpPost]
public ActionResult NewIndividualSearchCharacteristic(ABC.Core.Models.DTOs.Characteristic characteristics)
{
return PartialView("IndividualSearchCharacterisiticPartial", characteristics ?? new ABC.Core.Models.DTOs.Characteristic());
}
First partial view having second partial view
#model ABC.Core.Models.DTOs.Individual.IndividualSearch
#using (Html.BeginCollectionItem("IndividualSearches"))
{<div id="Characteristics" class="mb-3">
#if (Model?.Characteristics != null)
{
for (var i = 0; i < Model?.Characteristics.Count; i++)
{
<div class="form-group">
#{ Html.RenderPartial("IndividualSearchCharacterisiticPartial", Model.Characteristics[i], new ViewDataDictionary()); }
</div>
}
}
IndividualSearch Class
namespace ABC.Core.Models.DTOs.Individual
{
public List<Characteristic> Characteristics { get; set; } = new List<Characteristic>();
Model
namespace ABC.Core.Models.DTOs
{
public class Characteristic
{
public string Name { get; set; }
public string Value { get; set; }
}
}
second partial
#model ABC.Core.Models.DTOs.Characteristic
#using (Html.BeginCollectionItem("Characteristics"))
{
<div id="characteristic-details" class="card">
<div class="form-horizontal">
<div class="card-block">
<div class="container">
<div class="row">
<div class="col-*-*">
#Html.LabelFor(m => m.Name, "Name", new { #class = "form-control-label" })
</div>
<div class="col">
#Html.TextBoxFor(m => m.Name, null, new { #class = "form-control" })
</div>
Service class
individualSearch.Characteristics = requestTestData.individualCharacteristics;
To show data by two related partial view, you should confirm which is the first partial to show first.
According to your code, the IndividualSearchPartial partial view called IndividualSearchCharacterisiticPartial partial view, therefor, you should call IndividualSearchPartial partial view first in NewIndividualSearchCharacteristic action which will called IndividualSearchCharacterisiticPartial partial view automatically.
And in NewIndividualSearchCharacteristic action, you should return IndividualSearchCharacterisiticPartial partial view with IndividualSearch model data.
Here is an example based on your code:
Test view:
#{
ViewData["Title"] = "Test";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Test</h1>
<input id="Button1" type="button" value="button" />
<div id="aa"></div>
#section Scripts{
<script>
$("#Button1").click(function () {
$.ajax({
url: "/home/NewIndividualSearchCharacteristic",
type: "post",
success: function (data) {
$("#aa").html(data);
}
})
})
</script>
}
NewIndividualSearchCharacteristic action:
[HttpPost]
public ActionResult NewIndividualSearchCharacteristic()
{
IndividualSearch individualSearch = new IndividualSearch()
{
Characteristics = new List<Characteristic>() {
new Characteristic(){ Name="aa", Value="gfd"},
new Characteristic(){ Name="bb", Value="dsa"},
new Characteristic(){ Name="cc", Value="eqw"},
}
};
return PartialView("IndividualSearchPartial", individualSearch);
}
IndividualSearchPartial partial view:
#model IndividualSearch
#using (Html.BeginForm("IndividualSearches"))
{
<div id="Characteristics" class="mb-3">
#if (Model?.Characteristics != null)
{
for (var i = 0; i < Model?.Characteristics.Count; i++)
{
<div class="form-group">
#await Html.PartialAsync("IndividualSearchCharacterisiticPartial", Model.Characteristics[i])
</div>
}
}
</div>
}
IndividualSearchCharacterisiticPartial partial view:
#model Characteristic
#using (Html.BeginForm("Characteristics"))
{
<div id="characteristic-details" class="card">
<div class="form-horizontal">
<div class="card-block">
<div class="container">
<div class="row">
<div class="col-*-*">
#Html.LabelFor(m => m.Name, "Name", new { #class = "form-control-label" })
</div>
<div class="col">
#Html.TextBoxFor(m => m.Name, null, new { #class = "form-control" })
</div>
</div>
</div>
</div>
</div>
</div>
}
Here is the test code:
I'm having following FinanceProductFeatures table , I want show each of this table record as label name for a form.
So I created model class like this
public class ProductFinanceFeatures
{
public IList<AB_FinanceProductFeatures> ListProductFinanceFields { get; set; }
}
public partial class AB_FinanceProductFeatures
{
public string ProductFinanceNameEn { get; set; }
public string ProductFinance_Value_EN { get; set; }
}
then Controller class like this
[HttpGet]
public ViewResult Financing_Product_Feature_Configuration()
{
var model = new ProductFinanceFeatures
{
ListProductFinanceFields = db.FinanceProductFeatures.ToList()
};
return View(model);
}
then Its viewpage like this
#model albaraka.Models.ProductFinanceFeatures
#{
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#for (int i = 0; i < Model.ListProductFinanceFields.Count; i++)
{
<div class="form-group">
#Html.LabelFor(model => model.ListProductFinanceFields[i].ProductFinanceNameEn, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(m => m.ListProductFinanceFields[i].ProductFinance_Value_EN, new { #row = 5 })
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
but here I'm not getting expected result, cannot render the Label
showing like this
Just simply replace TextAreaFor with DisplayFor as below-
<div class="col-md-10">
#Html.DisplayFor(m => m.ListProductFinanceFields[i].ProductFinance_Value_EN, new { #row = 5 })
</div>
Or
<div class="col-md-10">
#Html.DisplayTextFor(m => m.ListProductFinanceFields[i].ProductFinance_Value_EN)
</div>
Hope this works for you..!
I'm trying to pass a RadioButtonFor to the model.
Controller
[HttpPost]
public ActionResult Contact(ApplicationCommentType model)
{
//send email here
//reload form
ApplicationCommentType appdata = new ApplicationCommentType();
appdata.CommentTypeData = db.CommentTypes.ToList();
return View(appdata);
}
ApplicationCommentType
public class ApplicationCommentType
{
public IEnumerable<CommentType> CommentTypeData { get; set; }
public String CommentTypeDataSelection { get; set; }
public String Name { get; set; }
public String Email { get; set; }
public String Comment { get; set; }
}
CommentType
public partial class CommentType
{
public int CommentTypeID { get; set; }
public string CommentTypeDesc { get; set; }
}
View
#using(#Html.BeginForm("Contact", "Home", FormMethod.Post, new{ #class ="form-horizontal"})){
<fieldset>
<legend>Contact Us</legend>
<div class="form-group">
#Html.LabelFor(x => x.Email, new {#class="col-lg-2 control-label"})
<div class="col-lg-10">
#Html.TextBoxFor(x => x.Email, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(x => x.Name, new { #class = "col-lg-2 control-label" })
<div class="col-lg-10">
#Html.TextBoxFor(x => x.Name, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<label for="textArea" class="col-lg-2 control-label">Questions, Comments, or Concerns</label>
<div class="col-lg-10">
#Html.TextAreaFor(x => x.Comment, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<label class="col-lg-2 control-label">Comment Type</label>
<div class="col-lg-10">
#foreach (var item in Model.CommentTypeData)
{
<div class="radio">
<label>
#Html.RadioButtonFor(x => x.CommentTypeData, item.CommentTypeDesc)
#Html.LabelFor(m => m.CommentTypeData, item.CommentTypeDesc, item.CommentTypeID)
</label>
</div>
}
#Html.HiddenFor(x => x.CommentTypeDataSelection)
</div>
</div>
</fieldset>
}
Now this kind of works, all the textbox items work. Placing a break point on the [HttpPost] return yields the following values.
Comment: "awesome"
CommentTypeData: Count = 0
CommentTypeDataSelection: null
Email: "example#example.com"
Name: "John Smith"
Shouldn't CommentTypeData have a count? If I check the request the selected value is there.
Request.Params["CommentTypeData"]: "General Improvement Suggestion"
So why is the Model not updated? Is it a requirement to manually update the Model from the Request object?
You can use #Html.RadioButtonFor but you should make sure that item.CommentTypeDesc compatible with Radio type.
Refer to MVC4: Two radio buttons for a single boolean model property
Hope it helps.
I have read several answers on this issue but despite this, it would appear I have developed code blindness.
I have the following view model:
public class IndividualProductVm
{
public virtual Products Products { get; set; }
public ProductSummary ProductSummary { get; set; }
public virtual IEnumerable<ProductSimpleResponse> ProductSimpleResponse { get; set; }
}
This is then passed into a view and then a partial view:
#model Websites.ViewModels.IndividualProductVm #{ ViewBag.Title = "Edit"; }
<h2>Edit</h2>
#using (Html.BeginForm(null, null, FormMethod.Post, new { name = "form", id = "mainForm" })) { #Html.AntiForgeryToken() #Html.ValidationSummary(true, "", new { #class = "text-danger" }) #Html.HiddenFor(model => model.Products.Id) #Html.HiddenFor(model
=> model.ProductSummary.SupplierId) Html.RenderPartial("_IndividualProduct", Model);
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index", new { id = Model.ProductSummary.SupplierId }, new { #class = "btn btn-default" })
</div>
#section Scripts { #Scripts.Render("~/bundles/jqueryval") }
#model Websites.ViewModels.IndividualProductVm
<div>
#Html.LabelFor(model => model.Products.ProductCode, htmlAttributes: new { #class = "control-label col-md-2" })
<div>
#Html.DisplayFor(model => model.Products.ProductCode, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div style="clear:both;"></div>
<div>
#Html.LabelFor(model => model.Products.ProductDescription, htmlAttributes: new { #class = "control-label col-md-2" })
<div>
#Html.DisplayFor(model => model.Products.ProductDescription, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<table class="table">
<tr>
<th>
Present
</th>
</tr>
#foreach (var item in Model.ProductSimpleResponse)
{
<tr>
#Html.HiddenFor(modelItem => item.Id)
#Html.HiddenFor(modelItem => item.SupplierId)
#Html.HiddenFor(modelItem => item.ProductCode)
<td>
#Html.EditorFor(modelItem => item.Present)
</td>
</tr>
}
</table>
However, when I enter the edit post, my viewmodel is null for the IEnumerable<ProductSimpleResponse> but fine for the other two classes.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(IndividualProductVm model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index", new { id = model.ProductSummary.SupplierId });
}
return View(model.Products);
}
If someone can explain what I'm doing wrong, I'd be most grateful.
Your property name is ProductSimpleResponse, alhtough the type is ProductSvhcSimpleResponse, so to iterate through it you should have.
#foreach (var item in Model.ProductSimpleResponse)
NOT
#foreach (var item in Model.ProductSvhcSimpleResponse)
use List because
IEnumerable is suitable just for iterate through collection and you can not modify (Add or Remove) data IEnumerable bring ALL data from server to client then filter them, assume that you have a lot of records so IEnumerable puts overhead on your memory.
public class IndividualProductVm
{
public virtual Products Products { get; set; }
public ProductSummary ProductSummary { get; set; }
public virtual List<ProductSvhcSimpleResponse> ProductSimpleResponse { get; set; }
}
More help click here
I am new to MVC 4.
I am receiving the following error:
Object reference not set to an instance of an object. Line 47:#Html.ActionLink("Back to List", "Index", new { id = Model.RestaurantId })
Here is my view that is causing the error (please see the last couple of lines):
#model OdeToFood.Models.RestaurantReview
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>New Review</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Rating)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Rating)
#Html.ValidationMessageFor(model => model.Rating)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Body)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Body)
#Html.ValidationMessageFor(model => model.Body)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ReviewerName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ReviewerName)
#Html.ValidationMessageFor(model => model.ReviewerName)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index", new { id = Model.RestaurantId })
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Here is my controller action that is being called when the Back To List link is clicked:
public ActionResult Index([Bind(Prefix="id")]int restaurantId)
{
var restaurant = _db.Restaurants.Find(restaurantId);
if (restaurant != null) {
return View(restaurant);
}
return HttpNotFound();
}
And here is the RestaurantReview model that is being strongly-typed in the view:
public class RestaurantReview
{
public int Id { get; set; }
public int Rating { get; set; }
public string Body { get; set; }
public string ReviewerName { get; set; }
public int RestaurantId { get; set; }
}
Any help would be muchly appreciated.
Cheers!
In your code you are showing the Create view, and the Index controller. Check if in the Create controller you are passing the Model, or check if it needs model.