model binders for complex types - asp.net

I have following model:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address BillingAddress { get; set; }
public Address ShippingAddress { get; set; }
}
where Address is:
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
}
I want to create a form/view, I have created the view :
Create
<div>
<label>First Name</label>
<input name="FirstName" />
</div>
<div>
<label>Last Name</label>
<input name="LastName" />
</div>
<div>
<label>Billing Address</label>
<input name="BillingAddress" />
</div>
<div>
<label>Shipping Address</label>
<input name="ShippingAddress" />
</div>
<div>
<input type="submit" value="Save" />
</div>
</form>
I want to show full address, I mean city and street. Do I need to create partial view or what? I need to post this view to following actionResult:
[HttpPost]
public ActionResult Create(Customer customerToCreate)
{
return View();
}
Please suggest solution

Simple solution, override ToString() on Address:
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
public override ToString()
{
return string.Format("{0} {1}",Street,City);
}
}
And in your view:
#model Customer
<div>
<label>First Name</label>
#Html.TextBoxFor<Customer,string>(m => m.Firstname)
</div>
<div>
<label>Last Name</label>
#Html.TextBoxFor<Customer,string>(m => m.Firstname)
</div>
<div>
<label>Billing Address</label>
#Html.TextBoxFor<Customer,string>(m => m.BillingAddress)
</div>
<div>
<label>Shipping Address</label>
#Html.TextBoxFor<Customer,string>(m => m.ShippingAddress)
<input name="ShippingAddress" />
</div>
<div>
<input type="submit" value="Save" />
</div>

create strongly typed view. type of Customer.

Related

asp.net Core - ModelState.IsValid is requiring fields from other tables even though they are not required in the DB

