MVC Razor passing Model from View to Controller - c#

My Controller which calls the View:
public ActionResult Excel()
{
myClass arg = new myClass(string arg1, object arg2);
return View(arg);
}
My View:
#model myClass
#using (Html.BeginForm("createXML", "Excel", new { arg = Model }))
{ { #Html.ValidationSummary(true)
<div class="container-full" style="background-color:aliceblue">
<h3 class="= container">Einstellungen:</h3>
<div class="container">
<div class="row">
<div class="col-md-3">
<div class="panel-group">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" href="#collapse1">nk</a>
</h4>
</div>
<div id="collapse1" class="panel-collapse collapse">
<ul class="list-group">
<li class="list-group-item">
<form action="select.html">
<label>
Anzahl:
<select name="decimal">
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
<option>9</option>
<option>10</option>
</select>
</label>
</form>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel-group">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" href="#collapse2">sign</a>
</h4>
</div>
<div id="collapse2" class="panel-collapse collapse">
<ul class="list-group">
<li class="list-group-item">
<form action="select.html">
<label>
left:
<select name="decimal">
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
<option>9</option>
<option>10</option>
</select>
</label>
</form>
</li>
<li class="list-group-item">
<form action="select.html">
<label>
right:
<select name="decimal">
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
<option>9</option>
<option>10</option>
</select>
</label>
</form>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel-group">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" href="#collapse3">Optionen</a>
</h4>
</div>
<div id="collapse3" class="panel-collapse collapse">
<form>
<fieldset>
<ul class="list-group">
<li class="list-group-item">
<label>
#Html.CheckBoxFor(m => m.P5_VerkBesch)
#*<input type="checkbox" name="verkBesch" value="1">*#
Verk
</label>
</li>
<li class="list-group-item">
<label>
#Html.CheckBoxFor(m => m.P5_SpezBesch)
#*<input type="checkbox" name="sFonds" value="1">*#
spezbesch
</label>
</li>
<li class="list-group-item">
<label>
<input type="checkbox" name="kgpr" value="1">
kgpr
</label>
</li>
<li class="list-group-item">
<label>
#Html.CheckBoxFor(m => m.P5_AD)
#*<input type="checkbox" name="Ad" value="1">*#
ad
</label>
</li>
<li class="list-group-item">
<label>
<input type="checkbox" name="md" value="1">
md
</label>
</li>
<li class="list-group-item">
<label>
<input type="checkbox" name="beschText" value="1">
beschtxt
</label>
</li>
<li class="list-group-item">
<label>
#Html.CheckBoxFor(m => m.P5_Uni)
#*<input type="checkbox" name="uni" value="1">*#
uni
</label>
</li>
</ul>
</fieldset>
</form>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel-group">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" href="#collapse4">Weitere Einstellungen</a>
</h4>
</div>
<div id="collapse4" class="panel-collapse collapse">
<ul class="list-group">
<li class="list-group-item">
<div>
<label>Anzahl Dateien pro:</label>
<br />
#*<small>123</small>*#
#Html.TextBoxFor(i => i.P5_ANZAHL, new { #type = "number", #style = "max-width: 100%" })
</div>
</li>
<li class="list-group-item">
<div>
<label>Datum ändern:</label>
<br />
<p>
#Html.TextBoxFor(m => m.P5_DATUM, new { #id = "datepicker", #type = "text", #style = "width: 100%" })
#*<input type="text" id="datepicker" style="width:100%">*#
</p>
</div>
</li>
<li class="list-group-item">
<div>
<label>Fds:</label>
<br />
#Html.TextAreaFor(m => m.P5_Fundstelle, new { #rows = "4", style = "width: 100%" })
#*<textarea style="max-width:100%;" rows="4"></textarea>*#
</div>
</li>
<li class="list-group-item">
<div>
<label>Anhaltspunkte :</label>
<br />
#*<small>123</small>*#
#Html.TextBoxFor(m => m.P5_Anhaltspunkte, new { style = "width: 100%" })
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container">
<h2>Excel</h2>
<div>
<form id="myForm" method="post"
enctype="multipart/form-data"
action="">
<div>
<h1>File Upload</h1>
#if (!IsPost)
{
#FileUpload.GetHtml(
initialNumberOfFiles: 2,
allowMoreFilesToBeAdded: true,
includeFormTag: true,
addText: "Add another file",
uploadText: "Upload")
}
<span>#message</span>
</div>
</form>
<input type="submit" value="Submit" />
</div>
</div>
</div>
}
Controller Function which should be called:
public ActionResult createXML(myClass arg)
{
//Code here...
return View("~/Views/Excel/Excel.cshtml");
}
My Problem is that "arg" is passed as null to createXML. I can't figure out why. One has to say that I am normally a WPF/WinForms Programmer.
UPDATE
I updated my code a bit, to make it clearer maybe.
UPDATE 2
I found out a part of the problem. My Object has only one constructor, where i need some paramateres. This generates somehow an error. I created a constructor without any parameter and it works, but it somehow creates a new object when I start my form and does not use the object i passed on to the view.

