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
Related
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:
Screenshot of table structure with data
Table structure is as below:
Id | Category | DisplayName | FieldName | FieldType | FieldLength | IsRequired
Please see screenshot for details.
On selection of a category all HTML fields defined in table against that category should populate in view.
And how to validate on form submit.
I have more than 400 categories.
This is what I think you can do.
First declare a class call it any name, I will use SampleClass
public class SampleClass
{
public int Id { get; set; }
public string Category { get; set; }
public string DisplayName { get; set; }
public string FieldName { get; set; }
public string FieldType { get; set; }
public string FieldLength { get; set; }
public int IsRequired { get; set; }//you can convert this to boolean to make it easier
}
Secondly, populate this class from the db in the Controller
//assuming you are using linq query
var allEntry = context.Table.Where(m => m.Category == SelectedCategory);
var allViews = new List<SampleClass>();
foreach (var entry in allEntry)
{
allViews.Add(new SampleClass
{
FieldName = entry.FieldName,
//and so on...
});
}
Third and in the view do this as I said in the comment
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, new { #class = "forms-horizontal", role = "form" }))
{
<div class="form-horizontal">
<h4>Title of the form</h4>
<hr /><br />
#foreach (var a in List<SampleClass>)//note this(List<SampleClass>) will be passed from the controller to the view.
{
switch (a.FieldType)
{
case "Test":
<div class="form-group">
<label for="#a.FieldName"></label>
<input id="#a.FieldName" type="text" name="#a.FieldName" required="#(a.IsRequired == 1 ? "required" : "")" />
</div>
break;
case "Number":
<div class="form-group">
<label for="#a.FieldName"></label>
<input id="#a.FieldName" type="number" name="#a.FieldName" required="#(a.IsRequired == 1 ? "required" : "")" />
</div>
break;
case "Select":
<div class="form-group">
<label for="#a.FieldName"></label>
#Html.DropDownList(a.FieldName, new SelectList(DbUtils.GetEvaluations(), "Value", "Text", null), "Select..", new { #required = #a.IsRequired == 1 ? "required" : "" })
</div>
break;
}
}
<div class="form-group">
<div class="col-md-offset-3 col-md-9">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</div>
</div>
}
You can have more cases in your select clause depending on what you need.
Hope this helps..
I need to pass some models to one view. What I've done so far is something like this:
OrderTypeModel:
namespace test.Models
{
public class OrderTypeModel
{
[Key]
public int Id { get; set; }
public string OrderType { get; set; }
}
}
OrderStatusModel:
namespace test.Models
{
public class OrderStatusModel
{
[Key]
public int Id { get; set; }
public string OrderStatus { get; set; }
}
}
OrderSizeModel:
namespace test.Models
{
public class OrderSizeModel
{
[Key]
public int Id { get; set; }
public string OrderSize { get; set; }
}
}
OrderModel:
namespace test.Models
{
public class OrdersModel
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public int OrderNumber { get; set; }
public int OrderType_Id { get; set; }
public int OrderStatus_Id { get; set; }
public int OrderSize_Id { get; set; }
public DateTime CreationDate{ get; set; }
}
}
And there's a MultipleViewsModel:
namespace test.Models
{
public class MultipleViewsModel
{
public List<OrderTypeModel> Type { get; set; }
public List<OrderSizeModel> Size { get; set; }
public List<OrderStatusModel> Status { get; set; }
public OrdersModel Orders { get; set; }
}
}
OrderType_Id, OrderStatus_Id, and OrderSize_Id are Foreign Keys in my database. Now When I need to call them all in a form, it throws an error:
My View is something like this:
#model MultipleViewsModel
#{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Orders.Title" class="control-label"></label>
<input asp-for="Orders.Title" class="form-control" />
<span asp-validation-for="Orders.Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Orders.OrderType_Id" class="control-label"></label>
#{
foreach (var ot in ViewData["OrderType"] as Type)
{
<input asp-for="Orders.OrderType_Id" type="radio" value=#ot.Id name=#ot.OrderType. class="form-control" />
}
}
<span asp-validation-for="Orders.OrderType_Id" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
And my Controller is something like this:
public IActionResult Create()
{
ViewData["OrderType"] = _context.OrderType
.Any();
ViewData["OrderSize"] = _context.OrderSize.Any();
return View();
}
I need to put the models in radio forms. But an error is thrown at this line in view:
foreach (var ot in ViewData["OrderType"] as Type)
And says Type is not found in current context.
Note that I've cut a major part of what was not needed and so my form in the view is not complete just to be succinct.
Any kind of help is appreciated.
You can use view model as you have included in your page :
#model MultipleViewsModel
On server side , you should initialize the model class and set the values . For example , you can fill the Type in create action like :
MultipleViewsModel multipleViewsModel = new MultipleViewsModel();
multipleViewsModel.Type = new List<OrderTypeModel>();
multipleViewsModel.Type = db.orderTypes.ToList();
multipleViewsModel.Orders = new OrdersModel() { OrderType_Id = 2 };
return View(multipleViewsModel);
On client side , you can loop the OrderTypeModel list and set default value by which matches the OrderType_Id :
#{
foreach (var orderType in Model.Type)
{
<input asp-for="Orders.OrderType_Id" type="radio" value=#orderType.Id name=Orders.OrderType_Id />
#Html.Label("OrderType" + orderType.Id, orderType.OrderType)
}
}
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();
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" />