When I attempt to create a "TblCompanyInfo" record I am getting an error that "The TblOutdialSetting field is required.". I assume it has to do with the foreign key that exists on TblCompanyInfo. However, this key should only require a matching CompanyID from TblCompanyInfo when you create a new record in TblOutdialSettings. I am not trying to create a new record for TblOutdialSettings, only TblCompanyInfo.
Here is my foreign key
Here is my controller
// GET: Company/Create
public IActionResult Create()
{
TblCompanyInfo tblcompanyinfo = new TblCompanyInfo();
return View(tblcompanyinfo);
}
// POST: Company/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("CompanyId,Username,Password,CompanyName,ContactFirstName,ContactLastName,ContactPriPhone,ContactPriPhoneExt,ContactSecPhone,ContactSecPhoneExt,ContactEmail,Address1,Address2,City,State,Zipcode,CompanyPhone,LogoFile,Htmlcolor,CompanyUrl,CompanyEmail,AgreeToTou,AuthNetcustomerProfileId,AuthNetcustomerPaymentProfileId,PaymentInfoValid,DateTimeSignedUp,Active,TestMode,BizAcctFreeRemCount,AcctTypeId,InTrial,CompanyStatusId,OnHold,OnHoldReasonId,OnHoldStartDate")] TblCompanyInfo tblcompanyinfo)
{
if (ModelState.IsValid)
{
_context.Add(tblcompanyinfo);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
else
{
foreach (var error in ViewData.ModelState.Values.SelectMany(modelState => modelState.Errors))
{
ModelState.AddModelError(string.Empty, error.ErrorMessage);
}
}
return View(tblcompanyinfo);
}
Here is my View
#model AppointmentRemindersNetCoreMVC.Models.TblCompanyInfo
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
#*#Html.ValidationSummary(true)*#
<h4>TblCompanyInfo</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Username" class="control-label"></label>
<input asp-for="Username" class="form-control" />
<span asp-validation-for="Username" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CompanyName" class="control-label"></label>
<input asp-for="CompanyName" class="form-control" />
<span asp-validation-for="CompanyName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ContactFirstName" class="control-label"></label>
<input asp-for="ContactFirstName" class="form-control" />
<span asp-validation-for="ContactFirstName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ContactLastName" class="control-label"></label>
<input asp-for="ContactLastName" class="form-control" />
<span asp-validation-for="ContactLastName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ContactPriPhone" class="control-label"></label>
<input asp-for="ContactPriPhone" class="form-control" />
<span asp-validation-for="ContactPriPhone" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ContactPriPhoneExt" class="control-label"></label>
<input asp-for="ContactPriPhoneExt" class="form-control" />
<span asp-validation-for="ContactPriPhoneExt" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ContactSecPhone" class="control-label"></label>
<input asp-for="ContactSecPhone" class="form-control" />
<span asp-validation-for="ContactSecPhone" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ContactSecPhoneExt" class="control-label"></label>
<input asp-for="ContactSecPhoneExt" class="form-control" />
<span asp-validation-for="ContactSecPhoneExt" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ContactEmail" class="control-label"></label>
<input asp-for="ContactEmail" class="form-control" />
<span asp-validation-for="ContactEmail" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Address1" class="control-label"></label>
<input asp-for="Address1" class="form-control" />
<span asp-validation-for="Address1" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Address2" class="control-label"></label>
<input asp-for="Address2" class="form-control" />
<span asp-validation-for="Address2" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="City" class="control-label"></label>
<input asp-for="City" class="form-control" />
<span asp-validation-for="City" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="State" class="control-label"></label>
<input asp-for="State" class="form-control" />
<span asp-validation-for="State" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Zipcode" class="control-label"></label>
<input asp-for="Zipcode" class="form-control" />
<span asp-validation-for="Zipcode" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CompanyPhone" class="control-label"></label>
<input asp-for="CompanyPhone" class="form-control" />
<span asp-validation-for="CompanyPhone" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="AgreeToTou" /> #Html.DisplayNameFor(model => model.AgreeToTou)
</label>
</div>
#
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Here is my TblCompanyInfo.cs class
using System;
using System.Collections.Generic;
namespace AppointmentRemindersNetCoreMVC.Models
{
public partial class TblCompanyInfo
{
public TblCompanyInfo()
{
TblCalendarData = new HashSet<TblCalendarDatum>();
TblClientVoicefiles = new HashSet<TblClientVoicefile>();
TblCompanyToBillingItems = new HashSet<TblCompanyToBillingItem>();
TblEnglishSpanishMappings = new HashSet<TblEnglishSpanishMapping>();
TblEvents = new HashSet<TblEvent>();
TblImportData = new HashSet<TblImportDatum>();
TblImportLogs = new HashSet<TblImportLog>();
TblNoReminderLists = new HashSet<TblNoReminderList>();
TblReportData = new HashSet<TblReportDatum>();
TblResellerPaymentCustomerCompanies = new HashSet<TblResellerPayment>();
TblResellerPaymentResellerCompanies = new HashSet<TblResellerPayment>();
TblResellerToCompanyCompanies = new HashSet<TblResellerToCompany>();
TblResellerToCompanySalesCompanies = new HashSet<TblResellerToCompany>();
TblRules = new HashSet<TblRule>();
}
public long CompanyId { get; set; }
public string Username { get; set; } = null!;
public string Password { get; set; } = null!;
public string CompanyName { get; set; } = null!;
public string ContactFirstName { get; set; } = null!;
public string ContactLastName { get; set; } = null!;
public string ContactPriPhone { get; set; } = null!;
public string? ContactPriPhoneExt { get; set; }
public string? ContactSecPhone { get; set; }
public string? ContactSecPhoneExt { get; set; }
public string? ContactEmail { get; set; }
public string? Address1 { get; set; }
public string? Address2 { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? Zipcode { get; set; }
public string? CompanyPhone { get; set; }
public string? LogoFile { get; set; }
public string? Htmlcolor { get; set; }
public string? CompanyUrl { get; set; }
public string? CompanyEmail { get; set; }
public bool AgreeToTou { get; set; }
public string? AuthNetcustomerProfileId { get; set; }
public string? AuthNetcustomerPaymentProfileId { get; set; }
public bool PaymentInfoValid { get; set; }
public DateTime DateTimeSignedUp { get; set; }
public bool? Active { get; set; }
public bool TestMode { get; set; }
public int BizAcctFreeRemCount { get; set; }
public int AcctTypeId { get; set; }
public bool? InTrial { get; set; }
public int CompanyStatusId { get; set; }
public bool OnHold { get; set; }
public int OnHoldReasonId { get; set; }
public DateTime OnHoldStartDate { get; set; }
public virtual TblAcctType? AcctType { get; set; } = null!;
public virtual TblCompanyStatusId? CompanyStatus { get; set; } = null!;
public virtual TblOnHoldReason? OnHoldReason { get; set; } = null!;
public virtual TblBillingTypeToCompany? TblBillingTypeToCompany { get; set; } = null!;
public virtual TblOutdialSetting? TblOutdialSetting { get; set; } = null!;
public virtual ICollection<TblCalendarDatum> TblCalendarData { get; set; }
public virtual ICollection<TblClientVoicefile> TblClientVoicefiles { get; set; }
public virtual ICollection<TblCompanyToBillingItem> TblCompanyToBillingItems { get; set; }
public virtual ICollection<TblEnglishSpanishMapping> TblEnglishSpanishMappings { get; set; }
public virtual ICollection<TblEvent> TblEvents { get; set; }
public virtual ICollection<TblImportDatum> TblImportData { get; set; }
public virtual ICollection<TblImportLog> TblImportLogs { get; set; }
public virtual ICollection<TblNoReminderList> TblNoReminderLists { get; set; }
public virtual ICollection<TblReportDatum> TblReportData { get; set; }
public virtual ICollection<TblResellerPayment> TblResellerPaymentCustomerCompanies { get; set; }
public virtual ICollection<TblResellerPayment> TblResellerPaymentResellerCompanies { get; set; }
public virtual ICollection<TblResellerToCompany> TblResellerToCompanyCompanies { get; set; }
public virtual ICollection<TblResellerToCompany> TblResellerToCompanySalesCompanies { get; set; }
public virtual ICollection<TblRule> TblRules { get; set; }
}
}
you are probably using Net 6. You can remove option nullable from project configuration
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<!--<Nullable>enable</Nullable>-->
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
or make nullable your problem properties. But in this case you will probably have to do this to most properties in your another classes too
public TblOutdialSetting? TblOutdialSetting {get; set;}
and fix create code. Try to use this syntax. Ef sometimes can't figure what table to update if you don't put it explicitly.
_context.Set<TblCompanyInfo>().Add(tblcompanyinfo);
await _context.SaveChangesAsync();
and IMHO you have to remove [Bind] from the action. I don' t see any sense in using this.

How to generate items from same table depending in a condition in select list ASP.NET Core

So here is the problem: I have a product table with columns ID, Name, Type (Brand or Product) and ParentProduct (Select List).
Now imaging that the Products table got some records in which some are (Type) Products and others are Brands, what I want is that while inserting a new Product with type = Product then ParentProduct shows Products with type Brand, whereas inserting a product as brand makes the parentproduct select list disabled.
You could use jQuery to get the Type value and then judge to disable the ParentProduct or not.
Here is a simple demo you could follow:
Model:
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string ParentProduct { get; set; }
}
public class Brand
{
public int ID { get; set; }
public string Name { get; set; }
}
View(Create.cshtml):
#model Product
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Type" class="control-label"></label>
<select asp-for="Type" class="form-control">
<option value="0">Select a Type</option>
<option value="Brand">Brand</option>
<option value="Product">Product</option>
</select>
<span asp-validation-for="Type" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ParentProduct" class="control-label"></label>
<select asp-for="ParentProduct" class="form-control" asp-items="#ViewBag.Brand" disabled>
<option>Select a Brand</option>
</select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts
{
<script>
$("#Type").change(function () {
var v = $(this).val();
console.log(v);
if (v == "Product") {
$("#ParentProduct").removeAttr('disabled');
}
else {
$("#ParentProduct").prop('disabled', true);
}
});
</script>
}
Controller:
public class HomeController : Controller
{
private readonly YourDbContext _context;
public HomeController(YourDbContext context)
{
_context = context;
}
public IActionResult Privacy()
{
ViewBag.Brand = new SelectList(_context.Brands.ToList(), "ID", "Name");
return View();
}
}
Result:

Some model properties always null on post, modelbinding issue

I am making a part of a website where you can update your profile. The profile is tied to the user which is an extension of Microsofts Identity.
The problem is that LastName and FirstName is always null after making a post request.
I would like to think that if this was a ModelState error I would have caught it through !ModelState.IsValid.
https://i.gyazo.com/eea1e3465427eba5a731947473fca821.mp4
Model
using Microsoft.AspNetCore.Identity;
using System;
namespace Certificate_Wiki.Models {
public class CertificateUser : IdentityUser {
public string FirstName { get; set; }
public string LastName { get; set; }
public string Website { get; set; }
public string Occupation { get; set; }
public string Country { get; set; }
public string Description { get; set; }
public String ProfilePictureUrl { get; set; }
public Byte[] ProfilePicture { get; set; }
public bool isPrivate { get; set; }
}
}
cshtml
#{
ViewData["Title"] = "Edit Profile";
}
#model Certificate_Wiki.Models.CertificateUser
<link rel="stylesheet" href="~/css/pages/ProfileEdit.css" media="all" />
<div class="background"></div>
<div class="content">
<div class="content-image">
<img src="~/images/profile/Component 1 – 1.png" alt="" />
</div>
<div class="content-profile">
<div class="profile-image">
<img src="https://www.pngitem.com/pimgs/m/78-786293_1240-x-1240-0-avatar-profile-icon-png.png" alt="error loading image" />
</div>
<div class="profile-form">
<h2>#User.Identity.Name</h2>
<div asp-validation-summary="All">
</div>
<form asp-action="Edit" method="post">
<div class="form-row">
<label>First Name</label>
<input asp-for="FirstName" type="text" name="name" />
</div>
<div class="form-row">
<label>Last Name</label>
<input asp-for="LastName" type="text" name="name" />
</div>
<div class="form-row">
<label>Occupation</label>
<input asp-for="Occupation" type="text" name="occupation" />
</div>
<div class="form-row">
<label>Website</label>
<input asp-for="Website" type="url" name="website" />
</div>
<div class="form-row">
<label>Country</label>
<input asp-for="Country" type="text" name="Country" />
</div>
<div class="form-row">
<label>Profile Description</label>
<textarea asp-for="Description" type="text" name="description"></textarea>
</div>
<div class="form-row">
<label>Private Profile</label>
<input asp-for="isPrivate" type="checkbox" name="Private" />
</div>
<div class="form-row">
<button type="submit">Save</button>
</div>
</form>
</div>
</div>
</div>
Controller
[HttpGet]
[Authorize]
[Route("Profile/edit")]
public async Task<IActionResult> EditAsync()
{
var Profile = await userManager.FindByEmailAsync(User.Identity.Name);
if (Profile == null) { return View(); }
return View(Profile);
}
[ValidateAntiForgeryToken]
[HttpPost]
[Authorize]
[Route("Profile/edit")]
public async Task<IActionResult> EditAsync([FromForm]CertificateUser model)
{
//TODO
//Remove CW from single-line if
if (!ModelState.IsValid) { Console.WriteLine("Modelstate invalid"); return View(model); }
var Profile = await userManager.FindByEmailAsync(User.Identity.Name);
if (Profile == null) { return View(); }
//Update database
Profile.FirstName = model.FirstName;
Profile.LastName = model.LastName;
Profile.Description = model.Description;
Profile.Country = model.Country;
Profile.Occupation = model.Occupation;
Profile.Website = model.Website;
await userManager.UpdateAsync(Profile);
Console.WriteLine("Update success");
return RedirectToAction("Index");
}
Note, While I am at it I would also like to ask if there is a "better" or a rather clean way to update the user without profile... = model... for every property to be updated.
If you use the asp-for attribute for input box, then you shouldn't use the name attribute in the same time (or at least use the same value for it).
In your example you set FirstName for asp-for and then just name for the name attribute of the input for FirstName.

