Iam getting the following error when posting the form with ProductVM fields:
This is the model being passed:
[BindProperties]
public class ProductVM
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public decimal Price { get; set; }
public int Quantity { get; set; } = 0;
public List<SelectListItem> Categories { get; set; }
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public List<SelectListItem> Suppliers { get; set; }
public int SupplierId { get; set; }
public string SupplierName { get; set; }
}
This is the form:
#model IEnumerable<ProductVM>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
#Html.DisplayFor(modelItem => item.CategoryName)
</td>
<form asp-action="AddToCart">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="#item.Id">
<input type="hidden" asp-for="#item.Name">
<input type="hidden" asp-for="#item.Price">
<td>
<input asp-for="#item.Quantity" value="1" class="form-control" style="inline-size:100px">
<span asp-validation-for="#item.Quantity" class="text-danger"></span>
</td>
<td>
<input type="submit" id="addtocart" value="Add To Cart" class="btn btn-primary"/>
</td>
</form>
</tr>
}
Any ideas why the name field is throwing error even with the "input type="hidden" asp-for="#item.Name"" ?
foreach loop is not submiting data back, you have to use for loop
#foreach (var i=0, i< Model.Count; i++) {
.....
<input type="hidden" asp-for="#Model[i].Id">
<input type="hidden" asp-for="#Model[i].Name">
...
}
Related
In my class I have below
public int recordref { get; set; }
[DisplayFormat(ConvertEmptyStringToNull = false)]
[Required]
public string disty { get; set; }
[DisplayFormat(ConvertEmptyStringToNull = false)]
[Required]
public string country { get; set; }
public int claim_year { get; set; }
public int claim_month { get; set; }
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string claim_no { get; set; }
[DisplayFormat(ConvertEmptyStringToNull = false)]
[Required]
public string partner { get; set; }
//[DisplayFormat(ConvertEmptyStringToNull = false)]
public string sp_mdf_number { get; set; }
public decimal original_claim_amount { get; set; }
//[DisplayFormat(ConvertEmptyStringToNull = false)]
public string description { get; set; }
public DateTime date_created { get; set; }
public string created_by { get; set; }
public DateTime date_updated { get; set; }
public string updated_by { get; set; }
and my RAZOR page is as below.
#page "/adddebtor/{CurrentID}"
#page "/adddebtor"
#using BlazorAppDashboard.Data
#inject DebtorService ObjDebtorService
#using Blazored.Typeahead;
#inject NavigationManager NavigationManager
<h3>Add Debtor</h3>
<EditForm Model="#objDebtor" OnValidSubmit="#SaveDebtor">
<DataAnnotationsValidator />
<ValidationSummary />
<table>
<tr>
<td>Disty</td>
<td>
<select #bind="#objDebtor.disty">
<option value="-1"></option>
#if (objDisties != null)
{
#foreach (var disty in objDisties)
{
<option value="#disty.disty_name">#disty.disty_name</option>
}
}
</select>
</td>
</tr>
<tr>
<td>Country</td>
<td>
<select #bind="#objDebtor.country">
<option value="-1"></option>
<option value="AU">AU</option>
<option value="NZ">NZ</option>
</select>
</td>
</tr>
<tr>
<td>Claim Year</td>
<td><input for="Name" #bind="#objDebtor.claim_year" /></td>
</tr>
<tr>
<td>Claim Month</td>
<td><input for="Name" #bind="#objDebtor.claim_month" /></td>
</tr>
<tr>
<td>Claim No</td>
<td><input for="Name" #bind="#objDebtor.claim_no" /></td>
</tr>
<tr>
<td>Partner</td>
<td>
<input type="text" list="textsearch" #bind-value="#objDebtor.partner" #bind-value:event="oninput" #onkeyup="autoComplete" />
<datalist id="textsearch">
#if (partners != null)
{
#foreach (var pt in partners)
{
<option value="#pt.partner">#pt.partner</option>
}
}
</datalist>
</td>
</tr>
<tr>
<td>SP/MDF No</td>
<td><input for="Name" #bind="#objDebtor.sp_mdf_number" /></td>
</tr>
<tr>
<td>Original Claim Amount</td>
<td><input for="Name" #bind="#objDebtor.original_claim_amount" /></td>
</tr>
<tr>
<td>Description</td>
<td><input for="Name" #bind="#objDebtor.description" /></td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="Save" /> <input type="button" #onclick="#CancelSave" value="Cancel" />
#ErrorMessage
</td>
</tr>
</table>
</EditForm>
#code {
[Parameter]
public string CurrentID { get; set; }
public string ErrorMessage { get; set; }
BlazorAppDashboard.Data.Debtors objDebtor = new BlazorAppDashboard.Data.Debtors();
List<BlazorAppDashboard.Data.Disties> objDisties = new List<Disties>();
string temp_partner = "";
protected override async Task OnInitializedAsync()
{
if (CurrentID != null)
{
objDebtor = await Task.Run(() => ObjDebtorService.GetDebtorByRef(Convert.ToInt32(CurrentID)));
}
objDisties = await Task.Run(() => ObjDebtorService.getDistyList());
}
protected void SaveDebtor()
{
ErrorMessage = "";
ReturnData rd = new ReturnData();
rd = ObjDebtorService.Create(objDebtor, Convert.ToInt32(CurrentID));
if (rd.status == 1)
{
NavigationManager.NavigateTo("debtorslist");
}
else
{
ErrorMessage = "Error.Unable to save." + rd.error;
}
}
protected void CancelSave()
{
NavigationManager.NavigateTo("debtorslist");
}
//autofill partner code
private List<Partner> partners;
protected override void OnInitialized()
{
DebtorService partnerdataservice = new DebtorService();
}
private async Task<List<Partner>> autoComplete()
{
DebtorService partnerdataservice = new DebtorService();
partners = await Task.FromResult(partnerdataservice.getPartners(objDebtor.partner));
return partners;
}
//end autofill code
}
When I submit the form, it won't convert null values to empty strings and give me SQL errors.
I do not wish to check each field one by one when I pass parameters to SQL. My parameters are below.
cmd.Parameters.AddWithValue("#disty", debtors.disty);
cmd.Parameters.AddWithValue("#country", debtors.country);
cmd.Parameters.AddWithValue("#claim_year", debtors.claim_year);
cmd.Parameters.AddWithValue("#claim_month", debtors.claim_month);
cmd.Parameters.AddWithValue("#claim_no", debtors.claim_no);
cmd.Parameters.AddWithValue("#partner", debtors.partner);
cmd.Parameters.AddWithValue("#status", "Pending");
cmd.Parameters.AddWithValue("#sp_mdf_number", debtors.sp_mdf_number);
cmd.Parameters.AddWithValue("#description", debtors.description);
cmd.Parameters.AddWithValue("#original_claim_amount", debtors.original_claim_amount);
cmd.Parameters.AddWithValue("#created_by", "User Name");
Why am I still getting the below error
Error.Unable to save.The parameterized query '(#recordref int,#disty nvarchar(16),#country nvarchar(2),#claim_' expects the parameter '#claim_no', which was not supplied.
As an example #claim_no field is not entered (no value typed in the box) in the blazor view and it should convert to empty string ?
I don't think it's that inconvenient to just do a null check. It's only 5 characters more, and I think it will be easier to debug than to have some auto behavior filled in somewhere else:
Method One: Null Coalesce
If your SQL table's column is not nullable, and you really want to insert an empty string:
cmd.Parameters.AddWithValue("#claim_no", debtors.claim_no ?? ""); // null coalesce
Method 2: Submit Null Value to SQL
If your column is nullable, but you can't figure out how to pass a null value, try something like (you might have to tweak it, I'm at work and can't test rn):
cmd.Parameters.AddWithValue("#claim_no", debtors.claim_no ?? DBNull.Value);
Also, I VERY (VERY!) highly recommend using Dapper, so you can just list all the parameters you want in new { ID = debtors.ID, claim= . . . } and so on. I usually avoid NuGet Packages, but I consider this one pretty essential if you're doing any database work. I use many dozens of queries in my current site, and Dapper has saved me probably hours of my precious time.
i have selectList with multiple attr which is filled through cascading(on select a company i just show selected Company Models) Now i want to allow user to select multiple models such that on select, item should add in c# list and display in page and then allow user to select more of any other company.Picture attached
Following is my code.
OrderViewModel
public class OrderViewModel
{
[Display(Name ="Order ID")]
public int order_id { get; set; }
[Required]
public string cus_name { get; set; }
public string cus_phone { get; set; }
public System.DateTime Date { get; set; }
[DataType(DataType.Date)]
public System.DateTime Date { get; set; }
public int Amount { get; set; }
public List<Products> Products { get; set; }
}
i want to bind selected Item in 'Products' List of OrderViewModel which will be send to server with Further fields.
Products
public class Products
{
public int id { get; set; }
public int modelId { get; set; }
public int Phoneid { get; set; }
public int Quantity { get; set; }
public double price { get; set; }
public bool isSelected { get; set; }
public int order_id { get; set; }
}
Razor View
<div class="form-group row">
<label class="control-label col-6">Company Name</label>
<div class="col-12">
<select id="CompanyId" class="custom-select mr-sm-2"
asp-items="#(new SelectList( #ViewBag.Companies,"Phoneid","Com_name"))">
<option value="">Please Select</option>
</select>
</div>
<span class="text-danger"></span>
</div>
<div class="form-group row">
<label class="control-label col-6"></label>
<div class="col-12">
<select id="modelId" multiple class="custom-select mr-sm-2"
asp-items="#(new SelectList(string.Empty,"modelId","model_name","--Select--"))">
<option value="">Please Select</option>
</select>
</div>
<span class="text-danger"></span>
</div>
what i have tried yet to add item in list
<script>
$("#modelId").change(function () {
var list = #(Model.Products);
let item = $(this).children("option:selected").val();
list.forEach(x => {
if (x.modelId != item) {
#{
Products products = new Products()
{
isSelected=true,
modelId= item,
};
Model.Products.Add(products);
}
}
});
})
#for (int i = 0; i < Model.Products.Count; i++)
{
}
</script>
I display all selected product throught partial view now i just want to send these selected products along with Quanity and Price of each to Server
Here is a working demo like below:
Model:
public class Model
{
[Key]
public int modelId { get; set; }
[Display(Name = "Model Name")]
public string model_name { get; set; }
public int Phoneid { get; set; }
public IList<Products> Products { get; set; }
}
public class Company
{
[Key]
public int Phoneid { get; set; }
[Display(Name = "Company Name")]
public string Com_name { get; set; }
}
public class Products
{
public int id { get; set; }
public int modelId { get; set; }
public int Phoneid { get; set; }
public int Quantity { get; set; }
public double price { get; set; }
public bool isSelected { get; set; }
public int order_id { get; set; }
}
View(Index.cshtml):
#model Products
<div>
<div style="float:left;width:40%">
<form id="form">
<div class="form-group row">
<label>Company Name</label>
<div class="col-12">
<select id="CompanyId" asp-for="Phoneid" class="custom-select mr-sm-2"
asp-items="#(new SelectList( #ViewBag.Companies,"Phoneid","Com_name"))">
<option value="">Please Select</option>
</select>
</div>
</div>
<div class="form-group row">
<label>Model Name</label>
<div class="col-12">
<select id="modelId" multiple class="custom-select mr-sm-2" name="modelId"
asp-items="#(new SelectList(string.Empty,"modelId","model_name","--Select--"))">
<option value="">Please Select</option>
</select>
</div>
</div>
<div>
<input type="button" id="saveBtn" value="Save" />
</div>
</form>
</div>
<div style="float:right;width:60%">
<h5>Products</h5>
<div id="products"></div>
</div>
</div>
#section Scripts
{
<script>
$(function () {
$('#CompanyId').change(function () {
var data = $("#CompanyId").val();
console.log(data);
$.ajax({
url: '/Home/GetModel?Phoneid=' + $("#CompanyId").val(),
type: 'Get',
success: function (data) {
var items = "<option value='0'>Select</option>";
$.each(data, function (i, item) {
items += "<option value='" + item.value + "'>" + item.text + "</option>";
});
$('#modelId').html(items);
}
})
});
$('#saveBtn').click(function () {
$.ajax({
url: '/Home/GetProduct?Phoneid=' + $("#CompanyId").val() + "&modelId=" + $('#modelId').val(),
type: 'Post',
success: function (data) {
$('#products').html(data);
}
})
})
})
</script>
}
Partial View(_Partial.cshtml):
#model IEnumerable<Products>
<table class="table">
<tbody>
#foreach (var item in Model)
{
<tr>
<td>check</td>
<td>
<input asp-for="#item.isSelected" />
</td>
<td>Product Id</td>
<td>
#Html.DisplayFor(modelItem => item.id)
</td>
</tr>
<tr>
<td>Quantity</td>
<td>
#Html.DisplayFor(modelItem => item.Quantity)
</td>
<td>Price</td>
<td>
#Html.DisplayFor(modelItem => item.price)
</td>
</tr>
}
</tbody>
</table>
Controller:
public class HomeController : Controller
{
private readonly MvcProj3Context _context;
public HomeController(MvcProj3Context context)
{
_context = context;
}
public IActionResult Index()
{
ViewBag.Companies = _context.Company.ToList();
return View();
}
public JsonResult GetModel(int Phoneid)
{
List<Model> model = new List<Model>();
model = (from m in _context.Model
where m.Phoneid == Phoneid
select m).ToList();
return Json(new SelectList(model, "modelId", "model_name"));
}
[HttpPost]
public IActionResult GetProduct(int Phoneid, string[] modelId)
{
var data = new List<Products>();
var ids = modelId[0].Split(',');
foreach(var item in ids)
{
var id = int.Parse(item);
//guess the modelA in CompanyA contains several products
var product = (from p in _context.Products
where p.Phoneid == Phoneid && p.modelId == id
select p).ToList();
foreach (var pro in product)
{
data.Add(pro);
}
}
return PartialView("_Partial", data);
}
}
Result:
I have a form that looks like this:
For each row of achievements I have two properties in my viewmodel and 2 inputs in my html:
<table id="submissionTable">
<tr>
<td><b>Place</b></td>
<td></td>
<td><b>Event</b></td>
</tr>
<tr id="tablerow0">
<td width="135"><input type="number" min="1" asp-for="AchievementsRank1" class="form-control" /></td>
<td width="20"><span></span></td>
<td width="590"><select asp-for="event1" asp-items="#Html.GetEnumSelectList<Events>()" class="form-control"></select></td>
</tr>
<tr id="tablerow1">
<td width="135"><input type="number" min="1" asp-for="AchievementsRank2" class="form-control" /></td>
<td width="20"><span></span></td>
<td width="590"><select asp-for="event2" asp-items="#Html.GetEnumSelectList<Events>()" class="form-control"></select></td>
</tr>
<tr id="tablerow2">
<td width="135"><input type="number" min="1" asp-for="AchievementsRank3" class="form-control" /></td>
<td width="20"><span></span></td>
<td width="590"><select asp-for="event3" asp-items="#Html.GetEnumSelectList<Events>()" class="form-control"></select></td>
</tr>
</table>
[Display(Name = "Achievements")]
[Required(ErrorMessage = "Atleast one achievement is required.")]
public int AchievementsRank1 { get; set; }
[Required(ErrorMessage = "Atleast one achievement is required.")]
public Events event1 { get; set; }
public int? AchievementsRank2 { get; set; }
public Events event2 { get; set; }
public int? AchievementsRank3 { get; set; }
public Events event3 { get; set; }
But what if let's say I'd want 10 or more rows of achievements? I would have to add all those properties and inputs for each row... There must be an easier way to do this I just don't know what to look for.
Of course you can create a class that represent you 2 datas association (rank / event). Then in your viewmodel you can create a List of this items, initialized with 3 empty instance. Then with some javascript you could try to add (and remove) rows to your html with a plus and minus icon with some Html.IdFor(m => m...). You'll prefer to use a for instead of a foreach.
I hope I have been clear enough about this.
You may need to modify your model to something like this:
public class Profile
{
public Profile()
{
Achievements = new List<Achievement>
{
// initialize 3 achievements
new Achievement(),
new Achievement(),
new Achievement()
};
}
public string Username { get; set; }
public List<Achievement> Achievements { get; set; }
}
public class Achievement
{
public int Rank { get; set; }
public int EventId { get; set; }
}
and your view should be like this:
<input asp-for="Username" type="text" />
#foreach (var achievement in Model.Achievements)
{
int index = Model.Achievements.IndexOf(achievement);
<input asp-for="achievements[index].Rank" type="text" />
<select asp-for="achievements[index].EventId" asp-items="#Html.GetEnumSelectList<Events>()" class="form-control">
<option></option>
</select>
}
CustomCssFields is null when going back to controller. I already set a constructor but it is still the same. I found some similar question MVC Model with a list of objects as property but it almost has the same code as mine.
MODEL
public class CompanyModelView
{
public CompanyModelView()
{
CustomCssFields = new List<CompanyCssReferenceModelView>();
}
[BsonId]
public string _id { get; set; }
[BsonElement("CompanyName")]
[DisplayName("Company Name")]
public string CompanyName { get; set; }
[BsonElement("CompanyCode")]
[DisplayName("Company Code")]
public string CompanyCode { get; set; }
[BsonElement("CompanyConnectionString")]
[DisplayName("Company Connection String")]
public string CompanyConnectionString { get; set; }
[BsonElement("CargowiseVersion")]
[DisplayName("Cargowise Version")]
public string CargoWiseVersion { get; set; }
[BsonElement("CustomCssFields")]
[UIHint("CustomCssFields")]
public IList<CompanyCssReferenceModelView> CustomCssFields { get; set; }
}
public class CompanyCssReferenceModelView
{
[BsonElement("FieldName")]
public CssOptionEnum FieldName;
[BsonElement("FieldValue")]
public string FieldValue;
}
VIEW
here is the view.
#model WebTrackerModels.CompanyModels.CompanyModelView
<form asp-action="SaveCompany" enctype="multipart/form-data">
<div class="form-group">
<table class="table">
<thead>
<tr>
<th>
Field Name
</th>
<th>
Field Value
</th>
</tr>
</thead>
<tbody>
#{
for (int i = 0; i < Model.CustomCssFields.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(model => model.CustomCssFields[i].FieldName)
</td>
<td>
#Html.TextBoxFor(model => model.CustomCssFields[i].FieldValue, new { #class = "form-control", type = "color", value = Model.CustomCssFields[i].FieldValue })
</td>
</tr>
}
}
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-success" />
</div>
</form>
I have passed the model to partial view,And then i need to bind some text fields to the model in the view.
#model SRINews.Domain.NewsTotal
#using (Html.BeginForm("UpdateNewsItem", "Home", FormMethod.Post))
{
<table class="table table-borderless table-cart" id="mytable" data-addclass-on-smdown="table-sm">
<tbody>
#foreach (var item in Model.items)
{
<tr class="newsRow" id="#item.ItemId">
<td class="cart-img nostretch">
<img src="#item.ImageUrl" alt="">
</td>
</tr>
<tr>
<td>
<input type="text" class="form-control" placeholder="Personalized Name">
//#Html.TextboxFor(x=>x)
// I want to bind PersonalizedName to model
</td>
</tr>
<tr>
<td>
<input type="text" class="form-control" placeholder="Country">
// I want to bind Country to model
</td>
</tr>
}
</tbody>
</table>
<input type="submit" class="btn btn-primary" value="Personal Details" />
}
Model
public class Items
{
public int ItemId { get; set; }
public string ItemCode { get; set; }
public string PersonalizedName {get;set;}
public string Country {get;set;}
}
public class NewsTotal
{
public int BaseItem { get; set; }
public string BaseName {get;set;}
public List<Items> items { get; } = new List<Items>();
}
Public ActionResult UpdateNewsItem(NewsTotal nTotal)
{
return View();
}
You want to use a traditional for loop so you can use the index to bind to your List<T> in the model, you'll also need to make items mutable, so you'll need to have a set for it as well or else you won't be able to submit anything:
//You'll need to make this mutable, so it can post the edited values
public List<Items> items { get; set; } = new List<Items>();
Then in your View:
#for(int i = 0; i < Model.items.Count; i++)
{
#Html.HiddenFor(x => Model.items[i].ItemId)
#Html.HiddenFor(x => Model.items[i].ItemCode)
<tr class="shoppingCartRow" id="#Model.items[i].ItemId">
<td class="cart-img nostretch">
<img src="#Model.items[i].ImageUrl" alt="">
</td>
</tr>
<tr>
<td>
#Html.TextboxFor(x=> Model.items[i].PersonalizedName, new { #placeholder = "Personalized Name"})
</td>
</tr>
<tr>
<td>
#Html.TextboxFor(x=> Model.items[i].Country, new { #placeholder = "Country"})
</td>
</tr>
}