Unable to get updated Checkboxes from uploaded Excel file in asp.net core 3.1 mvc - asp.net-core-mvc

As soon as the Excel file is uploaded and clicked on Submit button, the data in the Excel file will be populated as shown in the below screenshot. Some checkboxes will be selected while some others are not, based on the conditions written in the program. Till here everything is working fine.
End user selects/un-selects checkboxes, and clicks on Process Hold Reasons button. Now, I want updated model with modified checkboxes values. I'm not even understanding how and where to write the code to achieve this. Tried number of ways, but unable to succeed.
Can someone please suggest me on this!
Below is the code in Index.cshtml page.
#model SupportTool.Models.ViewModels.EligibilityIssuesViewModel
#{
ViewData["Title"] = "Eligiblity Issues";
}
<style>
.Success {
font-weight: bold;
color: green;
font-size: 15px;
}
.Error {
font-weight: bold;
color: red;
font-size: 15px;
}
td {
height: 20px;
}
</style>
<br />
<br />
<div>
<span class="Success">#ViewBag.Success</span>
<span class="Error">#ViewBag.Error</span>
</div>
<form asp-controller="EligibilityIssues" asp-action="Index" method="post" class="form-horizontal" role="form" enctype="multipart/form-data">
<div class="form-group">
<div class="row">
<div class="alert-danger" asp-validation-summary="ModelOnly"></div>
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<p>Upload Excel file:</p>
<div class="custom-file">
<input asp-for="ExportedExcelFile" class="form-control custom-file-input" />
<label class="custom-file-label">Choose file...</label>
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input type="submit" value="Submit" class="btn-success" />
</div>
</div>
</div>
#section Scripts {
<script>
$(function () {
$('.custom-file-input').on('change', function () {
var fileName = $(this).val().split('\\').pop();
$(this).next('.custom-file-label').html(fileName);
});
});
</script>
}
#if (Model != null && Model.HoldReasons != null)
{
<h1>Eligibility issues should be shown here</h1>
<table>
<thead>
<tr>
<th> </th>
<th>Hold Reasons</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.HoldReasons.Count; i++)
{
<tr>
<td>
#Html.CheckBox("holdReason[" + i + "].HoldReasonShouldBeChecked", Model.HoldReasons[i].HoldReasonShouldBeChecked)
#if (!Model.HoldReasons[i].HoldReasonShouldBeChecked)
{
<span style="border: 1px solid red">
(#Model.HoldReasons[i].StateName)#Model.HoldReasons[i].HoldReason
</span>
}
else
{
<span>(#Model.HoldReasons[i].StateName)#Model.HoldReasons[i].HoldReason</span>
}
#Html.Hidden("holdReason[" + i + "].WorkOrderId", Model.HoldReasons[i].WorkOrderId)
</td>
</tr>
}
</tbody>
</table>
<input type="submit" asp-action="ProcessHoldReasons" value="Process Hold Reasons" class="btn btn-primary" />
}
</form>
Below is the Code in HomeController
namespace SupportTool.Controllers
{
public class EligibilityIssuesController : Controller
{
private readonly IEligibilityIssuesBLL _bll;
private readonly IWebHostEnvironment _hostEnvironment;
public EligibilityIssuesController(IEligibilityIssuesBLL bll, IWebHostEnvironment hostEnvironment)
{
_bll = bll ?? throw new ArgumentNullException(nameof(bll));
_hostEnvironment = hostEnvironment ?? throw new ArgumentNullException(nameof(hostEnvironment));
}
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index(EligibilityIssuesViewModel model)
{
try
{
if (ModelState.IsValid)
{
if (model.ExportedExcelFile != null)
{
var folder = Path.Combine(_hostEnvironment.WebRootPath, "uploaded_excel_files");
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
var incomingFileName = model.ExportedExcelFile.FileName;
model.UploadedExcelFileName = Path.Combine(folder, incomingFileName);
using (var fileStream = new FileStream(model.UploadedExcelFileName, FileMode.Create))
{
model.ExportedExcelFile.CopyTo(fileStream);
}
model.HoldReasons = _bll.GetHoldReasons(model);
}
return View(model);
}
return View(model);
}
catch (Exception ex)
{
ViewBag.Error = new HtmlString(#"Unexpected error occurred. Below is the message for your verification:<br />"
+ ex.Message);
return View(model);
}
}
}
}

Related

how to load data in table and bind the same to view Model class in .net core mvc (without refreshing the page)

I'm trying to add data in table via server but unable to succeed. I tried many things but nothing worked. I'm getting the data from the server but unable to populate that in table.. can someone please help me out?
I'm getting this :
"DataTables warning: table id=particlarsTable - Invalid JSON response. For more information about this error, please see http://datatables.net/tn/1"
Let me explain my code first:
(In View)
When I click on "Add New Particular" button, bootstrap modal will popup with input fields, those fields will send to the server then again back to view and populate the table with the same data without refreshing the whole page.
I'm stuck on the last stage, I'm getting the data on UI/View in ajax success handler but unable to load that data in table.
here is my view :
<form class="form-horizontal" method="post" id="createAdHocForm">
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label>Select Flat</label>
<select asp-for="AdHocInvoice.FlatRid" asp-items="#(new SelectList(Model.Flats,"FlatRid","FlatNumber"))" class="form-control form-control-sm selectpicker" data-live-search="true">
<option selected disabled value="">Select One</option>
</select>
</div>
</div>
</div>
<table class="table table-sm table-responsive-md nowrap w-100" id="particlarsTable">
<thead class="thead-light">
<tr>
<th>Particulars</th>
<th>Amount</th>
<th></th>
</tr>
</thead>
<tbody class="bg-white">
#foreach (var item in Model.AdHocInvoice.FlatInvoiceItems)
{
<tr>
<td>#item.Particular</td>
<td>#item.Amount</td>
<td></td>
</tr>
}
</tbody>
</table>
<button class="btn btn-sm btn-outline-info" type="button" onclick="showParticularForm()">Add New Particular</button>
<hr />
<div class="row text-center">
<button class="btn btn-sm btn-outline-success mx-auto" type="submit">Submit</button>
</div>
</form>
bootstrap modal to fill the table :
<div class="modal fade" id="particularWindow">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Invoice Items</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div class="modal-body">
<form class="form-horizontal" method="post" id="particularForm">
<div class="row">
<div class="col-md-5">
<div class="form-group">
<label>Enter Particular</label>
<input id="particularName" name="particular" class="form-control form-control-sm" required />
</div>
</div>
<div class="col-md-5">
<label>Amount</label>
<input id="particularAmount" name="amount" class="form-control form-control-sm" required />
</div>
<div class="col-md-2">
<button class="btn btn-sm btn-outline-success mt-4" id="btnParticularSubmit" type="button" onclick="addParticular()">Add</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
Scripts:
<link rel="stylesheet" href="~/DataTables/datatables.min.css" />
<script type="text/javascript" src="~/DataTables/datatables.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#particlarsTable').DataTable();
});
function showParticularForm() {
$('#particularWindow').modal('show');
}
function addParticular() {
var particular = $('#particularName').val();
var amount = $('#particularAmount').val();
$.ajax({
url: '/FlatInvoice/AddParticular',
data: 'particular=' + particular + '&amount=' + amount,
success: function (response) {
$('#particlarsTable').DataTable().ajax.reload()
}
});
}
</script>
Action Method on controller:
public JsonResult AddParticular(string particular, decimal amount)
{
_flatInvoiceViewModel.AdHocInvoice.FlatInvoiceItems.Add(new FlatInvoiceItem { Particular = particular, Amount = amount });
return Json(_flatInvoiceViewModel);
}
Class/Model:
public class FlatInvoiceItem
{
public Guid FlatInvoiceItemRid { get; set; }
public Guid FlatInvoiceRid { get; set; }
public Guid FundRuleRid { get; set; }
public string Particular { get; set; }
public decimal Amount { get; set; }
}
NOTE: this code is in initial phase, I need that input data on server for further process(will add code later) so don't want to use $.('#table_body_id').append("<tr>..</tr>"); type of code.
1.Firstly,reload() is used to send request back to the method which display the DataTable initially(e.g. name this method Test).
2.Secondly,From your AddParticular method,you just add a data to the list but the lifetime is just one request,so when you reload to Test method,the list still contains the initial data without new data.
Conclusion: I suggest that you could save data to database.And get data from database.
Here is a working demo about how to use DataTabale:
1.Model:
public class Test
{
public int Id { get; set; }
public AdHocInvoice AdHocInvoice { get; set; }
}
public class AdHocInvoice
{
public int Id { get; set; }
public string Name { get; set; }
public List<FlatInvoiceItem> FlatInvoiceItems { get; set; }
}
public class FlatInvoiceItem
{
public int Id { get; set; }
public Guid FlatInvoiceItemRid { get; set; }
public Guid FlatInvoiceRid { get; set; }
public Guid FundRuleRid { get; set; }
public string Particular { get; set; }
public decimal Amount { get; set; }
}
public class ViewModel
{
public string Particular { get; set; }
public decimal Amount { get; set; }
}
2.View:
<form class="form-horizontal" method="post" id="createAdHocForm">
<table class="table table-sm table-responsive-md nowrap w-100" id="particlarsTable">
<thead class="thead-light">
<tr>
<th>Particulars</th>
<th>Amount</th>
<th></th>
</tr>
</thead>
#*<tbody class="bg-white">
#foreach (var item in Model.AdHocInvoice.FlatInvoiceItems)
{
<tr>
<td id="particular">#item.Particular</td>
<td id="amount">#item.Amount</td>
<td></td>
</tr>
}
</tbody>*# //DataTable no need to add this tbody
</table>
<button class="btn btn-sm btn-outline-info" type="button" onclick="showParticularForm()">Add New Particular</button>
<hr />
<div class="row text-center">
<button class="btn btn-sm btn-outline-success mx-auto" type="submit">Submit</button>
</div>
</form>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css">
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js"></script>
<script>
$(document).ready(function () {
//$('#particlarsTable').DataTable();
$('#particlarsTable').DataTable({
ajax: {
type: 'GET',
dataType: 'JSON',
url: '#Url.Action("Test", "Home")',
},
columns: [
{ 'data': 'particular' },
{ 'data': 'amount' }
]
})
});
function showParticularForm() {
$('#particularWindow').modal('show');
}
function addParticular() {
var particular = $('#particularName').val();
var amount = $('#particularAmount').val();
$.ajax({
url: '/Home/AddParticular',
data: 'particular=' + particular + '&amount=' + amount
}).done(function (data) {
$('#particularWindow').modal('hide');
$('#particlarsTable').DataTable().ajax.reload();
});
}
</script>
3.Controller:
public class HomeController : Controller
{
private readonly MyDbContext _context;
public HomeController(MyDbContext context)
{
_context = context;
}
public IActionResult Index()
{
return View();
}
public JsonResult Test()
{
var _flatInvoiceViewModel = _context.Test.Include(i=>i.AdHocInvoice)
.ThenInclude(a=>a.FlatInvoiceItems).Where(i => i.Id == 1).FirstOrDefault();
var list = new List<ViewModel>();
foreach (var item in _flatInvoiceViewModel.AdHocInvoice.FlatInvoiceItems)
{
var model = new ViewModel() { Amount = item.Amount, Particular = item.Particular };
list.Add(model);
}
return Json(new { data = list });
}
public void AddParticular(string particular, decimal amount)
{
var _flatInvoiceViewModel = _context.Test.Include(i => i.AdHocInvoice)
.ThenInclude(a => a.FlatInvoiceItems).Where(i => i.Id == 1).FirstOrDefault();
_flatInvoiceViewModel.AdHocInvoice.FlatInvoiceItems.Add(new FlatInvoiceItem { Particular = particular, Amount = amount });
_context.SaveChanges();
}
4.Result:
Reference:
https://datatables.net/examples/data_sources/ajax.html
https://stackoverflow.com/a/59449895/11398810

CommandStateChange event won't fire in HTA iframe

I have created an HTML-application to search through directories for a userdefined search term and return the results in a list. As part of this tool I gave the user the possibility to open the resulting folder directly inside an iframe in the application.
The tricky part now is to enable / disable the forward and backward-navigation buttons when the commandstate of the iframe changes.
I somehow can't add it to my iframe-element! Other events like BeforeNavigate2 otherwise work fine...
My code snippet:
<script>
var EmbededExplorer;
var EmbededExplorerObj;
var FSO = new ActiveXObject("Scripting.FileSystemObject");
function Body_OnLoad()
{
InitEmbededExplorer();
}
function InitEmbededExplorer()
{
var ExplorerDiv = document.getElementById("MyIFrameDiv");
EmbededExplorer = document.createElement("IFRAME");
EmbededExplorer.setAttribute("type", "text/html");
EmbededExplorer.setAttribute("style", "float:left;width:90%;height:100%;border:0px");
ExplorerDiv.insertBefore(EmbededExplorer, ExplorerDiv.children(2));
EmbededExplorer.setAttribute("src", "C:\");
EmbededExplorerNavigateTo("C:\SomeDirectory\HighlightedFile.txt");
}
function EmbededExplorerNavigateTo(Destination)
{
var E = null;
var Path;
Path = FSO.GetParentFolderName(Destination);
if (FSO.FolderExists(Destination))
{
Path = Destination;
}
else if (FSO.FileExists(Destination))
{
Path = FSO.GetParentFolderName(Destination);
}
else
{
alert("Unable to locate the file or directory.");
}
try
{
EmbededExplorer.setAttribute("src", Path);
}
catch (Excep)
{
E = Excep;
}
if (E != null)
{
alert("Could not open directory. Please refer to the administrator.");
}
else
{
//UpdateFolderAddressBar(document.getElementById('MyIFrameAddressBar'), Path);
setTimeout(function() { RegisterEmbededBrowserAfterLoading(Destination); }, 100);
}
}
function RegisterEmbededBrowserAfterLoading(SelectItem)
{
if (EmbededExplorer.contentWindow.document.readyState == "loading")
{
setTimeout(function() { RegisterEmbededBrowserAfterLoading(SelectItem); }, 100);
}
else
{
//EmbededExplorer.contentWindow.document.childNodes[0] contains the actual (shell-)explorer-component of the iframe (a WebBrowser2 class component)
EmbededExplorer.contentWindow.document.childNodes[0].RegisterAsBrowser = true;
EmbededExplorerObj = EmbededExplorer.contentWindow.document.childNodes[0];
//All my attempts to add the CommandStateChange-Event
EmbededExplorer.attachEvent("CommandStateChange", EmbededExplorerObj_CommandStateChange);
EmbededExplorerObj.attachEvent("CommandStateChange", EmbededExplorerObj_CommandStateChange);
EmbededExplorer.CommandStateChange = function(a, b) {alert("CommandStateChange fired!!");};
EmbededExplorerObj.CommandStateChange = function(a, b) {alert("CommandStateChange fired!!");};
//The following event works fine...
EmbededExplorerObj.attachEvent("BeforeNavigate2", EmbededExplorerObj_BeforeNavigate2);
if (SelectItem)
{
EmbededExplorerObj.Document.SelectItem(SelectItem.replace(/\//g, "\\"), 16 + 8 + 1);
}
}
}
function EmbededExplorerObj_CommandStateChange(Command, Enable)
{
alert("CommandStateChange fired!!");
//Code to enable / disable my forward / backward navigation buttons
}
function EmbededExplorerObj_BeforeNavigate2(Obj, URL)
{
alert("BeforeNavigate2 fired!!");
//UpdateFolderAddressBar(document.getElementById('MyIFrameAddressBar'), URL);
}
</script>
Documentation of the required event: https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa768328%28v%3dvs.85%29
For validation purpose, I rebuilt the event in VBA and it works just fine.
Private WithEvents Win As WebBrowser
Sub SetWin()
Dim WinShell 'As New Shell32.Shell
Set WinShell = CreateObject("Shell.Application")
Set Win = WinShell.Windows(1)
End Sub
Private Sub Win_CommandStateChange(ByVal Command As Long, ByVal Enable As Boolean)
Stop 'fires 3x after every navigation, each time containing my desired state-information in the 'Command' parameter
End Sub
EDIT:
As requested I will add the structure of my application below:
<BODY Onload="Initialisieren()" OnContextMenu="return false;" OnClick="VerbergeContextMenus()" OnKeyPress="Body_OnKeyPress()" OnFocus="document.getElementById('ModNr').focus()" OnBeforeUnload="TempLeeren();">
<TABLE Style="Width: 100%; Height: 100vh" border="0">
<TR Style="Height:auto">
<TD Style="Width: 50%; Vertical-Align: text-top">
<LABEL For="ModNr" Class="Überschrift" OnSelectStart="return false;">Suchbegriff:</LABEL><INPUT ID="ModNr" Type="Text" UseContextMenuId="CopyPasteContextMenu" onContextMenu="ZeigeContextMenu(this, event);return false;">
</TD>
<TD Style="Width: 50%; Vertical-Align: text-top">
<DIV Class="Überschrift" OnSelectStart="return false;">Zuletzt geöffnet:</DIV>
</TD>
</TR>
<TR Style="Height:auto">
<TD>
<DIV Class="Bereich" ID="Auswahl" OnSelectStart="return false;">
<TABLE Style="Width: 100%" ID="AuswahlTabelle">
<TR></TR>
<TR Style="Height: 30px">
<TD ColSpan="99" Style="Vertical-Align: Bottom"><INPUT ID="SuchKnopf" Type="Button" Value="Suche"></TD>
</TR>
</TABLE>
</DIV>
<DIV Class="Bereich" ID="DatenBankAuswahl" OnSelectStart="return false;">
<TABLE Style="Width: 100%; Margin: 0px; Padding: 0px" ID="DatenBankAuswahlTabelle">
<TR></TR>
</TABLE>
</DIV>
</TD>
<TD Style="Vertical-Align: Top" rowSpan="99">
<DIV Class="ListenBereich" Name="Historie" ID="Historie" Type="Radio" size="20" OnSelectStart="return false;">
</DIV>
</TD>
</TR>
<TR Style="Height:auto">
<TD>
<DIV Class="Überschrift" OnSelectStart="return false;" Id="Überschrift">Suchergebnisse:</DIV>
</TD>
</TR>
<TR>
<TD>
<DIV Class="ListenBereich" Name="Ergebnisse" ID="Ergebnisse" OnSelectStart="return false;">
</DIV>
<DIV Class="Bereich" Name="MyIFrameDiv" ID="MyIFrameDiv" style="display:none">
<BUTTON ID="ExplorerCloseBtn" Title="Schließen" Style="float:right;min-width:1px;width:5%;height:100%;border-radius:0px 8px 8px 0px;margin:0px;padding:0px" OnClick="SuchergebnisseAnzeigen();"><div style="position:relative;font-family:'wingdings 2';font-Size:20px">T</DIV></BUTTON>
<DIV Style="float:left;min-width:1px;width:5%;height:100%;margin:0px;padding:0px">
<BUTTON disabled="true" ID="ExplorerNavigateRootBtn" Title="Zum übergeordneten Verzeichnis" Style="float:left;min-width:1px;width:100%;height:33%;border-radius:8px 0px 0px 0px;margin:0px;padding:0px" OnClick="ExplorerNavToRoot();"><div style="position:relative;font-family:'wingdings 3';font-Size:20px">Í</DIV></BUTTON>
<BUTTON disabled="false" ID="ExplorerNavigateGoBack" Title="Einen Schritt zurück" Style="float:left;min-width:1px;width:100%;height:34%;border-radius:0px 0px 0px 0px;margin:0px;padding:0px" OnClick="try{ EmbededExplorerObj.goBack(); } catch(Excep) { }"><div style="position:relative;font-family:'wingdings 3';font-Size:20px">§</DIV></BUTTON>
<BUTTON disabled="false" ID="ExplorerNavigateGoForward" Title="Einen Schritt vor" Style="float:left;min-width:1px;width:100%;height:33%;border-radius:0px 0px 0px 8px;margin:0px;padding:0px" OnClick="try{ EmbededExplorerObj.goForward(); } catch(Excep) { }"><div style="position:relative;font-family:'wingdings 3';font-Size:20px">¨</DIV></BUTTON>
</DIV>
</DIV>
</TD>
</TR>
</TABLE>
</BODY>

How do you connect a POST to a different razor page loaded via AJAX into a modal popup?

Edit: Have marked up where the error in the original code was stopping this from working.
I can find plenty of info and examples of this on MVC, but doesn't seem to apply for Razor Pages?
Simple scenario: I have a page (FooList) showing a list of Foo items. Each has an Edit button. This opens a modal popup with the layout (and data) coming from a second page (FooEdit).
The Edit form appears and populates fine, but I can't work out how to get it to post the data back to the FooEdit code behind?
List page, FooList.cshtml
#page
#model Pages.FooListModel
<table>
#foreach (var item in Model.FooListVM)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
<a onclick="openModal(#item.ID);">Edit</a>
</td>
</tr>
}
</table>
<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header border-bottom-0">
<h5 class="modal-title" id="exampleModalLabel">Edit Foo</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form> <---- Edit: ** This shouldn't be here **
<div class="modal-body">
</div>
</form> <---- Edit
</div>
</div>
</div>
<script>
function openModal(i) {
$.get("FooEdit?id="+i,
null,
data => {
$("#editModal").modal("show");
$("#editModal .modal-body").html(data);
});
};
</script>
Code behind, FooList.cshtml.cs
public class FooListModel : PageModel
{
public IList<FooListVM> FooListVM { get; set; }
public void OnGet()
{
FooListVM = new List<FooListVM>
{
new FooListVM { ID = 1, Name = "Foo 1" },
new FooListVM { ID = 2, Name = "Foo2" }
};
}
}
public class FooListVM
{
public int ID { get; set; }
public string Name { get; set; }
}
Second page for the popup, FooEdit.cshtml
#page
#model Pages.FooEditModel
#(Layout=null)
<form method="post">
<input asp-for="FooEditVM.Name" class="form-control" /><br />
<input asp-for="FooEditVM.Stuff1" class="form-control" /><br />
<input asp-for="FooEditVM.Stuff2" class="form-control" /><br />
<input type="submit" value="Save"/>
</form>
And the code behind for the popup, FooEdit.cshtml.cs
public class FooEditModel : PageModel
{
[BindProperty]
public FooEditVM FooEditVM { get; set; }
public void OnGet(int id)
{
FooEditVM = new FooEditVM
{
Name = $"This is item {id}",
Stuff1 = "Stuff1",
Stuff2 = "Stuff2"
};
}
public void OnPost(int id)
{
// How do we get to here???
var a = FooEditVM.Name;
}
}
public class FooEditVM
{
public string Name { get; set; }
public string Stuff1 { get; set; }
public string Stuff2 { get; set; }
}
I've been through all the MS Tutorial stuff on Asp.net Core 2.2, but it doesn't seem to cover this.
Also as a side question, although it works, is there a "ASP helper tag" way of doing the ajax bit?
Have realised the problem was the 'form' tag in the Modal Dialog markup that was clashing the 'form' tag from the partial page. Removing it fixed everything using:
In FooEdit.cshtml
<form id="editForm" asp-page="FooEdit">
. . .
</form>
In FooEdit.cshtml.cs
public void OnPost()
{
// Fires in here
}
I'm pretty sure the fooedit page is going to need some jQuery to handle this.
See below for what I would do in the fooedit page.
#page
#model Pages.FooEditModel
#(Layout=null)
<form id=fooedit method="post" action="FooEdit">
<input asp-for="FooEditVM.Name" class="form-control" /><br />
<input asp-for="FooEditVM.Stuff1" class="form-control" /><br />
<input asp-for="FooEditVM.Stuff2" class="form-control" /><br />
<input type="submit" value="Save"/>
</form>
<SCRIPT language="JavaScript" type="text/Javascript">
<!--
$(document).ready(function(e) {
$("#fooedit").submit(function(e) {
e.preventDefault();
var form_data = $(this).serialize();
var form_url = $(this).attr("action");
var form_method = $(this).attr("method").toUpperCase();
$.ajax({
url: form_url,
type: form_method,
data: form_data,
cache: false,
success: function(returnhtml){
$("#editModal.modal-body").html(returnhtml);
}
});
});
});
</SCRIPT>

Prevent knockout validation from evaluating on initial load

I have a simple view-model with a few required attributes... I want each input to highlight red if the corresponding property is not valid, but I don't want this highlighting to display when the page is initially loaded... only when a value changes or when the user tries to save / continue...
Right now it's validating the view-model on initial load because I'm specifying data-bind="css: { error: name.isValid() == false }", but I don't know of any other way to get this to work dynamically (similar to how jQuery unobtrusive validation works)...
var foo = { name: ko.observable().extend({required: true}) };
<div data-bind="css: { error: !name.isValid() }">
<input type="text" data-bind="value: name" />
</div>
Any ideas on how to make this work would be appreciated... Thanks!
A better approach is to configure knockout validation to decorate the element with the validationElement class. This is done by adding this configuration option:
ko.validation.configure({ decorateElement: true });
Click here to see a jsfiddle demonstrating this.
****EDIT, IN RESPONSE TO COMMENT FROM QUESTION ASKER***
If you need to decorate the parent element, a more elegant and reusable solution is to apply this custom binding to the parent element.
Javascript
ko.bindingHandlers.parentvalElement = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var valueIsValid = valueAccessor().isValid();
if(!valueIsValid && viewModel.isAnyMessageShown()) {
$(element).addClass("parentError");
}
else {
$(element).removeClass("parentError");
}
}
};
And apply the binding in your HTML like so:
<form data-bind='submit:OnSubmit'>
<label data-bind='parentvalElement:name'>
<span>Name</span>
<input data-bind="value: name" />
</label>
<input type='submit' value='submit' />
<form>
Take a look at this updated jsfiddle to see it in action.
So, here is the solution I came up with:
var Foo = function()
{
this.name = ko.observable().extend({required: true}).isModified(false);
this.validate: function()
{
if (!this.isValid())
{
//... loop through all validated properties and set .isModified(true)
return false;
}
return true;
};
ko.validation.group(foo);
};
var Bar = function()
{
this.foo = new Foo();
this.errors = ko.observableArray([]); //<-- displays errors for entire page
this.save = function()
{
if (!this.foo.validate())
{
this.errors(ko.toJS(this.foo.errors()));
}
};
}
ko.applyBindings(new Bar());
And here is the markup...
<div data-bind="with: foo">
<div class="control-group"
data-bind="css: { error: name.isModified() && !name.isValid() }">
<label class="control-label">Name<span class="help-inline">*</span></label>
<div class="controls">
<input type="text" class="input-block-level" placeholder="Name"
data-bind="value: name, event: { blur: function () { name.isModified(true); }}" />
</div>
</div>
<div class="alert alert-error"
data-bind="visible: $parent.errors().length > 0">
<h5>Errors!</h5>
<ul data-bind="foreach: $parent.errors()">
<li data-bind="text: $data"></li>
</ul>
</div>
</div>
<button type="submit" class="btn btn-primary" data-bind="click: save">Save</button>
and here is the CSS
.error { color: Red; font-weight: bold; }
.help-inline { display: none; }
.error .help-inline { display: inline-block; }
.error input { border-color: Red; }

MVC3 partial view does not return ajax post

The idea of the problem is that i have a "CreateINF" view then i display a pop up with that calls a partial view. In this pop up there is a grid, then when i select one of the elements of this grid i want to fill some textbox on the "CreateINF" view with the information of the element selected in the popup but this last action does not work and I dont know where is the error
this is the pop up "PopTrabajador"
#model PagedList.IPagedList<Sistema_GEC_v3.Models.TrabajadorExterno>
#{
ViewBag.Title = "Buscar Reserva";
}
<h1><strong>Resultado Búsqueda</strong></h1>
<br />
<table id="MainContent_Table2" rules="all" style="border-color: Black; width: 900px; margin-bottom: 0px;" border="1">
<tr>
<th style="font-weight: bold; width: 150px;" align="center">
</th>
<th style="font-weight: bold; width: 150px;" align="center">
#Html.LabelFor(modelItem => Model.First().vcNombre)
</th>
<th style="font-weight: bold; width: 150px;" align="center">
#Html.LabelFor(modelItem => Model.First().vcApellidoPaterno)
</th>
<th style="font-weight: bold; width: 150px;" align="center">
#Html.LabelFor(modelItem => Model.First().vcApellidoMaterno)
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td align="center">
#using (Ajax.BeginForm("DevolverTrabajador", "Infracciones",
null
, new AjaxOptions
{
UpdateTargetId = "update-message",
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
OnSuccess = "searchSuccess"
}, new { #id = "searchForm"+#item.idTrabajadorExterno}))
{
<div id="update-message" class="error invisible"></div>
<input data-val="true" data-val-number="The field idTrabajadorExterno must be a number." data-val-required="El campo idTrabajadorExterno es obligatorio." id="idTrabajadorExterno" name="idTrabajadorExterno" type="hidden" value="#item.idTrabajadorExterno" />
#Html.HiddenFor(model => item.idTrabajadorExterno, new { id = "idTrabajadorExterno" + #item.idTrabajadorExterno })
#Html.HiddenFor(model => item.vcNombre, new { id = "vcNombre" + #item.idTrabajadorExterno })
<input type="submit" value="Seleccionar" />
}
</td>
<td style="font-weight: normal; width: 150px;" align="center">
#Html.DisplayFor(modelItem => item.vcNombre)
</td>
<td style="font-weight: normal; width: 150px;" align="center">
#Html.DisplayFor(modelItem => item.vcApellidoPaterno)
</td>
<td style="font-weight: normal; width: 150px;" align="center">
#Html.DisplayFor(modelItem => item.vcApellidoMaterno)
</td>
</tr>
}
</table>
this is the view "CreateINF"
#model Sistema_GEC_v3.Models.Infraccion
#{
ViewBag.Title = "Create";
}
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<link href="#Url.Content("~/Content/themes/base/jquery.ui.all.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
<script type="text/javascript">
var linkObj;
//$(document).ready(function () {
$(function () {
$(".busqueda").button();
$('#dialog').dialog({
autoOpen: false,
width: 930,
resizable: false,
title: 'hi there',
modal: true
});
$(".busqueda").click(function () {
linkObj = $(this);
var dialogDiv = $('#dialog');
var viewUrl = linkObj.attr('href');
$.get(viewUrl, function (data) {
dialogDiv.html(data);
dialogDiv.dialog('open');
});
return false;
});
});
function searchSuccess() {
var id = $("#update-message").html();
var parent = linkObj.closest("tr");
document.getElementById('tipoTrabajador').value = $("#vcNombre" + id.toString()).val();
$('#dialog').dialog('close');
}
function actualizarVentanaModal(viewUrl) {
var dialogDiv = $('#dialog');
$.get(viewUrl, function (data) {
dialogDiv.html(data);
});
}
// });
</script>
<div id="commonMessage"></div>
<div id="dialog" title="Seleccionar Cliente"></div>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Infraccion</legend>
<div class="editor-label">
#Html.LabelFor(model => model.vcCodigoInfraccion)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.vcCodigoInfraccion, new { id = "tipoTrabajador" })
#Html.ValidationMessageFor(model => model.vcCodigoInfraccion)
</div>
<a class="busqueda" href= "#Href("~/Infracciones/PopTrabajador")"> <img src="#Href("~/Content/Images/buscar.png")" alt="0" title="Buscar" id="" border="0" height="16" width="16"/></a>
</fieldset>
<p>
<input value="Guardar" type="submit" name = "button"/>
</p>
}
And this is the controller
public ActionResult PopTrabajador(int? page, string tipoTrabajador)
{
int tamPag = 10;
int numPag = page ?? 1;
var trabajadoresE = dbTExterno.trabajadoresExternos.OrderBy(t => t.vcNombre);
return PartialView("PopTrabajador", trabajadoresE.ToPagedList(numPag, tamPag));
}
[HttpPost]
public ActionResult DevolverTrabajador(TrabajadorExterno trabajador)
{
try
{
return Content(trabajador.idTrabajadorExterno.ToString());
}
catch (Exception e)
{
return RedirectToAction("General", "Error");
}
}
public ActionResult CreateINF()
{
return View();
}
//
// POST: /Infracciones/Create
[HttpPost]
public ActionResult CreateINF(FormCollection collection)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}

Resources