blazorstrap InputType.select not binding - asp.net-core

trying to create a very basic form with blazorstrap.
#page "/testform"
<h1> test form #Status</h1>
<EditForm Model=#Person OnSubmit=#FormSubmitted>
<DataAnnotationsValidator />
<ValidationSummary />
<p>
<label>
name:
<BSBasicInput T="string"
InputType="InputType.Text"
#bind-value=Person.Name />
</label>
</p>
<p>
<label>
Age:
<InputNumber #bind-Value="Person.Age" />
</label>
</p>
<p>
<label>
type:
<InputSelect #bind-Value="Person.Type">
<option value="">Select classification ...</option>
#foreach (var enumType in Enum.GetValues(typeof(BankAccountType)))
{
<option value="#enumType">#enumType</option>
}
</InputSelect>
#* <BSBasicInput T="BankAccountType?" InputType="InputType.Select" #bind-Value="Person.Type"> *#
#* <option value="">Select classification ...</option> *#
#* #foreach (var enumType in Enum.GetValues(typeof(BankAccountType))) *#
#* { *#
#* <option value="#enumType">#enumType</option> *#
#* } *#
#* </BSBasicInput> *#
</label>
</p>
<input type="submit" value="Submit" class="btn btn-primary" />
</EditForm>
#code
{
string Status = "Not submitted";
Person Person = new Person();
BankAccountType? AccountType = null;
void FormSubmitted()
{
Status = "Form submitted";
Console.WriteLine($"person: {Person} + " + AccountType);
}
private void SelectedChanged(BankAccountType? value)
{
Console.WriteLine("changed " + value);
}
}
the person class is
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public BankAccountType? Type { get; set; }
public override string ToString()
{
var accountType = Type.HasValue ? Type.Value.ToString() : "";
return $"{Name} is {Age} years old {accountType}";
}
}
The name binding with BSBasicInput works fine. using InputSelect for the select works fine. but when I try and use BSBasicInput with the InputType.Select (see commented out part). I cannot get it to bind, or at least show it is ever changing or setting either Person.Type or when I directly create a property in the razor file for it.
Sure I must be doing something wrong here, and its not blazorstrap, but can't see what it is now

<BSBasicInput T="BankAccountType" InputType="InputType.Select" #bind-Value="Person.Type">
<option value="">Select classification ...</option>
#foreach (var enumType in Enum.GetValues(typeof(BankAccountType)))
{
<option value="#enumType">#enumType</option>
}
</BSBasicInput>
This works. It seems to be that BSBasicInput cannot handle nullable enums

Related

model property is not passed to controller action

What is my mistake? AllFindedBrands property is not passing to SearchBrandResult Action of Controller
Controller:
public async Task<IActionResult> Search(string Articul, int idClient)
{
List<BrandList> findedBrands = new List<BrandList>();
#ViewBag.list = woDupes;
SearchViewModel model = new SearchViewModel();
model.Articul = Articul;
model.idClient = idClient;
model.AllFindedBrands = new List<BrandList>(findedBrands);
return View(model);
}
[HttpPost]
public async Task<IActionResult> SearchBrandResult(SearchViewModel model)
{
return View();
}
View:
<form asp-controller="Search" asp-action="SearchBrandResult" asp-route-
Articul="#Model.Articul"
asp-route-AllFindedBrands="#Model.AllFindedBrands" asp-route-
idClient="#Model.idClient" method="post" enctype="multipart/form-data">
<select asp-for="SelectedBrand" asp-items="#(new SelectList(#ViewBag.list,
nameof(FindedBrand.Name),
nameof(FindedBrand.Name)))"
multiple="true" class="form-control brand-chosen">
</select>
<input type="submit" />
All other properties of ViewModel is successfully passed to th Action
AllFindedBrands is type of complex model and asp-route-* cannot dynamic bind the value. You can F12 in the browser to check the generated url in the form.
Two ways you can follow:
1.By using asp-all-route-data and foreach the AllFindedBrands to bind the value which passes the value by route data.
Assume your model like below:
public class SearchViewModel
{
public string Articul { get; set; }
public string idClient { get; set; }
public List<BrandList> AllFindedBrands { get; set; }
public List<string> SelectedBrand { get; set; }
}
public class BrandList
{
public string Name { get; set; }
}
View(For easy testing, I just hard-coded the dropdownlist):
#model SearchViewModel
#{
var data = new Dictionary<string, string>();
for(int i=0;i<Model.AllFindedBrands.Count();i++)
{
data.Add("AllFindedBrands[" + i + "].Name", Model.AllFindedBrands[i].Name);
}
}
<form asp-action="SearchBrandResult" asp-route-Articul="#Model.Articul" asp-all-route-data="#data" asp-route-idClient="#Model.idClient" method="post" enctype="multipart/form-data">
<select asp-for="SelectedBrand" multiple="true" class="form-control brand-chosen">
<option value="aaa">aaa</option>
<option value="bbb">bbb</option>
<option value="ccc">ccc</option>
</select>
<input type="submit" />
</form>
2.By listing the property and make them hidden inputs which passes the value by form data:
#model SearchViewModel
<form asp-action="SearchBrandResult" asp-route-Articul="#Model.Articul" asp-route-idClient="#Model.idClient" method="post" enctype="multipart/form-data">
#for (int i = 0; i < Model.AllFindedBrands.Count(); i++)
{
<input asp-for="#Model.AllFindedBrands[i].Name" hidden />
}
<select asp-for="SelectedBrand" multiple="true" class="form-control brand-chosen">
<option value="aaa">aaa</option>
<option value="bbb">bbb</option>
<option value="ccc">ccc</option>
</select>
<input type="submit" />
</form>

