I need to add a search bar on my index page, and the result of the research will be show on another page.
What I've tried:
The view
<form class="row contact-form rounded-pill link no-gutters" id="contact-form-data">
#using (Html.BeginForm("RechercheResto", "Client", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="col-12 col-lg-8 d-inline-block d-lg-flex align-items-center">
<div class="form-group">
<label><i class="lni lni-restaurant" aria-hidden="true"></i></label>
<input type="text" name="nomResto" placeholder="Nom du restaurant" class="form-control">
</div>
</div>
<div class="col-12 col-lg-4">
<i class="fa fa-spinner fa-spin mr-2 d-none" aria-hidden="true"></i>
<input type="submit" class="btn main-btn rounded-pill w-100 h-100 contact_btn" value="Rechercher le restaurant" />
</div>
}
</form>
The controller
[ValidateAntiForgeryToken]
public ActionResult RechercheResto(string nomResto)
{
var restoList = from r in db.Restaurants select r;
if (!string.IsNullOrEmpty(nomResto))
{
restoList = restoList.Where(r => r.name.Contains(nomResto));
}
return View(restoList.ToList());
}
At the time of returning View(restoList.ToList()) you can add viewname in first parameter and second will be your result.ToList().
Thanks for your help. I've just find the solution. The problem was the I put before the BeginForm.
The working code look like that
#using (Html.BeginForm("RechercheResto", "Client", FormMethod.Post, new { #class= "row contact-form rounded-pill link no-gutters"}))
{
#Html.AntiForgeryToken()
<div class="col-12 col-lg-8 d-inline-block d-lg-flex align-items-center">
<div class="form-group">
<label><i class="lni lni-restaurant" aria-hidden="true"></i></label>
<input type="text" name="nomResto" placeholder="Nom du restaurant" class="form-control">
</div>
</div>
<div class="col-12 col-lg-4">
<i class="fa fa-spinner fa-spin mr-2 d-none" aria-hidden="true"></i>
<input type="submit" class="btn main-btn rounded-pill w-100 h-100 contact_btn" value="Rechercher le restaurant" />
</div>
}
Related
im working on a .net core 3.1 razor pages project, i have a form that has a submit button but when i click on it execution is not hitting the Post handler in the page model. Im using webApi as well in the same razor pages project
my startup class
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages()
.AddNewtonsoftJson()
.AddRazorRuntimeCompilation();
services.AddControllers()
.AddNewtonsoftJson()
.AddRazorRuntimeCompilation();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
//app.UseDatabaseErrorPage();
//app.UseExceptionHandler("/Error");
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
});
}
my html
#page
#model CandidateBowl.WebPortal.Pages.JobAdvertisement.AddModel
#{
}
<form method="post" action="/Handlers?handler=Advertisement">
<div class="container-fluid">
<div class="row p-5 border pt-4 my-3 rounded">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="JobAdvertisementModel.CompanyId" />
<div class="col-12 px-3 mb-3 border-bottom text-center text-uppercase">
<h2 class="text-primary">Job Advertisement</h2>
</div>
<div class="col-9 pt-3">
<div class="input-group row mb-3">
<div class="col-md-3 col-sm-12 d-sm-block">
<label asp-for="JobAdvertisementModel.IndustryId"></label>
</div>
<div class="col-md-9 col-sm-12">
<select class="form-select" asp-for="JobAdvertisementModel.IndustryId" asp-items="Model.Options">
<option value=""></option>
</select>
<span class="text-danger" asp-validation-for="JobAdvertisementModel.IndustryId"></span>
</div>
</div>
<div class="input-group row mb-3">
<div class="col-md-3 col-sm-12 d-sm-block">
<label asp-for="JobAdvertisementModel.Title"></label>
</div>
<div class="col-md-9 col-sm-12">
<input class="form-control" asp-for="JobAdvertisementModel.Title" />
<span class="text-danger" asp-validation-for="JobAdvertisementModel.Title"></span>
</div>
</div>
<div class="input-group row mb-3">
<div class="col-md-3 col-sm-12 d-sm-block">
<label asp-for="JobAdvertisementModel.Salary"></label>
</div>
<div class="col-md-9 col-sm-12">
#* use year selector *#
<input class="form-control" asp-for="JobAdvertisementModel.Salary" />
<span class="text-danger" asp-validation-for="JobAdvertisementModel.Salary"></span>
</div>
</div>
<div class="input-group row mb-3">
<div class="col-md-3 col-sm-12 d-sm-block">
<label asp-for="JobAdvertisementModel.Location"></label>
</div>
<div class="col-md-9 col-sm-12">
#* use year selector *#
<input class="form-control" asp-for="JobAdvertisementModel.Location" />
<span class="text-danger" asp-validation-for="JobAdvertisementModel.Location"></span>
</div>
</div>
<div class="input-group row mb-3">
<div class="col-md-3 col-sm-12 d-sm-block">
<label asp-for="JobAdvertisementModel.Role"></label>
</div>
<div class="col-md-9 col-sm-12">
#* use year selector *#
<input class="form-control" asp-for="JobAdvertisementModel.Role" />
<span class="text-danger" asp-validation-for="JobAdvertisementModel.Role"></span>
</div>
</div>
<div class="input-group row mb-3">
<div class="col-md-3 col-sm-12 d-sm-block">
<label asp-for="JobAdvertisementModel.Requirements"></label>
</div>
<div class="col-md-9 col-sm-12">
#* use year selector *#
<textarea class="form-control" asp-for="JobAdvertisementModel.Requirements"></textarea>
<span class="text-danger" asp-validation-for="JobAdvertisementModel.Requirements"></span>
</div>
</div>
<div class="input-group row mb-3">
<div class="col-md-9 offset-3 col-sm-12">
<div class="row">
<div class="col-md-6 col-sm-12">
<button class="btn btn-primary form-control">Save</button>
</div>
<div class="col-md-6 col-sm-12">
#* return to candidate details page *#
<a asp-page="Index" class="btn btn-success form-control">Back To Job Advertisements </a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
#section Scripts{
<script src="~/js/tinMCE.js" type="text/javascript"></script>
}
my code behind
public class AddModel : PageModel
{
private readonly IJobAdvertisementRepository jobAdvertisementRepository;
private readonly IIndustryRepository industryRepository;
public AddModel(IJobAdvertisementRepository jobAdvertisementRepository, IIndustryRepository industryRepository)
{
this.jobAdvertisementRepository = jobAdvertisementRepository;
this.industryRepository = industryRepository;
}
[BindProperty]
public JobAdvertisementModel JobAdvertisementModel { get; set; }
public List<SelectListItem> Options { get; set; }
public async Task<IActionResult> OnGetAsync()
{
Options = industryRepository.GetIndustries().Result.Select(a => new SelectListItem
{
Value = a.Id.ToString(),
Text = a.Name
}).ToList();
return Page();
}
public async Task<IActionResult> OnPostAdvertisementAsync()
{
if (!ModelState.IsValid)
{
Options = industryRepository.GetIndustries().Result.Select(a => new SelectListItem
{
Value = a.Id.ToString(),
Text = a.Name
}).ToList();
return Page();
}
var id = await jobAdvertisementRepository.AddAdvertisement(JobAdvertisementModel);
if (id == 0)
return Page();
return RedirectToPage();//still to decide where it should go
}
}
what could be the issue, any help is will be greatly appreciated
You posted,You have a from that has a submit button I guess you mean this line <button class="btn btn-primary form-control">Save</button> is not working.
try this
<input type="submit" value="Save" class="btn btn-primary form-control"/>
also make sure action="/Handlers?handler=Advertisement" is correct in your form.
Please change
<form method="post" action="/Handlers?handler=Advertisement">
to
<form method="post" asp-page-handler="Advertisement">
This will generate the following HTML:
<form method="post" action="/Add?handler=Advertisement">
And like #PritomSarkar says, fix your button
<input type="submit" value="Save" class="btn btn-primary/>
I'm new in coding with ASP.NET Core. I created a CRUD functions in my application and then i decided to change some rules like Create New do not navigate to another tab it will be navigated to a component modal. So I did the steps that it shown in a tutorial but I didn't get a result .
Here's the page :
This is the code in index.cshtml:
#model IEnumerable<PermissionManagement.MVC.Models.Segment>
#{
ViewData["Title"] = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div id="PlaceHolderHere"></div>
<button type="button" class="btn btn-primary" data-toggle="ajax-modal" data-target="#addSegment" asp-action="_SegmentModelPartial" data-url="#Url.Action("Create")"> Create </button>
And this is the code wrote it in SegmentModelPartial:
#model Segment
<div class="modal fade" id="addSegment">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addSegmentLabel">Modal title</h5>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close">
<span>x</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="SName" class="control-label">Nom</label>
<input asp-for="SName" class="form-control" />
<span asp-validation-for="SName" class="text-danger"></span>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group ">
<label asp-for="SRang" class="control-label">Rang</label>
<input asp-for="SRang" class="form-control" />
<span asp-validation-for="SRang" class="text-danger"></span>
</div>
</div>
</form>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" data-save="modal">Save changes</button>
</div>
</div>
</div>
</div>
This is my code in site.js:
$(function () {
var PlaceHolderElement = $('#PlaceHolderHere');
$('button[data-toggle="ajax-modal"]').click(function (event) {
var url = $(this).data('url');
$.get(url).done(function (data) {
PlaceHolderElement.html(data);
PlaceHolderElement.find('.modal').modal('show');
})
})
PlaceHolderElement.on('click', '[data-save="modal"]', function (event) {
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var sendData = form.serialize();
$.post(actionUrl, sendData).done(function (data)){
PlaceHolderElement.find('.modal').modal('hide');
})
})
})
And finally this is the controller:
[HttpGet]
public IActionResult Create()
{
Segment seg = new Segment();
return PartialView("_SegmentModelPartial", seg);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Segment segment)
{
_context.Add(segment);
await _context.SaveChangesAsync();
return PartialView("_SegmentModelPartial", segment);
}
I am using ASP.NET Core MVC with identity but I want to perform my own logic on login. So, what I did is I scaffolded the login page and make my own design.
Here is my form:
#page
#model LoginModel
#{
ViewData["Title"] = "Login";
}
<div class="login-page cnt-bg-photo overview-bgi" style="background-image:
url('#Url.Content("~/assets/img/banner-1.jpg")')">
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class="forgot-box contact-2 clearfix">
<div class="login-header clearfix">
<div class="pull-left">
<h4>Login</h4>
</div>
</div>
<!-- <div role="alert"
class="alert alert-danger font-12">
<strong>Oops!!</strong> Invalid Email or Password
</div>-->
<p>Please enter your user name and password to login</p>
<form action="/Account/Login" method="POST">
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-group w-46-f float-left">
<div class="form-check checkbox-theme pull-left">
<div class="checkbox">
<label asp-for="Input.RememberMe">
<input asp-for="Input.RememberMe" />
#Html.DisplayNameFor(m => m.Input.RememberMe)
</label>
</div>
</div>
</div>
<div class="form-group float-right">
<label class="pull-right forgotpassword-label">Forgot Password ?</label>
</div>
<div class="clearfix"></div>
<button type="submit" class="btn btn-color btn-md pull-right">Login</button>
</form>
<div class="clearfix"></div>
<div class="text-center p-t-46 p-b-20 font-14">
<span class="txt2">
Or Login with
</span>
</div>
<div class="login100-form-social flex-c-m">
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="#Model.ReturnUrl" method="post">
#foreach (var provider in Model.ExternalLogins)
{
if (provider.DisplayName == "Google")
{
<button type="submit" class="btn login100-form-social-item flex-c-m bg3 m-r-5" style="display: inline-block" name="provider" value="#provider.Name" title="Log in using your #provider.DisplayName account"><i class="fab fa-google"></i></button>
}
else if (provider.DisplayName == "Facebook")
{
<button type="submit" class="btn login100-form-social-item flex-c-m bg1 m-r-5" style="display: inline-block" name="provider" value="#provider.Name" title="Log in using your #provider.DisplayName account"> <i class="fab fa-facebook-f"></i></button>
}
else if (provider.DisplayName == "Twitter")
{
<button type="submit" class="btn login100-form-social-item flex-c-m bg2 m-r-5" style="display: inline-block" name="provider" value="#provider.Name" title="Log in using your #provider.DisplayName account"> <i class="fab fa-twitter"></i></button>
}
}
</form>
</div>
<hr>
<div class="text-center forgotpassword-label font-14">
Not a member?
<a href="#Url.Action("Packages","Account")">
<strong>Sign up now</strong>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Now I want to post the login data to my own controller action. But it get null on my action method. Parameters are not filled with actual data of the form.
Here is my controller method:
[HttpPost]
public IActionResult Login(LoginModel.InputModel model)
{
//My logic
return View();
}
Why does it get model parameter as null?
Well, specifically, the source of it being null is the fact that in the Razor Page, all the input names begin with Input. because for the code-behind, the model being bound to is LoginModel and the data in those inputs will be bound to the InputModel property (Input) there. However, in your controller, you're binding directly to InputModel, and InputModel has no Input member. As a result, none of the posted inputs match anything and are therefore not bound.
If you're going to post from a Razor Page, you need to do it to a Razor Page. There's just too many inconsistencies between Razor Pages and MVC to line everything up correctly. If you you want to use a controller, you're going to need a view specifically geared for that, not your Razor Page.
Here's my problem, I have a Book that contains List<Page> that contains List<Line> and I'm trying to create a view that will edit my book. This view contains all the lines from the book. I've made an MVVM for List<Page> that calls smaller MVVM for List<Line>. The problem is that MVVM is not counting all Line as one single form, so this happens:
<form>
<h1>Page one</h1>
<input type="hidden" name="[0].lineContent" value=""/>
<input type="hidden" name="[1].lineContent" value=""/>
<h1>Page one</h1>
<input type="hidden" name="[0].lineContent" value=""/>
<input type="hidden" name="[1].lineContent" value=""/>
<input type="hidden" name="[2].lineContent" value=""/>
</form>
Each time my for loop for page iterates, my for loops in lines get reset to 0, this creates duplicate name entries.
There are multiple ways to correct this, the easiest:
Build input by hand with unique name id instead of using HTML helpers
My question is the following: how can I achieve a clean razor that can be reused, and that will not duplicate name entries in my form?
UPDATE:
Context:
Just like the book exemple up, my models have the same structure.
in my case I have a Submission that contains a list of Section that contains a list of SubSection that contains a list of SubmissionLine.
Here's my main view:
#model
QuotingPlus.Models.Submission
#{
ViewData["Title"] = "Edit";
}
<form id="submission-form" asp-action="Edit">
<div>
<p class="d-inline-block">
<a class="btn btn-primary" data-toggle="collapse"
href="#multiCollapseExample1" role="button" aria-expanded="false" aria-controls="multiCollapseExample1">Edit submission details</a>
</p>
<nav aria-label="breadcrumb" class="d-inline-block">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/Clients/Details/#(Model.IdProjectNavigation.IdClientNavigation.IdClient)">#Model.IdProjectNavigation.IdClientNavigation.FirstName
#Model.IdProjectNavigation.IdClientNavigation.LastName</a>
</li>
<li class="breadcrumb-item">
#Model.IdProjectNavigation.Name
</li>
<li class="breadcrumb-item active" aria-current="page">#Model.Number</li>
</ol>
</nav>
</div>
<div class="row">
<div class="col mb-3">
<div class="collapse multi-collapse" id="multiCollapseExample1">
<div class="card card-body">
<h1>Submission details</h1>
<hr/>
<div class="row">
<div class="col-md-4">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="IdSubmission"/>
<div class="form-group">
<label asp-for="IdTypeSubmission" class="control-label"></label>
<select asp-for="IdTypeSubmission" class="form-control" asp-items="ViewBag.IdTypeSubmission"></select>
<span asp-validation-for="IdTypeSubmission" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IdProject" class="control-label"></label>
<select asp-for="IdProject" class="form-control" asp-items="ViewBag.IdProject"></select>
<span asp-validation-for="IdProject" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Number" class="control-label"></label>
<input asp-for="Number" class="form-control"/>
<span asp-validation-for="Number" class="text-danger"></span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="save-warning" style="display: none;" class="alert alert-info alert-dismissible fade show" role="alert">
<strong>WARNING!</strong> Make sure you save before leaving.
<button type="button" class="close" data-dismiss="alert" aria-label="Close" onclick="SetWarningDisplayPreferenceCookie();">
<span aria-hidden="true">×</span>
</button>
</div>
<div id="carouselIndicators" style="height: 100% !important;" class="carousel slide pb-5 mb-5" data-interval="false">
<div class="carousel-inner">
#{
Html.RenderPartial("SubmissionSectionEditor", Model.SubmissionSection.ToList());
}
</div>
<nav class="navbar navbar-light bg-secondary mb-0 pt-2 fixed-bottom">
<div>
<button type="submit" value="Save" class="btn btn-default text-white">
<i class="material-icons" style="font-size: 2em;">
save
</i>
</button>
</div>
<a class="col text-center mh-100 pt-2 pb-2" href="#carouselIndicators" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="false"></span>
</a>
<a class="col text-center mh-100 pt-2" href="#carouselIndicators" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="false"></span>
</a>
<div>
<a asp-action="Index" class="btn text-white">
<i class="material-icons" style="font-size: 2em;">
cancel
</i>
</a>
</div>
</nav>
</div>
</form>
Here's SubmissionSectionEditor.cshtml
#model List<SubmissionSection>
#{
var isFirstCarouselItem = true;
}
#for (var indexSection = 0; indexSection < Model.Count(); indexSection++)
{
<div class="carousel-item #((isFirstCarouselItem) ? "active" : "")">
#{
isFirstCarouselItem = false;
}
<h1>#Model[indexSection].IdSectionNavigation.Name</h1>
<div id="#(Model[indexSection].IdSection + "accordion")">
#{
var submissionSubSections = M odel[indexSection].SubmissionSubSection;
}
#if (submissionSubSections != null)
{
Html.RenderPartial("SubmissionSubSectionEditor", submissionSubSections.ToList());
}
</div>
</div>
}
Here's SubmissionSubSectionEditor.cshtml
#model List<SubmissionSubSection>
#for (var indexSubSection = 0; indexSubSection < Model.Count; indexSubSection++)
{
<div class="card">
<div class="card-header" id="#(Model[indexSubSection].IdSubSection + "SubSectionHeader")">
<h2 class="mb-0">
<button type="button" class="btn btn-link collapsed" data-toggle="collapse" data-target="#("#" + Model[indexSubSection].IdSubSection + "SubSectionCollapse")" aria-
expanded="true" aria-controls="#(Model[indexSubSection].IdSubSection + "SubSectionCollapse")">
#Model[indexSubSection].IdSubSectionNavigation.Name
</button>
</h2>
</div>
<div id="#(Model[indexSubSection].IdSubSection + "SubSectionCollapse")" class="collapse" aria-labelledby="#(Model[indexSubSection].IdSubSection +
"SubSectionHeader")" data-parent="#("#" + Model[indexSubSection].IdSubmissionSectionNavigation.IdSection + "accordion")">
<div class="card-body">
<table class="table w-100">
<thead class="thead-dark">
<tr>
<th>Quantity</th>
<th>Article</th>
<th>Total Material</th>
<th>Unit Price Material</th>
<th>Total Sub Contractor</th>
<th>Unit Price Sub Contractor</th>
<th>Total Workforce</th>
<th>Unit Price Workforce</th>
<th>Display</th>
</tr>
</thead>
<tbody>
#{
Html.RenderPartial("SubmissionLineEditor", Model[indexSubSection].SubmissionLine.ToList());
}
</tbody>
</table>
</div>
</div>
</div>
}
Here's SubmissionLineEditor.cshtml
#model List<SubmissionLine>
#for (var indexLine = 0; indexLine < Model.Count; indexLine++)
{
<tr>
#Html.HiddenFor(x => Model[indexLine].IdSubmissionLine)
#Html.HiddenFor(x => Model[indexLine].IdArticle)
#Html.HiddenFor(x => Model[indexLine].IdSubmissionSubSection)
<td>#Html.TextBoxFor(x => Model[indexLine].Quantity, new {#type = "number", #step = "0.5", #min="0"})</td>
<td>#Model[indexLine].IdArticleNavigation.Designation</td>
<td>#Model[indexLine].TotalMaterial</td>
<td>#Model[indexLine].IdArticleNavigation.UnitPriceMaterial</td>
<td>#Model[indexLine].TotalSubContractor</td>
<td>#Model[indexLine].IdArticleNavigation.UnitPriceSubContractor</td>
<td>#Model[indexLine].TotalWorkforce</td>
<td>#Model[indexLine].IdArticleNavigation.UnitPriceWorkforce</td>
<td>#Html.CheckBoxFor(x => Model[indexLine].IsDisplayed, new {#class = "checkbox"}).
</td>
</tr>
}
I only want to know how I can keep this structure with MVVM and avoid the input name duplicate issue?
RE-UPDATE:
I'm not quite sure why this would be useful since I get name duplicate input, but here's my save action:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Admin, SuperAdmin, Employe")]
public ActionResult Edit(int id, Submission submission, [FromForm] List<SubmissionLine> lines)
{
var test = Request.Form;
if (id != submission.IdSubmission)
{
return NotFound();
}
if (ModelState.IsValid)
{
SubmissionUpdateHelper.SaveSubmissionModifications(_context, submission, lines);
return RedirectToAction(nameof(Index));
}
ViewData["IdProject"] = new SelectList(_context.Project, "IdProject", "Name", submission.IdProject);
ViewData["IdTypeSubmission"] = new SelectList(_context.TypeSubmission, "IdTypeSubmission",
"TypeSubmission1", submission.IdTypeSubmission);
return View(submission);
}
For showing sub-list properties in View, Try code below:
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="BookName" class="control-label"></label>
<input asp-for="BookName" class="form-control" />
<span asp-validation-for="BookName" class="text-danger"></span>
</div>
<div class="form-group">
#for (int i = 0; i < Model.Pages.Count; i++)
{
<div class="form-group">
<label asp-for="Pages[i].PageName" class="control-label"></label>
<input asp-for="Pages[i].PageName" class="form-control" />
<span asp-validation-for="Pages[i].PageName" class="text-danger"></span>
</div>
<div class="form-group">
#for (int j = 0; j < Model.Pages[i].Lines.Count; j++)
{
<div class="form-group">
<label asp-for="Pages[i].Lines[j].LineContent" class="control-label"></label>
<input asp-for="Pages[i].Lines[j].LineContent" class="form-control" />
<span asp-validation-for="Pages[i].Lines[j].LineContent" class="text-danger"></span>
</div>
}
</div>
}
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
Finally, I've just added the input by hand so I could keep my MVVM pattern. I've also used an increment variable in TempData so I could keep track of the input
in Edit.cshtml
#{
TempData["submissionLineCount"] = 0;
}
here's the new version of SubmissionLineEditor.cshtml
#model List<SubmissionLine>
#for (var indexLine = 0; indexLine < Model.Count; indexLine++)
{
<tr>
<input name="[#(TempData["submissionLineCount"])].IdSubmissionLine" value="#(Model[indexLine].IdSubmissionLine)" type="hidden"/>
<input name="[#(TempData["submissionLineCount"])].IdArticle" value="#(Model[indexLine].IdArticle)" type="hidden"/>
<input name="[#(TempData["submissionLineCount"])].IdSubmissionSubSection" value="#(Model[indexLine].IdSubmissionSubSection)" type="hidden"/>
<td>#TempData["submissionLineCount"]</td>
<td>
<input name="[#(TempData["submissionLineCount"])].Quantity" value="#(Model[indexLine].Quantity)" type="number" step="0.5" min="0"/>
</td>
<td>#Model[indexLine].IdArticleNavigation.Designation</td>
<td>#Model[indexLine].TotalMaterial</td>
<td>#Model[indexLine].IdArticleNavigation.UnitPriceMaterial</td>
<td>#Model[indexLine].TotalSubContractor</td>
<td>#Model[indexLine].IdArticleNavigation.UnitPriceSubContractor</td>
<td>#Model[indexLine].TotalWorkforce</td>
<td>#Model[indexLine].IdArticleNavigation.UnitPriceWorkforce</td>
<td>
<input type="checkbox" name="[#(TempData["submissionLineCount"])].IsDisplayed" class="checkbox" value="#(Model[indexLine].IsDisplayed)"/>
</td>
</tr>
{
TempData["submissionLineCount"] = (Convert.ToInt32(TempData["submissionLineCount"]) + 1);
}
}
This is clearly not the best way, I hope there will be more documentation on how to apply MVVM in AspNet.Core in the future...
If this answer gets deprecated, make sure to post your own!
I've spent several hours combing Stackoverflow and other sites trying everyone's solutions with no luck so far. I'm sure I've missed something, but I can't see it. Hopefully you can point me to a fix.
I have an initial form inside a partial view that is rendered into a parent view whose validation works fine. Once the form is submitted via Ajax replace, I return either a login or registration partial view with a new form in the response. This second form will not display the model validation errors when an incomplete form is submitted and the same partial view is returned.
Thanks in advance for any tips you can offer to bring an end to this insanity!
Parent View Section
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
<div class="panel panel-primary" id="formData">
#await Html.PartialAsync("_UserNamePartial", new UserNameViewModel())
</div>
</div>
</div>
Working Rendered Partial View
<div class="panel-heading">
<h3 class="panel-title">Let's Start With Your E-mail Address</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-xs-12">
<form asp-controller="Account" asp-action="IsAccountValid" data-ajax="true" data-ajax-method="POST"
data-ajax-mode="replace" data-ajax-update="#formData">
#Html.AntiForgeryToken()
<div class="form-group">
<label for="UserName">Your Email Address</label>
<div class="input-group">
<input type="text" id="UserName" name="UserName" class="form-control" placeholder="Your email address" />
<div class="input-group-btn">
<button type="submit" id="btnGetStarted" class="btn btn-primary">Get Started</button>
</div>
</div>
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
</form>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
</div>
</div>
</div>
Initial Validation Controller Action
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public IActionResult IsAccountValid(UserNameViewModel model)
{
if (!ModelState.IsValid)
return PartialView("../Home/_UserNamePartial", model);
AccountRepository accountRepository = new AccountRepository(ConnectionConfig.InshoraDev);
AuthName match = accountRepository.GetAuthName(model.UserName);
if (match != null)
{
ModelState.Clear();
LoginViewModel loginModel = new LoginViewModel()
{
UserName = model.UserName
};
return PartialView("_UserLoginPartial", loginModel);
}
ModelState.Clear();
SignUpViewModel signupModel = new SignUpViewModel()
{
UserName = model.UserName,
};
return PartialView("_UserSignUp", signupModel);
}
Login Partial View (Validation Error Display Not Working)
#model Inshora.Models.Account.LoginViewModel
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="panel-heading">
<h3 class="panel-title">Log Into Your Account</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-xs-12">
<form id="login-form" asp-controller="Account" asp-action="Login" method="post" role="form" style="display: block;"
data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-update="formData" data-ajax-complete="AcctLib.Login.Events.onComplete">
#Html.AntiForgeryToken()
<div class="form-group">
<input type="text" name="UserName" id="UserName" tabindex="1" class="form-control" placeholder="Email Address" value="#Model.UserName">
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
<div class="form-group">
<input type="password" name="Password" id="Password" tabindex="2" class="form-control" placeholder="Password">
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group text-center">
<input type="checkbox" tabindex="3" class="" name="RememberMe" id="RememberMe">
<label for="RememberMe"> Remember Me</label>
</div>
<div class="form-group">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<input type="submit" name="login-submit" id="login-submit" tabindex="4" class="form-control btn btn-primary" value="Log In">
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-lg-12">
<div class="text-center">
<a id="PasswordReset" asp-controller="Account" asp-action="PasswordReset" data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#formData" tabindex="5" class="inshora-forgot-password">Forgot Password?</a>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
AcctLib.Login.Init();
})
</script>
LoginViewModel
public class LoginViewModel
{
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
[Required]
public bool RememberMe { get; set; }
}
Client Side Initialization Code
AcctLib.Login.RebindForm = function() {
$('form').each(function (i, f) {
$form = $(f);
$form.removeData('validator');
$form.removeData('unobtrusiveValidation');
$.validator.unobtrusive.parse($form);
});
}
AcctLib.Login.Init = function () {
AcctLib.Login.RebindForm();
$('#UserName').focus();
}
Update
I have updated the parent page (index.cshtml) to the following and it still doesn't display the messages.
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
<div class="panel panel-primary" id="formData">
#await Html.PartialAsync("_UserNamePartial", new UserNameViewModel())
</div>
</div>
</div>
#section Scripts
{
#{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
The problem was that I had not used the asp-for tag helpers. Those helpers are responsible for generating the data-* attributes needed by the unobtrusive validation parser. Once I started using them it started working. Thank you to everyone who tried to help.
Corrected View
<div class="panel-body">
<div class="row">
<div class="col-xs-12">
<form id="login-form" asp-controller="Account" asp-action="Login" method="post" role="form"
data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-update="#formData">
#Html.AntiForgeryToken()
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="UserName"></label>
<input asp-for="UserName" class="form-control" placeholder="Email Address"/>
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" placeholder="Password"/>
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group text-center">
<input asp-for="RememberMe" />
<label asp-for="RememberMe"> Remember Me</label>
</div>
<div class="form-group">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<input type="submit" name="login-submit" id="login-submit" tabindex="4" class="form-control btn btn-primary" value="Log In">
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-lg-12">
<div class="text-center">
<a id="PasswordReset" asp-controller="Account" asp-action="PasswordReset" data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#formData" tabindex="5" class="inshora-forgot-password">Forgot Password?</a>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
if (!ModelState.IsValid)
return PartialView("..\\Home\\_UserNamePartial", model);
pretty sure this violates pathing
if(!ModelState.IsValid)
return PartialView("../Home/_UserNamePartial", model);
Cut renderPartial link and paste to before #script section, like below:
#{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
#section Scripts
{
}