change view like this
#using (Html.BeginForm("createXML", "Excel", FormMethod.Post))
and
add this above createxml
[HttpPost]
UPDATE
I tried your codes and worked. Here is codes
HomeController codes. You can place this methods in your ExcelController
public ActionResult Excel()
{
myClass arg = new myClass();
arg.p = 5;
return View(arg);
}
[HttpPost]
public ActionResult createXML(myClass arg)
{
int a = arg.p;
return View();
}
Home/Excel.cshtml codes
#model Identity.Controllers.myClass
#{
Layout = null;
}
#using (Html.BeginForm("createXML", "Home", FormMethod.Post))
{ #Html.ValidationSummary(true)
<div class="container-full" style="background-color:aliceblue">
<h3 class="= container">Einstellungen:</h3>
<div class="container">
#Html.TextBoxFor(x => x.p)
<input type="submit" value="Submit" />
</div>
</div>
}

You are using wrong overload.
You can use this overload. BeginForm(
this HtmlHelper htmlHelper,
string actionName,
string controllerName,
Object routeValues,
FormMethod method,
Object htmlAttributes)
#Html.BeginForm("createXML", "Excel", new { arg = Model },FormMethod.Post,null)
or remove the argument and use hidden field within the form:
#using(Html.BeginForm("createXML", "Excel",FormMethod.Post))
{
.....
#Html.Hidden("arg", Model)
}

Related

Getting viewmodel value null on Http Post method,