Need help making many-to-many relationships with CRUD in asp.net core

I currently have 2 models:
Movie.cs
public int MovieId { get; set; }
public Genre Genre { get; set; }
public int GenreId { get; set; }
public string MovieTitle { get; set; }
Genre.cs
public int GenreId { get; set; }
public string GenreName { get; set; }
public List<Movie> Movies;
My Create.cshtml View
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="GenreId" class="control-label"></label>
<select asp-for="GenreId" class="form-control" asp-items="ViewBag.GenreId"></select>
</div>
<div class="form-group">
<label asp-for="DirectorId" class="control-label"></label>
<select asp-for="DirectorId" class="form-control" asp-items="ViewBag.DirectorId"></select>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="IsHot" /> #Html.DisplayNameFor(model => model.IsHot)
</label>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="IsNew" /> #Html.DisplayNameFor(model => model.IsNew)
</label>
</div>
<div class="form-group">
<label asp-for="MovieTitle" class="control-label"></label>
<input asp-for="MovieTitle" class="form-control" />
<span asp-validation-for="MovieTitle" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MovieLang" class="control-label"></label>
<input asp-for="MovieLang" class="form-control" />
<span asp-validation-for="MovieLang" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MovieLink" class="control-label"></label>
<input asp-for="MovieLink" class="form-control" />
<span asp-validation-for="MovieLink" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MoviePlot" class="control-label"></label>
<input asp-for="MoviePlot" class="form-control" />
<span asp-validation-for="MoviePlot" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MovieImageFile" class="control-label"></label>
<input asp-for="MovieImageFile" accept="image/*" />
<span asp-validation-for="MovieImageFile" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MovieReleaseYear" class="control-label"></label>
<input asp-for="MovieReleaseYear" class="form-control" />
<span asp-validation-for="MovieReleaseYear" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreateDate" class="control-label"></label>
<input asp-for="CreateDate" class="form-control" />
<span asp-validation-for="CreateDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MovieTrailerLink" class="control-label"></label>
<input asp-for="MovieTrailerLink" class="form-control" />
<span asp-validation-for="MovieTrailerLink" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
so when I make a View to let Admins create a new Movie it allows them to choose from a dropdown list of Genres.
But now I want a many-to-many relationship which would allow me to add 3,4 Genres into one Movie, how do I make views and models for that?
I'm pretty sure I need to make a third model for many-to-many
relationship but I can't find an example on the internet that use it.
Yes, you need to create a third table to connect the two tables many-to-many relationship, first you need to modify your model, as follows:
public class Movie
{
[Key]
public int MovieId { get; set; }
public string MovieTitle { get; set; }
public List<MovieGenre> MovieGenres { get; set; }
}
public class Genre
{
public int GenreId { get; set; }
public string GenreName { get; set; }
public List<MovieGenre> MovieGenres { get; set; }
}
//Intermediate table
public class MovieGenre
{
public int MovieId { get; set; }
public Movie Movie { get; set; }
public int GenreId { get; set; }
public Genre Genre { get; set; }
}
You still need to determine the relationship between these three tables in OnModelCreating in the dbcontext file:
public partial class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{
}
public DbSet<Movie> Movie { get; set; }
public DbSet<Genre> Genre { get; set; }
public DbSet<MovieGenre> MovieGenre { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MovieGenre>()
.HasKey(bc => new { bc.MovieId, bc.GenreId });
modelBuilder.Entity<MovieGenre>()
.HasOne(bc => bc.Movie)
.WithMany(b => b.MovieGenres)
.HasForeignKey(bc => bc.MovieId);
modelBuilder.Entity<MovieGenre>()
.HasOne(bc => bc.Genre)
.WithMany(c => c.MovieGenres)
.HasForeignKey(bc => bc.GenreId);
}
}
Next, you can pass Movie object and select the list of GenreId to the controller:
View:
#model WebApplication_core_mvc.Models.Movie
#{
ViewData["Title"] = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Index</h1>
<div class="row">
<div class="col-md-4">
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label class="control-label"></label>
<select class="form-control" asp-items="ViewBag.GenreId" name="GenreIdList" multiple></select>
</div>
<div class="form-group">
<label asp-for="MovieTitle" class="control-label"></label>
<input asp-for="MovieTitle" class="form-control" />
<span asp-validation-for="MovieTitle" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
Controller:
public class MMTestController : Controller
{
private readonly MyDbContext _context;
public MMTestController(MyDbContext context)
{
_context = context;
}
public IActionResult Index()
{
List<SelectListItem> selectListItems = _context.Genre.Select(a => new SelectListItem
{
Text = a.GenreName,
Value = a.GenreId.ToString()
}).ToList();
ViewBag.GenreId = selectListItems;
return View();
}
public IActionResult Create(Movie movie, List<int> GenreIdList)
{
_context.Movie.Add(movie);
_context.SaveChanges();
foreach (var item in GenreIdList)
{
MovieGenre movieGenre = new MovieGenre()
{
GenreId = item,
MovieId = movie.MovieId,
Genre = _context.Genre.Where(x => x.GenreId == item).FirstOrDefault(),
Movie = movie
};
_context.MovieGenre.Add(movieGenre);
}
_context.SaveChanges();
return View();
}
}
Here is the test result:

