I have a model that combine default data when generate Razor pages and new input data. Both of them will be passing to Controller to handle. I have done it with multiple Razor pages, with a base model that contain common field but in this new Page, I have stumble upon this error which I don't know how to dealt with it. In the deepest floor of the Model, it would only accept one field. If I try to pass more than one, my entire model in Controller become a null value.
#for (int i = 0; i < Model.layThongTinBangThuocCongTyThanhViens.Count; i++)
{
//Value for Model
<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].CttvId" />
<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].CttvThuocBangId" />
<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].CauHinhBangId" />
<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDong" />
<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].CongThucDong" />
<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].DichCongThuc" />
<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].listId" />
var dataTarget = "#collapse" + i.ToString();
var tempid = "collapse" + i.ToString();
var danhmuc = Model.layThongTinBangThuocCongTyThanhViens.ElementAt(i);
for (int x = 0; x < danhmuc.TenDongThuocCongTies.Count; x++)
{
<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDongThuocCongTies[x].CauHinhBangId" />
//If I un-comment even one of the line below, the model become null.
//Each of them field is not special. I can comment the one above and choose whichever field I want to uncomment
//but in order to the model normal, only field is allowed to exist.
#*<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDongThuocCongTies[x].CttvThuocBangId" />
<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDongThuocCongTies[x].CongThucDong" />
<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDongThuocCongTies[x].CauHinhBang_Cha" />
<input type="hidden" asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDongThuocCongTies[x].TenDong" />*#
}
My TenDongThuocCongTies Model as request:
public class TenDongThuocVeCongTyModel
{
public int CttvThuocBangId { get; set; }
public int CauHinhBangId { get; set; }
public string CongThucDong { get; set; }
public string TenDong { get; set; }
public int CauHinhBang_Cha { get; set; }
[NotMapped]
public string inputValue { get; set; }
}
My Model:
[HttpPost]
[Route("post-item")]
public IActionResult PostHoaCheLuyen(TongHopItemHoaCheLuyenModel model)
{
}
Edit: The data passing back to Controller is not wrong. It just turn everything to null if I pass more than one field. It doesn't return any kind of error so I don't know how to fix it. I am hoping to see anyone who used to meet the same situation as me can solve it for me.
I use the following code,and it can work.Maybe you need to check if your model properties' names and types are right.Or have you added other properties to the model which are not existed in TongHopItemHoaCheLuyenModel.
Models:
public class TongHopItemHoaCheLuyenModel {
public List<LayThongTinBangThuocCongTyThanhViens> layThongTinBangThuocCongTyThanhViens { get; set; }
}
public class LayThongTinBangThuocCongTyThanhViens {
public List<TenDongThuocVeCongTyModel> TenDongThuocCongTies { get; set; }
public int CttvId { get; set; }
public int CttvThuocBangId { get; set; }
public int CauHinhBangId { get; set; }
public string TenDong { get; set; }
public string CongThucDong { get; set; }
public string DichCongThuc { get; set; }
public int listId { get; set; }
}
public class TenDongThuocVeCongTyModel
{
public int CttvThuocBangId { get; set; }
public int CauHinhBangId { get; set; }
public string CongThucDong { get; set; }
public string TenDong { get; set; }
public int CauHinhBang_Cha { get; set; }
[NotMapped]
public string inputValue { get; set; }
}
Actions:
public IActionResult TestBindList()
{
TongHopItemHoaCheLuyenModel t = new TongHopItemHoaCheLuyenModel()
{
layThongTinBangThuocCongTyThanhViens = new List<LayThongTinBangThuocCongTyThanhViens> {
new LayThongTinBangThuocCongTyThanhViens(){
CauHinhBangId=1,
CttvId=1,
CttvThuocBangId=1,
listId=1,
CongThucDong="ctd1",
DichCongThuc="dct1",
TenDong="td1",
TenDongThuocCongTies=new List<TenDongThuocVeCongTyModel>(){
new TenDongThuocVeCongTyModel(){
CauHinhBangId=1,
CttvThuocBangId=1,
CauHinhBang_Cha=1,
CongThucDong="ctd1",
TenDong="td1",
inputValue="value1"
},
new TenDongThuocVeCongTyModel(){
CauHinhBangId=1,
CttvThuocBangId=1,
CauHinhBang_Cha=1,
CongThucDong="ctd1",
TenDong="td1",
inputValue="value2"
}
}
},
new LayThongTinBangThuocCongTyThanhViens(){
CauHinhBangId=2,
CttvId=2,
CttvThuocBangId=2,
listId=2,
CongThucDong="ctd2",
DichCongThuc="dct2",
TenDong="td2",
TenDongThuocCongTies=new List<TenDongThuocVeCongTyModel>(){
new TenDongThuocVeCongTyModel(){
CauHinhBangId=2,
CttvThuocBangId=2,
CauHinhBang_Cha=2,
CongThucDong="ctd2",
TenDong="td2",
inputValue="value01"
},
new TenDongThuocVeCongTyModel(){
CauHinhBangId=2,
CttvThuocBangId=2,
CauHinhBang_Cha=2,
CongThucDong="ctd2",
TenDong="td2",
inputValue="value02"
}
}
}
}
};
return View(t);
}
[HttpPost]
public IActionResult PostHoaCheLuyen(TongHopItemHoaCheLuyenModel model)
{
return Ok();
}
View:
#model xxx.xxx.TongHopItemHoaCheLuyenModel
<form method="post" asp-action="PostHoaCheLuyen">
#for (int i = 0; i < Model.layThongTinBangThuocCongTyThanhViens.Count; i++)
{
//Value for Model
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].CttvId" />
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].CttvThuocBangId" />
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].CauHinhBangId" />
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDong" />
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].CongThucDong" />
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].DichCongThuc" />
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].listId" />
var dataTarget = "#collapse" + i.ToString();
var tempid = "collapse" + i.ToString();
var danhmuc = Model.layThongTinBangThuocCongTyThanhViens.ElementAt(i);
for (int x = 0; x < danhmuc.TenDongThuocCongTies.Count; x++)
{
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDongThuocCongTies[x].CauHinhBangId" />
//If I un-comment even one of the line below, the model become null.
//Each of them field is not special. I can comment the one above and choose whichever field I want to uncomment
//but in order to the model normal, only field is allowed to exist.
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDongThuocCongTies[x].CttvThuocBangId" />
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDongThuocCongTies[x].CongThucDong" />
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDongThuocCongTies[x].CauHinhBang_Cha" />
<input asp-for="layThongTinBangThuocCongTyThanhViens[i].TenDongThuocCongTies[x].TenDong" />
}
}
<input type="submit" value="submit" />
</form>
result:
Related
I've tried and played around a lot, but I can't seem to get my form authentication working.
Before I add functionality to actually create a review, I need to get this working.
The site simply submits the form, regardless of what is in the review text element. It should be catching and displaying an error message if it is blank.
Model:
public class CreatedReview
{
public int Id { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Review1 { get; set; }
public int? RestaurantId { get; set; }
public int? UserId { get; set; }
[Required]
public int? Stars { get; set; }
public CreatedReview(string review, int stars)
{
this.Review1 = review;
this.Stars = stars;
}
}
Index.cshtml
#model CreatedReview
#{
}
<h1>Leave a review!</h1>
<form method="post" asp-action="LeaveReview">
<p>Restaurant</p>
<input type="text" name="name" />
<p>Zipcode</p>
<input type="number" name="zipcode" />
<p>Review</p>
<input type="text" name="review" />
<span asp-validation-for="Review1" class="text-danger"></span>
<p>Star Rating</p>
<input type="number" name="stars" />
<input type="submit" />
</form>
Controller:
public IActionResult LeaveReview(string name, int zipcode, string review, int stars)
{
if (!ModelState.IsValid)
{
return View("Index");
}
// create review blah blah blah (need to accept all of the parameters later)
var newReview = new CreatedReview(review, stars);
return View("Index");
}
I have a web application for registering teams to a competition, where each team can select a number of technologies that they will use for their project. The technologies are saved in a Label class.
I am using a view model to bind the information from the form to the action.
However, when I try to submit the form, it takes all other fields, except the list of technologies.
Label.cs
public class Label
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string ColorPalette { get; set; }
}
CreateTeamViewModel.cs
public class CreateTeamViewModel
{
[Required]
public string TeamName { get; set; }
public string ProjectName { get; set; }
public string ProjectDescription { get; set; }
[Required]
public string RepositoryLink { get; set; }
public List<Label> Labels = new List<Label>();
}
TeamsController.cs
public class TeamsController
{
private readonly ApplicationDbContext context;
public IActionResult Create()
{
ViewData["Labels"] = this.context.Labels.ToList();
return View();
}
[HttpPost]
public IActionResult Create(CreateTeamViewModel team)
{
List<Label> labels = team.Labels;
int count = labels.Count; // count = 0
return LocalRedirect("/");
}
}
Create.cshtml (the list of checkboxes)
#model Competition.Data.ViewModels.CreateTeamViewModel
#{
List<Label> labels = ViewData["Labels"] as List<Label>;
}
<form asp-action="Create">
<div class="form-check">
#for(int i = 0; i < labels.Count; i++)
{
<input asp-for="#Model.Labels[i].IsSelected" type="checkbox" />
<label asp-for="#Model.Labels[i].Name">
<span class="badge badge-#labels[i].ColorPalette">#labels[i].Name</span>
</label>
<input asp-for="#Model.Labels[i].Name" type="hidden" value="#labels[i].Name" />
<input asp-for="#Model.Labels[i].ColorPalette" type="hidden" value="#labels[i].ColorPalette" />
}
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
You need to bind to a list of int instead of a list of Label on your view model. Then, you'll need to use that list of selected ids to fill your list of labels on the Team entity you're persisting:
public class CreateTeamViewModel
{
[Required]
public string TeamName { get; set; }
public string ProjectName { get; set; }
public string ProjectDescription { get; set; }
[Required]
public string RepositoryLink { get; set; }
public List<int> SelectedLabels { get; set; } = new List<int>();
}
Then, you'll need to modify your form to bind your checkboxes to this list:
#foreach (var label in labels)
{
<input asp-for="SelectedLabels" id="Label#(label.Id)" value="#label.Id" type="checkbox" />
<label id="Label#(label.Id)">
<span class="badge badge-#label.ColorPalette">#label.Name</span>
</label>
}
Notice that I removed the hidden inputs. You should never post anything that the user should not be able to modify, as even hidden inputs can be tampered with.
After posting, server-side you'll end up with a list of label ids that were selected by the user. Simply query the associated labels out of your database and then assign that to the team you're creating:
team.Labels = await _context.Set<Label>().Where(x => model.SelectedLabels.Contains(x.Id)).ToListAsync();
I have these viewmodels for employees and their roles:
public class EmployeeViewModel
{
public int Id { get; set; }
// some more properties
public List<EmployeeRoleViewModel> EmployeeRoles { get; set; }
}
public class EmployeeRoleViewModel
{
public int Id { get; set; }
public int EmployeeId { get; set; }
public int RoleId { get; set; }
public string Title { get; set; }
public bool Selected { get; set; }
}
I am displaying the checkboxes like this in the Edit-view:
#for (int i = 0; i < Model.EmployeeRoles.Count(); i++)
{
<label>
<input type="checkbox" asp-for="#Model.EmployeeRoles[i].Selected" />
#Model.EmployeeRoles[i].Title
</label>
}
This generates this HTML for a checked checkbox:
<input type="checkbox" checked="checked"
id="EmployeeRoles_0__Selected"
name="EmployeeRoles[0].Selected" value="true" />
... and this for an unchecked one:
<input type="checkbox"
id="EmployeeRoles_2__Selected"
name="EmployeeRoles[2].Selected" value="true" />
The checkboxes are all being rendered correctly, with checkmarks on all the right boxes.
The form is posted to this controller-method (simplified for brevity):
[HttpPost]
public async Task<IActionResult> Edit(int id,[Bind("Id,EmployeeRoles")] Employee employee)
{
db.Update(employee);
await db.SaveChangesAsync();
return RedirectToAction("Details", "Branches");
}
The problem is that employee.EmployeeRoles is a list of length 0, even though several checkboxes are checked. What am I doing wrong?
The name of the input field will have to be
name="Employee.EmployeeRoles[index].Selected"
in order for EmployeeRoles[index] to be bound to the EmployeeRoles list inside the Employee object.
Please firstly confirm that you are using same view model both in view and in action function . You can add hidden input field to help you bind the properties :
#for (int i = 0; i < Model.EmployeeRoles.Count(); i++)
{
<label>
<input type="checkbox" asp-for="#Model.EmployeeRoles[i].Selected" />
#Model.EmployeeRoles[i].Title
</label>
<input type="hidden" asp-for="#Model.EmployeeRoles[i].Id" />
<input type="hidden" asp-for="#Model.EmployeeRoles[i].Title" />
}
Then on controller , you can get the lists :
Then check the Selected properties to confirm whether current role is selected .
I am building a form based on the model with this structure
public class DynamicFormElementsModel
{
//param might be shown in UI , but will not pass while setting params for report . Example select org.
public bool ReportParameter { get; set; }
public string ParamName { get; set; }
public string ParamLabel { get; set; }
public string ParamType { get; set; }
public bool Visible { get; set; }
public string ParamValue { get; set;}
}
And in Razor i am building the control like
#using (Html.BeginForm("Index", "Reports", FormMethod.Post, new { role = "form" }))
{
#Html.HiddenFor(p => Model.ReportId)
for (int i = 0; i < Model.Parameters.Count(); i++)
{
<div class="form-group">
<label class="control-label">#Model.Parameters[i].ParamLabel</label>
#switch (#Model.Parameters[i].ParamType.ToLower())
{
case "textbox":
if (#Model.Parameters[i].Visible)
{
<input type="text" class="form-control" value="#Model.Parameters[i].ParamValue" name="#Model.Parameters[i].ParamName" />
}
else
{
<input type="hidden" value="#Model.Parameters[i].ParamValue" name="#Model.Parameters[i].ParamName" />
}
break;
default:
break;
}
</div>
}
<input type="submit" class="btn btn-default" value="Submit" />
}
Normal textboxes will be rendered correctly with this approach . But how can i make a dropdown and fill the dropdown from a table id , name value pair from the sql table
How do you properly bind a Dictionary and it's values per key to checkboxes?
I can display them in the HTTPGET but binding the selected values again to HTTPPOST doesn't seem to work.
viewmodel
public class EditViewModel
{
public Foo Foo { get; set; }
public Dictionary<Bar, List<BarVersionEditVM>> Matrix { get; set; }
}
public class BarVersionEditVM
{
public int ID { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public bool IsSupported { get; set; }
}
view:
<form asp-action="Edit">
<div class="row">
#foreach (var kvp in Model.Matrix.OrderByDescending(x => x.Key.Name))
{
<div class="col-md-2 col-lg-2">
<fieldset>
<legend>#kvp.Key.Name</legend>
#foreach (var version in kvp.Value)
{
<div>
<input type="checkbox" id="#version.ID" value="#version.IsSupported" name="#version.Name" #(version.IsSupported ? "checked=\"checked\"" : "") />
<label>#version.Version:</label>
</div>
}
</fieldset>
</div>
}
</div>
<input type="hidden" asp-for="#Model.Foo.ID" />
<input type="submit" value="Save" class="btn btn-default" />
</form>
In the View I tried also to rewrite with foreach and using Html helpers, but without success:
#Html.CheckBoxFor(model => model.Matrix[kvpair.Key][i].IsSupported)
controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(EditViewModel vm) {
// vm is there but Matrix are null.
// and only the ID of Foo property is filled in.
}
any suggestions?
Unless your Dictionary has simple value types for both the Key and Value (e.g. public Dictionary<string, string>), the DefaultModelBinder requires that the form control name attributes be in the format
<input .... name="Matrix[0].Key" value="..." />
<input .... name="Matrix[0].Value[0].ID" value="..." />
<input .... name="Matrix[0].Value[0].Name" value="..." />
There are no HtmlHelper methods that will generate the correct html to allow binding to your Dictionary.
It is far simpler to create simple view model(s) to with IList<T> properties for the collections. Based on the view you have shown, those models would be
public class EditVM
{
public int FooID { get; set; }
public List<BarVM> Bars { get; set; }
}
public class BarVM
{
public string Name { get; set; }
public List<BarVersionVM> Versions { get; set; }
}
public class BarVersionVM
{
public int ID { get; set; }
public string Name { get; set; } // not clear where you use this property
public string Version { get; set; }
public bool IsSupported { get; set; }
}
and your view would then be
#model EditVM
....
#Html.HiddenFor(m => m.FooID)
#for(int i = 0; i < Model.Bars.Count; i++)
{
<fieldset>
<legend>#Model.Bars[i].Name</legend>
#Html.HiddenFor(m => m.Bars[i].Name) // in case you need to return the view in the POST method
#for(int j = 0; j < Model.Bars[i].Versions.Count; j++)
{
<div>
#Html.HiddenFor(m => m.Bars[i].Versions[j].ID)
#Html.CheckBoxFor(m => m.Bars[i].Versions[j].IsSupported)
#Html.LabelFor((m => m.Bars[i].Versions[j].IsSupported, Model.Bars[i].Versions[j].Version)
</div>
}
</fieldset>
}
<input type="submit" value="Save" />