My View page, it contains many dynamic controls
I know this question has been asked and answered a dozen of times but none of the solutions help me.
I have a following ViewModel which is consisted of QuestionBatch data model,listResponseTag and a list of listQuestion data model.
View Model
public class VM_Questionaire
{
public QuestionBatch ThequestionBatch { get; set; }
public List<Question> listQuestion { get; set; }
public List<ResponseTag> listResponseTag { get; set; }
}
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult submit_Questionaire(VM_Questionaire vm_question)
{
if (ModelState.IsValid)
{
Console.Write(Newtonsoft.Json.JsonConvert.SerializeObject(vm_question));
}
return View("Index");
}
View
<pre>
#model Fonz_Survey.Models.VM_Questionaire
#{
ViewBag.Title = "Questionaire";
}
#using (Html.BeginForm("submit_Questionaire", "Questions", FormMethod.Post))
{
#Html.AntiForgeryToken()
<h2>Questionaire</h2>
<div class="card">
<div class="card-header">
<div class="jumbotron-fluid">
<div class="container">
<div class="row">
<div class="col-lg-3 col-md-6 col-sm-6">
<h5 class="text-muted text-monospace">CODE</h5>
</div>
<div class="col-lg-3 col-md-6 col-sm-6 text-danger text-monospace">
#Html.DisplayFor(model => model.ThequestionBatch.Code, new { #class = "text-danger text-monospace" })
#Html.HiddenFor(model => model.ThequestionBatch.Code)
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-6 col-sm-6">
<h5 class="text-muted text-monospace">Question Batch</h5>
</div>
<div class="col-lg-3 col-md-6 col-sm-6 text-primary text-monospace">
#Html.DisplayFor(model => model.ThequestionBatch.BatchName, new { #class = "text-primary text-monospace" })
#Html.HiddenFor(model => model.ThequestionBatch.BatchName)
</div>
<div class="col-lg-3 col-md-6 col-sm-6">
<h5 class="text-muted text-monospace">No of Questions</h5>
</div>
<div class="col-lg-3 col-md-6 col-sm-6">
<label class="text-primary text-monospace">#ViewBag.totalQstns</label>
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-6 col-sm-6">
<h5 class="text-muted text-monospace">Description</h5>
</div>
<div class="col-lg-3 col-md-6 col-sm-6 text-primary text-monospace text-wrap">
#Html.DisplayFor(model => model.ThequestionBatch.Description, new { #class = "text-primary text-monospace text-wrap" })
#Html.HiddenFor(model => model.ThequestionBatch.Description)
</div>
</div>
</div>
</div>
</div>
<div class="card-body">
#{
//int rowIndex = 0;
}
#if (Model != null && Model.listQuestion != null)
{
for(var rowIndex=0; rowIndex< Model.listQuestion.Count; rowIndex++)
//foreach (Question question in Model.listQuestion)
{
// rowIndex++;
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-gray text-light">
<div class="d-inline-block">
<label class="text-lg-left font-weight-bold">#rowIndex.</label>
<label class="text-lg-left font-weight-bold">#Model.listQuestion[rowIndex].QuestionEN</label>
<br />
<label class="text-lg-left font-weight-bold">#Model.listQuestion[rowIndex].QuestionAR</label>
</div>
</div>
<div class="card-body font-weight-bolder">
#switch (#Model.listQuestion[rowIndex].QType)
{
case 1:
// to do Text Boxes
<p class="text-info"><u>Please enter your message below;</u></p>
<div class="form-row">
#*<input type="text" placeholder="#question.QuestionEN" name="Q_#question.Id" id="Q_#question.Id" class="form-control" />*#
#Html.EditorFor(model=> #Model.listQuestion[rowIndex].QuestionEN)
</div>
break;
case 2:
// to do Radio
<p class="text-info"><u>Please select any one of the option below;</u></p>
foreach (ChoiceTag ct in Model.listQuestion[rowIndex].ChoiceTags)
{
<div class="form-check">
<label class="form-check-label" for="C_#ct.Id">
<input type="radio" class="form-check-input" id="C_#ct.Id" name="#Model.listQuestion[rowIndex].Id" value="#ct.AnswerEN">#ct.AnswerEN | #ct.AnswerAR
</label>
</div>
}
break;
case 3:
// to do Radio
<p class="text-info"><u>Please select options below;</u></p>
foreach (ChoiceTag ct in Model.listQuestion[rowIndex].ChoiceTags)
{
<div class="form-check">
<label class="form-check-label" for="C_#ct.Id">
<input type="checkbox" class="form-check-input" id="C_#ct.Id" name="#Model.listQuestion[rowIndex].Id" value="#ct.AnswerEN">#ct.AnswerEN | #ct.AnswerAR
</label>
</div>
}
break;
}
</div>
</div>
</div>
</div>
}
<div class="form-group">
<div class="col-12 text-center">
<input type="submit" value="Submit" class="btn btn-success " />
</div>
</div>
}
</div>
<div class="card-footer">
<label class="text-danger">#ViewBag.Message</label>
</div>
</div>
}
</prev>
First object getting value, but the list getting null.
#Html.DisplayFor() will not post values. You need to use #Html.HiddenFor() along with #Html.DisplayFor(). Refer: Html.DisplayFor not posting values to controller in ASP.NET MVC 3
if you're not passing any type of data then make your data as a hidden field and after that you will received your data at your respective controller.
Thanks.

how to properly fix MVVM scaffolding issue with duplicate name tag? (ASP Core 2.2)

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!

ASP.NET Core 2.1 Unobtrusive Ajax Validation Not Working With Partial View Form Swap

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
{
}

Showing the data of the right side of the TimeLine