Blazor client WASM form validation in component not working

Im having trouble getting from validation to work properly in a Blazor WASM client application.
Encapsulating an InputText element to a component for compact layout does no longer perform validation that is executed correctly otherwise.
using model like
public class Customer {
[Required]
[StringLength(100)
public string customerName {get; set;} = "";
}
in a form of
<EditForm Model=#customer>
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-row">
<div class="form-group mb-0 col-sm-12">
<div class="input-group input-group-sm mb-1 mt-1">
<div class="input-group-prepend">
<span class="input-group-text" style="width:6em;">Firma</span>
</div>
<InputText type="text" class="form-control" #bind-Value=customer.customerName />
</div>
</div>
</EditForm>
the validation works fine!
But to modularize I outsource the inner stuff to an separate component
#page "/inputGroup"
<div class="input-group input-group-sm mb-1 mt-1">
<div class="input-group-prepend">
<span class="input-group-text" style="width:6em;">#label</span>
</div>
<InputText type=#type class="form-control" #bind-Value=#data #oninput=#onChange />
</div>
#code {
[Parameter]
public string label {get; set;} = "Label:";
[Parameter]
public string type {get; set;} = "text";
[Parameter]
public string data {get; set;} = "";
[Parameter]
public EventCallback<string> dataChanged {get; set;}
private Task onChange(ChangeEventArgs e) {
data = (string)e.Value;
return dataChanged.InvokeAsync(data);
}
}
Then I put this to my form, like
...
<div class="form-row">
<div class="form-group mb-0 col-sm-12">
<InputGroup label="Firma:" #bind-data=customer.customerName />
</div>
</div>
...
the validation is not working!?
You could do something like this where you subclass InputText
InputTextGroup.razor
#inherits Microsoft.AspNetCore.Components.Forms.InputText
<div class="mt-1">
<input type="text" id="#Id"
#attributes="#AdditionalAttributes" #bind="#CurrentValueAsString"
class="#CssClass myOtherCssClasses" />
</div>
#if (ValidationFor != null)
{
<div class="myValidationClass">
<ValidationMessage For="ValidationFor" />
</div>
}
And InputTextGroup.razor.cs
public partial class InputTextGroup
{
[Parameter] public string Id { get; set; } = HeadlessUI.HtmlElement.GenerateId();
[Parameter] public string? Label { get; set; }
[Parameter] public Expression<Func<string>>? ValidationFor { get; set; }
}
I took inspiration from this SO answer and this Microsoft docs

Resources