I know this question has been asked so many times but checked all the answers with my code and I have no naming conflict and I pass the ID as a hidden field and yet the ViewModel passes to the controller as null.
This question is the link of my project idea and snippet of the code. What might I be missing?
I created a new project and copied your code into the project. I, too, recieved a null model when trying to create. It took a few changes to fix it for me. I had to add 'Model.AccountOrOU' as the passed in model to the partial. I had to initialize the model in the controller for the Create Index. I also had to change the name attribute on the properties of the AccountOrOU model in the view. Here is my code ->
Index.cshtml
#model AccountVM
#{
ViewData["Title"] = "Home Page";
}
<h2>Create</h2>
#using (Html.BeginForm("SaveAccount", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div>
#{await Html.RenderPartialAsync("_Account.cshtml", Model.AccountOrOU);}
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" />
</div>
</div>
</div>
}
_Account.cshtml
#model AccountOrOU
<div class="form-horizontal" id="ViewData">
<h4>Account Partial</h4>
<hr />
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="text" name="AccountOrOU.Name" class="form-control" />
</div>
</div>
</div>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Test2.Models
{
public class AccountVM
{
public AccountVM()
{
AccountOrOU = new AccountOrOU();
}
public AccountOrOU AccountOrOU { get; set; }
}
public class AccountOrOU
{
public string Name { get; set; }
}
}
Controller
public IActionResult Index()
{
AccountVM blah = new AccountVM();
return View(blah);
}
public IActionResult SaveAccount(AccountVM input)
{
return View("Index", input);
}
See if this helps:
#{await Html.RenderPartialAsync("_Account.cshtml"}
Then change this line:
<input type="text" name="AccountOrOU.Name" class="form-control" />
to this:
#Html.TextBoxFor(x => x.AccountOrOU.Name, new { #class="form-control" })
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 have been trying to validate an input which is not properly in the model. I have the following code:
#using (Html.BeginForm("Address", "Locations", FormMethod.Post, new { id =
"mainForm" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="card shadow-sm">
<div class="card-header">
<h3 class="card-title">
Step 1
</h3>
<label>
Search for service location(s)
</label>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputEmail1">Zip code: <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="ZipCode" name="ZipCode" autocomplete="off" autofocus />
#Html.ValidationMessage("ZipCode")
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputPassword1">House number:</label>
<input type="text" class="form-control" id="HouseNumber" name="HouseNumber" />
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="exampleInputPassword1">City:</label>
<input type="text" class="form-control" id="City" name="City" />
</div>
</div>
</div>
</div>
<div class="card-footer">
<div class="row">
<div class="col-md-12">
<button class="btn btn-primary" type="submit">
<i class="fas fa-search"></i>
Search for location(s)
</button>
</div>
</div>
</div>
</div>
}
The mode is
#model PagedList.IPagedList<iCRM.Models.Address>
But the name which I gave to the input is not in the model. However the validation is not working at all. And the POST is ignoring my validation.
Can somebody help me out, what I am doing wrong?
Thanks in advance.
You have 2 options.
add ZipCode to the model class iCRM.Models.Address.
Write custom validation with Request.Forms["ZipCode"] and Javascript handler on client side. (#Html.ValidationMessage("ZipCode") you can not call)
This is idea based on option 2 but not a definite answer.
Controller
[HttpGet]
public ActionResult Address()
{
return View();
}
[HttpPost]
public ActionResult Address(AddressModel model)
{
if (String.IsNullOrEmpty(Request.Form["ZipCode"]))
{
ViewBag.ValidationForZipCode = "Problem with ZipCode";
}
return View();
}
View
#model WebApplication1.Models.AddressModel
#{
ViewBag.Title = "Address";
string strValidationForZipCode = "";
if (ViewBag.ValidationForZipCode != null)
{
strValidationForZipCode = ViewBag.ValidationForZipCode.ToString();
}
}
<h2>Address</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken();
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div class="pull-right">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</div>
<table>
<tr>
<td>
<div class="form-group">
#Html.LabelFor(model => model.Address1, htmlAttributes: new {
#class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Address1, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Address1, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<label for="exampleInputEmail1">Zip code: <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="ZipCode" name="ZipCode" autocomplete="off" autofocus />
<input type="hidden" id="hdValidationForZipCode" name="hdValidationForZipCode" value="#strValidationForZipCode" />
</div>
</td>
</tr>
<tr>
<td>
</td>
</tr>
</table>
</div>
}
<script type="text/javascript">
var str1 = document.getElementById("hdValidationForZipCode").value; //$('#hdValidationForZipCode').val();
if (str1 != "")
alert(str1);
</script>
Model
using System.ComponentModel.DataAnnotations;
namespace WebApplication1.Models
{
public class AddressModel
{
[Required]
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
}
I need some help with this, please!
I have this Model:
public class MyModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
I have also a EditorTemplate
#model MyProject.Models.MyModel
<div class="form-group">
<label class="col-md-2 control-label">#Html.DisplayFor(model => model.Name)</label>
<div class="col-md-10">
<input asp-for="#Model.Value" class="form-control"/>
<span asp-validation-for="#Model.Value" class="text-danger"></span>
</div>
</div>
The Edit View
#using Microsoft.AspNetCore.Mvc.ViewFeatures
#model List<MyProject.Models.MyModel>
#{
ViewData["Title"] = "Edit";
}
<div class="spaceUnderLogo">
<div class="row">
<div class="col-md-12 ">
<h2>Edit</h2>
<form id="manageMyModel" asp-controler="MyController" asp-action="Edit" method="post" class="">
<div class="form-horizontal">
<hr />
#Html.EditorForModel()
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
</div>
</div>
And MyController has this two methods
[HttpGet]
public async Task<IActionResult> Edit()
{
List<MyModel> model = await GetCurrentInfoAsync();
return View(model);
}
[HttpPost]
public async Task<ActionResult> Edit(List<MyModel> model)
{
// here is where I have the issue
}
As you see I'm having an issue with the post method. When I get the view I send 2 objects: {Id = 1, Name = "A", Value = "9999"} and {Id = 2, Name = "B", Value = null}
Edit B Value property to "8888" and in the Post I'm getting the List only with the Value property like this:
{Id = 0, Name = null, Value = "9999"} and {Id = 0, Name = null, Value = "8888"}
Any idea?
You can use:
#model MyProject.Models.MyModel
#Html.HiddenFor(model => model.Id)
#Html.HiddenFor(model => model.Name)
<div class="form-group">
<label class="col-md-2 control-label">#Html.DisplayFor(model => model.Name)</label>
<div class="col-md-10">
<input asp-for="#Model.Value" class="form-control"/>
<span asp-validation-for="#Model.Value" class="text-danger"></span>
</div>
</div>
The trick here is the:
#Html.HiddenFor(model => model.Id)
This will ensure that the form contains the value when posted, but there is no editor visible on the form. You will need one of these hidden input for any field that is readonly for the model retrieved by a GET in any form submit.
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>
}
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..!