How can I show the description Data on the left or the right side of the timeline acording of it Candidate.Saving result?
If it's Candidate.Saving == true it should be on the left, if it's Candidate.Saving==false it should be on the right side of the time line.
I will shou here my candidate Model And my View.
public class Candidate : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int Number { get; set; }
public string ProfileText { get; set; }
public Byte[] CV { get; set; }
public string CVNAME { get; set; }
public List<Profile> ProfileList { get; set; }
public String Description { get; set; }
public Boolean Saving { get; set; }
public string Title { get; set; }
public DateTime DateOfDescription { get; set; }
}
Here is my view:
#model HCCBPOHR.Data.Candidate
#{
ViewData["Title"] = "CandidateHistory";
}
<h2>Canidate - #Model.Name</h2>
<label>History</label>
<hr />
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-clock-o fa-fw"></i> History Of #Model.Name
</div>
#foreach (var Description in Model.Description)
{
#if (Model.Saving == true)
{
<ul>
<li class="timeline">
<div class="timeline-badge">
<i class="fa fa-check"></i>
</div>
<div class="timeline-panel">
<div class="timeline-heading">
<h4 class="timeline-title">Title</h4>
<p>
<small class="text-muted"><i class="fa fa-clock-o"></i> #Model.DateOfDescription.ToShortDateString()</small>
</p>
</div>
<div class="timeline-body">
<p>#Model.Description</p>
</div>
</div>
</li>
</ul>
}
#if (Model.Saving == false)
{
<ul>
<li class="timeline-inverted">
<div class="timeline-badge">
<i class="fa fa-check"></i>
</div>
<div class="timeline-panel">
<div class="timeline-heading">
<h4 class="timeline-title">Title</h4>
<p>
<small class="text-muted"><i class="fa fa-clock-o"></i> #Model.DateOfDescription.ToShortDateString()</small>
</p>
</div>
<div class="timeline-body">
<p>#Model.Description</p>
</div>
</div>
</li>
</ul>
}
}
</div>
#*<div class="panel-body">
<ul class="timeline">
<li>
<li class="timeline-inverted">
Isto é para inverter o lado
<div class="timeline-badge">
<i class="fa fa-check"></i>
</div>
<div class="timeline-panel">
<div class="timeline-heading">
<h4 class="timeline-title">Title</h4>
<p>
<small class="text-muted"><i class="fa fa-clock-o"></i> #Model.DateOfDescription.ToShortDateString()</small>
</p>
</div>
<div class="timeline-body">
<p>#Model.Description</p>
</div>
</div>
</li>
</ul>
</div>
</div>*#
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal" data-whatever="#Model.Saving"> Add History Description</button>
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-body">
<form asp-action="CandidateHistory">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
<label>Selects</label>
<select asp-for="Saving" class="form-control">
<option value="false">Candidate </option>
<option value="true">Hitachi</option>s
</select>
</div>
<div class="form-group">
<label asp-for="DateOfDescription" class="form-group"></label>
<input asp-for="DateOfDescription" class="form-group" />
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
What I've tried to do:
I have tried to make a foreach method where the side depends of the Candidate.Saving, But the result was this:
I have managed to fix it.
This is how I did It:
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-clock-o fa-fw"></i> History Of #Model.Name
</div>
<div class="panel-body">
#if (Model.Saving == true)
{
<ul class="timeline">
<li class="timeline">
<div class="timeline-badge">
<i class="fa fa-check"></i>
</div>
<div class="timeline-panel">
<div class="timeline-heading">
<h4 class="timeline-title">Title</h4>
<p>
<small class="text-muted"><i class="fa fa-clock-o"></i>
#Model.DateOfDescription.ToShortDateString()</small>
</p>
</div>
<div class="timeline-body">
<p>#Model.Description</p>
</div>
</div>
</li>
</ul>
}
#if (Model.Saving == false)
{
<ul class="timeline">
<li class="timeline-inverted">
<div class="timeline-badge">
<i class="fa fa-check"></i>
</div>
<div class="timeline-panel">
<div class="timeline-heading">
<h4 class="timeline-title">Title</h4>
<p>
<small class="text-muted"><i class="fa fa-clock-o"></i> #Model.DateOfDescription.ToShortDateString()</small>
</p>
</div>
<div class="timeline-body">
<p>#Model.Description</p>
</div>
</div>
</li>
</ul>
}
</div>
</div>
I only have on doubt right now, to be able to add more than one Description for the candidate I should Create a list off Descriptions
and do a foreach method in the view right?
Right now my controller looks like this:
public IActionResult CandidateHistory(int Id)
{
using (var applicationcontext = new ApplicationContext())
{
var candidate = applicationcontext.Candidates.Where(s => s.Id.Equals(Id)).SingleOrDefault();
if (candidate == null)
{
return NotFound();
}
Candidate candidates = new Candidate();
candidates.DescriptionList.Add(candidate);
candidates.DescriptionList.Add(new Candidate { Description = candidate.Description });
candidates.DescriptionList.Add(new Candidate { Title = candidate.Title });
candidates.DescriptionList.Add(new Candidate { DateOfDescription = candidate.DateOfDescription });
candidates.DescriptionList.Add(new Candidate { Saving = candidate.Saving });
applicationcontext.Candidates.Add(candidates);
return View();
}
}
[HttpPost, ActionName("CandidateHistory")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CandidateHistoryPost(int? id, Candidate candidatelist)
{
if (id == null)
{
return NotFound();
}
using (var context = new ApplicationContext())
{
var candidateUpdate = await context.Candidates.SingleOrDefaultAsync(s => s.Id == id);
if (await TryUpdateModelAsync<Candidate>(candidateUpdate, "", s => s.Description, s => s.Title, s => s.DateOfDescription, s => s.Saving))
{
try
{
await context.SaveChangesAsync();
return RedirectToAction(nameof(CandidateHistory));
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
}
return View(candidateUpdate);
}
}
I'm trying to output an Candidate.DescriptionList to the CandidateHistory View so that way I can save into that list all the descriptions, description title, date and "saving", so i can do a foreach method to output all the description of that user.
But now I have this error:
System.NullReferenceException: 'Object reference not set to an instance of an object.
HCCBPOHR.Data.Candidate.DescriptionList.get returned null.`
I have managed to fix it.
This is how I did It:
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-clock-o fa-fw"></i> History Of #Model.Name
</div>
<div class="panel-body">
#if (Model.Saving == true)
{
<ul class="timeline">
<li class="timeline">
<div class="timeline-badge">
<i class="fa fa-check"></i>
</div>
<div class="timeline-panel">
<div class="timeline-heading">
<h4 class="timeline-title">Title</h4>
<p>
<small class="text-muted"><i class="fa fa-clock-o"></i>
#Model.DateOfDescription.ToShortDateString()</small>
</p>
</div>
<div class="timeline-body">
<p>#Model.Description</p>
</div>
</div>
</li>
</ul>
}
#if (Model.Saving == false)
{
<ul class="timeline">
<li class="timeline-inverted">
<div class="timeline-badge">
<i class="fa fa-check"></i>
</div>
<div class="timeline-panel">
<div class="timeline-heading">
<h4 class="timeline-title">Title</h4>
<p>
<small class="text-muted"><i class="fa fa-clock-o"></i> #Model.DateOfDescription.ToShortDateString()</small>
</p>
</div>
<div class="timeline-body">
<p>#Model.Description</p>
</div>
</div>
</li>
</ul>
}
</div>
</div>
I only have on doubt right now, to be able to add more than one Description for the candidate I should Create a list off Descriptions
and do a foreach method in the view right?

How can i use Remote Attribute in Mvc 5?

I developing business application in ASP MVC 5. I was trying to use Remote attribute but its not working.
Metadata class - where i put attribute
[MetadataType(typeof(Departmanmetadata))]
public partial class DepartmanTable
{
}
public class Departmanmetadata
{
[Key]
[DisplayName("Deparman No")]
public int DepartmanID { get; set; }
[Required(ErrorMessage = "Bu alan boş bırakılamaz!")]
[StringLength(50, MinimumLength = 2, ErrorMessage = "Lütfen 2 ile 50 karakter arasında veri giriniz!")]
[DisplayName("Departman Adı")]
[Remote("IsNameAvaible", "Departman", ErrorMessage = "Sistemde zaten var!")]
public string DepartmanName { get; set; }
}
Controller
public JsonResult IsNameAvaible(string name)
{
return Json(!db.DepartmanTable.Any(a => a.DepartmanName == name), JsonRequestBehavior.AllowGet);
}
View
<link href="~/css/eklemeler.css" rel="stylesheet" />
<script src="~/scripts/jquery-1.10.2.min.js"></script>
<script src="~/scripts/jquery.validate.unobtrusive.min.js"></script>
<script src="~/scripts/jquery.validate.min.js"></script>
<div class="row wrapper border-bottom white-bg page-heading">
<div class="col-lg-10">
<h2>DEPARTMAN</h2>
<ol class="breadcrumb">
<li>
<a>Home</a>
</li>
<li>
<a>Departman</a>
</li>
<li class="active">
<strong>Departman Add</strong>
</li>
</ol>
</div>
<div class="col-lg-2">
</div>
</div>
<br />
<div class="row">
<div class="col-lg-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>Departman Add</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form class="form-horizontal form-label-left" method="post" action="~/Departman/Create" novalidate>
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="item form-group">
#Html.LabelFor(model => model.DepartmanName, htmlAttributes: new { #class = "control-label col-md-3 col-sm-3 col-xs-12" })
<div class="col-md-6 col-sm-6 col-xs-12">
#Html.EditorFor(model => model.DepartmanName, new { htmlAttributes = new { #class = "form-control col-md-7 col-xs-12" } })<span class="fa fa-long-arrow-up form-control-feedback right" aria-hidden="true"></span>
#Html.ValidationMessageFor(model => model.DepartmanName, "", new { #class = "text-danger" })
</div>
</div>
<br />
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Ekle" class="btn btn-primary" /> |
<i class="fa fa-share fa-2x"></i> #Html.ActionLink("Geri Dön!", "Index")
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
Create button doesn't working.
I think Scripts is the problem. Here the scripts.
<script src="~/scripts/jquery-1.10.2.min.js"></script>
<script src="~/scripts/jquery.validate.unobtrusive.min.js"></script>
<script src="~/scripts/jquery.validate.min.js"></script>
What might be the issue?
The property you apply the attribute to is named DepartmanName and generates the following html
<input type="text" name="DepartmanName" ..... />
which means you controller method must have a matching parameter. Change it to
public JsonResult IsNameAvaible(string departmanName)
{
....
}

Categories