How to bind input fields to two different models?

I am building a Book List application. It has the following models:
Book (int Id, string Title, string Type, int MinimumAge) [Id=Key, Title=Required, Type=Required, MinimumAge=Required]
Genre (int Id, string Name) [Master table]
BookGenre (int BookId, int GenreId) [Keyless Entity]
CreateViewModel (Book Book, IEnumerable Genres)
Right now I am working on the CREATE operation.
CONTROLLER code (BookController.cs)
The Create() POST methods of this Controller is incomplete.
using BookList.Data;
using BookList.Models;
using Microsoft.AspNetCore.Mvc;
namespace BookList.Controllers
{
public class BookController : Controller
{
private readonly ApplicationDbContext db;
public BookController(ApplicationDbContext db)
{
this.db = db;
}
// READ (Get)
public IActionResult Index()
{
IEnumerable<Book> bookList = db.Books;
return View(bookList);
}
// CREATE (Get)
public IActionResult Create()
{
// Create custom view model
CreateViewModel model = new CreateViewModel();
model.Book = new Book();
model.Genre = new Genre();
return View(model);
}
// CREATE (Post)
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(CreateViewModel obj)
{
// Write your code here
return RedirectToAction("Index");
}
}
}
VIEW Code (Create.cshtml):
#model CreateViewModel
<div class="row">
<div class="border mt-4">
<div class="row py-2">
<h2 class="text-primary">Add a New Book</h2>
<hr />
</div>
<form asp-action="Create">
<div asp-validation-summary="All" class="text-danger"></div>
#* Title *#
<div class="form-group mb-2">
<label asp-for="Book.Title"></label><br />
<input asp-for="Book.Title" class="form-control" />
<span asp-validation-for="Book.Title" class="text-danger"></span>
</div>
#* Genre *#
<div class="form-group mb-2">
<label>Genre</label><br/>
<div>
#foreach(var genre in Model.Genres)
{
<label for=#genre.Id>#genre.Name</label>
<input type="checkbox" id=#genre.Id value=#genre.Name />
}
</div>
</div>
#* Type(Fiction/Non-Fiction) *#
<div class="form-group mb-2">
<label asp-for="Book.Type"></label><br />
<div>
Fiction <input type="radio" asp-for="Book.Type" value="Fiction" />
Non-Fiction <input type="radio" asp-for="Book.Type" value="Non-Fiction"/>
</div>
<span asp-validation-for="Book.Type" class="text-danger"></span>
</div>
#* Minimum Age(dropdown) *#
<div class="form-group mb-2">
<label asp-for="Book.MinimumAge" class="control-label"></label>
<select asp-for="Book.MinimumAge" class="form-control">
<option value=8>8</option>
<option value=12>12</option>
<option value=16>16</option>
<option value=18>18</option>
</select>
<span asp-validation-for="Book.MinimumAge" class="text-danger"></span>
</div>
<div class="form-group mb-2">
<input type="submit" value="Add" class="btn btn-primary" />
</div>
</form>
</div>
</div>
#*Cient-side validation scripts*#
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Let's say the user enters the following details and clicks Create:
Title="XYZ", Genres="Action,Adventure", Type="Fiction", Minimum Age=12
Then, I want (auto-id, "XYZ","Fiction",12) to go into the Book table. And, (auto-id,1) and (auto-id,2) to go into the BookGenre table.
For your reference, the Genre master table contains the following details. And BookGenre table is a Keyless entity.
You need firstly know that model binding system bind data by name attribute.
From the view design I can see your CreateViewModel contains IEnumerable<Genre> Genres, so the frontend should add name like:Genres[index].PropertyName. But then you will find a problem that if you want to choose discontinuous checkbox, you will receive only continuous value and miss the discontinuous ones.
So suggest you also create a property List<string> GenresList and add name="GenresList" in your frontend.
Here is a whole working demo:
Model:
public class Book
{
public int Id { get; set; }
public int MininumAge { get; set; }
public string Title { get; set; }
public string Type { get; set; }
public ICollection<Genre>? Genres { get; set; }
}
public class Genre
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Book>? Books { get; set; }
}
public class CreateViewModel
{
public Book Book { get; set; }
public List<Genre>? Genres { get; set; }
public List<string> GenresList { get; set; }
}
View:
#model CreateViewModel
<div class="row">
<div class="border mt-4">
<div class="row py-2">
<h2 class="text-primary">Add a New Book</h2>
<hr />
</div>
<form asp-action="Create">
<div asp-validation-summary="All" class="text-danger"></div>
#* Title *#
<div class="form-group mb-2">
<label asp-for="Book.Title"></label><br />
<input asp-for="Book.Title" class="form-control" />
<span asp-validation-for="Book.Title" class="text-danger"></span>
</div>
#* Genre *#
<div class="form-group mb-2">
<label>Genre</label><br/>
<div>
#foreach(var genre in Model.Genres)
{
<label for=#genre.Id>#genre.Name</label> #* add name here*#
<input type="checkbox" id=#genre.Id value=#genre.Name name="GenresList"/>
}
</div>
</div>
#* Type(Fiction/Non-Fiction) *#
<div class="form-group mb-2">
<label asp-for="Book.Type"></label><br />
<div>
Fiction <input type="radio" asp-for="Book.Type" value="Fiction" />
Non-Fiction <input type="radio" asp-for="Book.Type" value="Non-Fiction"/>
</div>
<span asp-validation-for="Book.Type" class="text-danger"></span>
</div>
#* Minimum Age(dropdown) *#
<div class="form-group mb-2">
<label asp-for="Book.MininumAge" class="control-label"></label>
<select asp-for="Book.MininumAge" class="form-control">
<option value=8>8</option>
<option value=12>12</option>
<option value=16>16</option>
<option value=18>18</option>
</select>
<span asp-validation-for="Book.MininumAge" class="text-danger"></span>
</div>
<div class="form-group mb-2">
<input type="submit" value="Add" class="btn btn-primary" />
</div>
</form>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Controller:
public class BooksController : Controller
{
private readonly MvcProj6_0Context _context;
public BooksController(MvcProj6_0Context context)
{
_context = context;
}
// GET: Books/Create
public IActionResult Create()
{
CreateViewModel model = new CreateViewModel();
model.Book = new Book();
model.Genres = _context.Genre.ToList();
return View(model);
}
// POST: Books/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(CreateViewModel obj)
{
if (ModelState.IsValid)
{
var genres = new List<Genre>();
foreach (var item in obj.GenresList)
{
genres.Add(_context.Genre.Where(a => a.Name == item).First());
}
obj.Book.Genres = genres;
_context.Add(obj.Book);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(obj);
}
}
Note:
In .NET 6, it includes the <Nullable>enable</Nullable> element in the project file which makes the property non-nullable. The non-nullable property must be required, otherwise the ModelState will be invalid. You can use ? or initialize the property in the model design to skip the required validation.

.NET Core Razor page Dropdown not returning a value

I'm very new to .net core 5 and razor pages and trying to build a search form to display results but am unable to get the values from two specific dropdowns(always return a 1 or null, depending on the model definition). Here is the HTML for the search controls:
<form method="post">
<div class="container-fluid">
<div class="row">
<div class="col-sm-2 vh-100" style="background-color:lightgray;">
<div class="row">
<div class="colvertspc">
<label>Begin Date</label>
<input type="date" class="form-control" name="begindate" id="begindate" asp-for="paramsearch.BeginDate" />
</div>
</div>
<div class="row">
<div class="colvertspc">
<label>End Date</label>
<input type="date" class="form-control" name="enddate" id="enddate" asp-for="paramsearch.EndDate" />
</div>
</div>
<div class="row">
<div class="colvertspc">
<label>Access Rule Group</label>
<div>
<select name="accessrulegroup" asp-items="Model.accessgrouplist" class="form-control" asp-for="paramsearch.AccessRuleGroupCode">
<option value="-1">Select</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="colvertspc">
<label>Access Rule Category</label>
<div>
<select name="accessrulecategory" asp-items="Model.accesscategorylist" class="form-control" asp-for="paramsearch.AccessRuleCategoryCode">
<option value="-1">Select</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="colvertspc">
<label>Sort Order</label>
<div>
<select name="sortorder" class="form-control" asp-for="paramsearch.SortOrder">
<option value="1">ASC</option>
<option value="2">DESC</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="colvertspc">
<button type="submit" class="btn btn-success">SEARCH</button>
</div>
</div>
Here is the code for the PageModel to bind and retrieve values:
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
//dropdowns
public SelectList accessgrouplist { get; set; }
public SelectList accesscategorylist { get; set; }
//parameters from left side panel, for search
[BindProperty]
public SearchParametersModel paramsearch { get; set; }
public void OnGet()
{
//bind search dropdowns
this.accessgrouplist = new SelectList(PopulateAcessGroups(), "AccessRuleGroupCode", "AccessRuleGroup");
this.accesscategorylist = new SelectList(PopulateAcessCategories(), "AccessRuleCategoryCode", "AccessRuleCategory");
}
public IActionResult OnPost()
{
//check validators
if (ModelState.IsValid == false)
{
return Page();
}
//get SEARCH parameters as test
var ww = paramsearch.BeginDate;
var www = paramsearch.AccessRuleGroupCode;
var ww1w = paramsearch.AccessRuleCategoryCode;
var ww2w = paramsearch.SortOrder;
return RedirectToPage("/Index");
}
//hard coded dropdown data, will be replaced by database stuff
private static List<AccessRuleGroupModel> PopulateAcessGroups()
{
List<AccessRuleGroupModel> groups = new List<AccessRuleGroupModel>();
groups.Add(new AccessRuleGroupModel { AccessRuleGroupCode = 1, AccessRuleGroup = "Group 1" });
groups.Add(new AccessRuleGroupModel { AccessRuleGroupCode = 2, AccessRuleGroup = "Group 2" });
groups.Add(new AccessRuleGroupModel { AccessRuleGroupCode = 3, AccessRuleGroup = "Group 3" });
return groups;
}
And a couple of the class definitions:
public class AccessRuleGroupModel
{
[Key]
public int AccessRuleGroupCode { get; set; }
public string AccessRuleGroup { get; set; }
}
public class SearchParametersModel
{
public DateTime BeginDate { get; set; }
public DateTime EndDate { get; set; }
public string AccessRuleCategoryCode { get; set; }
public string AccessRuleGroupCode { get; set; }
public string SortOrder { get; set; }
}
Really hoping someone can point out what I'm missing.
Thanks in advance ,
Jim W
Asp.Net Core binds model data based on the name attribute. Use a tag helper so that the generated name attributes of dropdowns ensures that the data will be bound to paramsearch.
<div class="row">
<div class="colvertspc">
<label>Access Rule Group</label>
<div>
<select asp-for="paramsearch.AccessRuleGroupCode" asp-items="Model.accessgrouplist" class="form-control" >
<option value="-1">Select</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="colvertspc">
<label>Access Rule Category</label>
<div>
<select asp-for="paramsearch.AccessRuleCategoryCode" asp-items="Model.accesscategorylist" class="form-control">
<option value="-1">Select</option>
</select>
</div>
</div>
</div>

How can I bind a model to dropdownlist input with capability of filter of results?

I want to design a form in asp.net core, that has a dropdownlist. I want to bind a model to this dropdown that all of column of this model show in dropdown list, and user can select a row, and filter results.
Is there this capability in asp.net core or css or js?
It's my Model: Company.cs
public class Company
{
public int Id { get; set; }
public string CompanyCode { get; set; }
public string CompanyName { get; set; }
public string SubCompanyName { get; set; }
}
It's my form view: Insert.cshtml
<form asp-action="Insert">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="ErrorReportCode" class="form-control" , placeholder="RequestCode" id="txtErrorReportCode" />
<span asp-validation-for="ErrorReportCode" class="text-danger"></span>
</div>
<div class="form-group">
<input asp-for="ErrorReportDateReport" class="form-control datePicker" type="text" id="txtErrorReportDateReport" placeholder="RequestDate" />
<span asp-validation-for="ErrorReportDateReport" class="text-danger"></span>
</div>
<div class="form-group">
#Html.DropDownListFor< ????? >
</div>
<div class="form-group">
<input type="submit" value="Insert" class="btn-red pull-left" />
</div>
</form>
I want to have a drop down list in my form, that user can select a company according to its specification like CompanyCode, CompanyName and SubcompanyName. In other words I want a DropDownList with multi column.
Also, when user typed one word in text Area of this dropdown, display results in Company table change dynamically. In fact, data will filter.
Firstly,dropdownlist value cannot bind a model.I think bind Id of Company is a good choice.If you want to show more than one columns,you can put them together in option value.
Here is a demo(I use fake data to test):
Action
public IActionResult Insert() {
List<Company> l = new List<Company> { new Company { Id=1, CompanyCode="c1", CompanyName="company1", SubCompanyName="subcompany1"},
new Company { Id = 2, CompanyCode = "c2", CompanyName = "company2", SubCompanyName = "subcompany2" },
new Company { Id=3, CompanyCode="c3", CompanyName="company3", SubCompanyName="subcompany3"}};
List<SelectListItem> l1 = new List<SelectListItem>();
foreach(Company c in l) {
l1.Add(new SelectListItem { Text = c.CompanyCode + " | " + c.CompanyName + " | " + c.SubCompanyName, Value = c.Id + "" });
}
ViewBag.Select = l1;
return View();
}
View:
<input type="text" list="cars" />
<datalist id="cars">
#foreach (var i in ViewBag.Select)
{
<option id=#i.Value>#i.Text</option>
}
</datalist>
result:
If you prefer better style,you can look for some 3rd plugin.
Update(add <option>CompanyCode | CompanyName | SubCompanyName </option> in datalist):
<input type="text" list="cars" />
<datalist id="cars">
<option>CompanyCode | CompanyName | SubCompanyName </option>
#foreach (var i in ViewBag.Select)
{
<option id=#i.Value>#i.Text</option>
}
</datalist>
result:
update2:
you can also use placeholder:
view:
<input type="text" list="cars" placeholder="CompanyCode | CompanyName | SubCompanyName" style="width:400px"/>
<datalist id="cars">
#foreach (var i in ViewBag.Select)
{
<option id=#i.Value>#i.Text</option>
}
</datalist>
result:

Trouble with Request.Form inside .cshtml

Trying to create a page that will have a drop down selector with three image names and when an image name is selected and you hit submit it will then display that image on the page.
I found an example of this here(Seems to be outdated): https://learn.microsoft.com/en-us/aspnet/web-pages/overview/ui-layouts-and-themes/9-working-with-images
has: if(Request["photoChoice"] != null)
Read somewhere that the correction is Request.Form
#{ var imagePath = "";
if (Request.Form["photoChoice"] != null)
{
imagePath = #"images\" + Request.Form["photoChoice"];
}
}
<form method="post" action="">
<div>
I want to see:
<select name="photoChoice">
<option value="Photo1.jpg">Photo 1</option>
<option value="Photo2.jpg">Photo 2</option>
<option value="Photo3.jpg">Photo 3</option>
</select>
<input type="submit" value="Submit" />
</div>
<div style="padding:10px;">
#if (imagePath != "")
{
<img src="#imagePath" alt="Sample Image" width="300" />
}
</div>
</form>
The first error I was having was:
" Operator '!=' is ambiguous on operands of type 'StringValues' and 'null' "
adding (object) at the start of the Request there in the if statement
#{ var imagePath = "";
if ((object)Request.Form["photoChoice"] != null)
{
imagePath = #"images\" + Request.Form["photoChoice"];
}
Now I am getting another error "InvalidOperationException: Incorrect Content-Type" when I try to compile the site. It does refer to the If line of code
The link you refer to is used in asp.net, not in core.
The main reason for the error is that you put the request.Form in the
wrong place. Your current requirements should put the code
into the OnPost method in the code behind.
There are many ways to implement this function in the core, but they need to be triggered in the post method in the code behind.
Please refer to this.
The simplest way is to bind fields in the code behind. Please refer to the following for details.
Page.cs:
public class ShowImagesModel : PageModel
{
[BindProperty]
public string imagePath { get; set; }
[BindProperty]
public string photoChoice { get; set; }
public void OnGet()
{
imagePath = "";
}
public void OnPost()
{
if (!string.IsNullOrEmpty(photoChoice))
{
imagePath = #"images\" + photoChoice;
}
}
}
View:
#page
#model WebApplication1_razor_page.ShowImagesModel
#{
ViewData["Title"] = "ShowImages";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<h1>ShowImages</h1>
<form method="post" action="">
<div>
I want to see:
<select asp-for="photoChoice" >
<option value="Photo1.jpg">Photo 1</option>
<option value="Photo2.jpg">Photo 2</option>
<option value="Photo3.jpg">Photo 3</option>
</select>
<input type="submit" value="Submit" />
</div>
<div style="padding:10px;">
#if (Model.imagePath != "")
{
<img src="#Model.imagePath" alt="Sample Image" width="300" />
}
</div>
</form>
Here is the result: