I have the following partial view.
#using System.Data;
#using System.Dynamic;
#using System.Collections.Generic;
#using System.Linq;
#model TheMProject.Models.MyViewModel
<div id="Item_Buttons">
<h2 class="alignleft">Table 1</h2>
<p class="alignright">Item Name<input type="text" name="item_name" value="#Html.ValueFor(x => x.itemname)" class="search-query" placeholder="Search" style ="width:100px"/>
<button class="btn btn-success" id="Change_Item_Name" value="Change_Item_Name" name="action:Change_Item_Name" type="button"> Change Item Name</button>
Grade<input type="text" name="item_grade" value="#Html.ValueFor(x => x.grade)" class="search-query" placeholder="Search" style="width:100px"/>
<button class="btn btn-success" id="ChangeItemGrade" value="ChangeItemGrade" name="action:Change_Grade" type="button">Change Grade</button>
Delete Record<input type="text" name="delete_item" value="#Html.ValueFor(x => x.itemname)" class="search-query" placeholder="Search" style ="width:100px"/>
<button class="btn btn-success" id="DeleteItem" value="DeleteItem" name="action:Delete_Item" type="button">Delete Record</button>
</p>
<div style="clear: both;"></div>
</div>
<section>
<div id ="firstgrid">
<table id="TTable" class="gridTable">
<thead class="gridHead">
<tr>
#Html.DisplayFor(x => x.TColumns)
</tr>
</thead>
<tbody>
#Html.DisplayFor(x => x.TRows)
</tbody>
</table>
</div>
</section>
<section>
<form id="form1">
<div id="grid">
<table id="CTable" class="gridTable">
<thead class="gridHead">
<tr>
#Html.DisplayFor(x => x.Columns)
</tr>
</thead>
<tbody>
#Html.DisplayFor(x => x.Rows)
</tbody>
</table>
</div>
</form>
</section>
<section>
<div id ="Display_Average">
<table id="AvgDisplayTable" class="gridTable">
<thead class="gridHead">
<tr>
#Html.DisplayFor(x => x.AvgColumns)
</tr>
</thead>
<tbody>
#Html.DisplayFor(x => x.AvgRows)
</tbody>
</table>
</div>
</section>
<button class="btn" id="SubmitAverage" value ="SubmitAverage" name="action:SubmitAverage" type="button">Submit Averages</button>
<div id="ItemNameDiv" title="Change Item Name">
#using (Html.BeginForm("ChangeItemName", "Home", "POST"))
{
<section>
Heat Name:<input type="text" name="itemName" value="#Html.ValueFor(x => x.heatname)" style ="width:100px"/>
Change to:<input type="text" name="updatedName" value="" style="width: 100px" />
<input type="button" id="ChangeItemName" name="ChangeItemName" value="Change" />
</section>
}
</div>
<div id="ItemGradeDiv" title="Change Item Grade">
#using (Html.BeginForm("ChangeGrade", "Home", "POST"))
{
<section>
Item Grade:<input type="text" name="grade" value="#Html.ValueFor(x => x.grade)" style ="width:100px"/>
Change to:<input type="text" name="updatedName" value="" style="width: 100px" />
<input type ="hidden" name="hiddenItem" value ="#Html.ValueFor(x => x.itemname)" />
<input type="submit" name="ChangeGrade" value="Change" />
</section>
}
</div>
<div id="DeleteItemDiv" title="Delete Item">
#using (Html.BeginForm("DeleteItem", "Home", "POST"))
{
<section>
Heat Name:<input type="text" name="itemName" value="#Html.ValueFor(x => x.itemname)" style ="width:100px"/>
<input type="submit" name="DeleteItem" value="Delete" />
</section>
}
</div>
When the Change Item Name button is clicked, an ajax post is called with jquery as seen here:
$(document).ready(function () {
$('#ChangeItemName').click(function (e) {
var tdata = $('#form1').serialize();
var origname = $('#ItemNameDiv').find('input[name="itemName"]').first().val();
var newname = $('#ItemNameDiv').find('input[name="updatedName"]').first().val();
$.ajax({
type: "POST",
data: {
mCollection: tdata,
itemName: origname,
updatedName: newname
},
url: "Home/ChangeItemName",
success: function (result) { success(result); }
});
});
function success(result) {
$('#ItemNameDiv').dialog('close');
$("#My_Partial_V").html(result);
}
});
. Upon completion, the partial view is refreshed with the updated data from the post. Including the jquery required for this post, there are other scripts required to perform other actions in the view such as hiding the divs shown below the submit average button until other buttons are clicked. After the first time the previously mentioned ajax post is performed, if the same button is clicked the post is called twice instead of once, the third time its clicked it runs 4 times, etc.
Ive tried moving the script reference
<script src="#Url.Content("~/Scripts/partialitemscripts.js")"
type="text/javascript"></script>
outside of the partial view and into the view which contains the partial as well as into the layout file. When I try these two methods everything loads correctly until the partial view is refreshed at which time all the scripts fail to be called/referenced.
You could try to move the script outside of the Partialview and into the mainview (like you said), but change it a little: instead of triggering the function when the document is loaded, put it all in a function:
function ajaxCallback(result){
$('#ChangeItemName').click(function (e) {
var tdata = $('#form1').serialize();
var origname = $('#ItemNameDiv').find('input[name="itemName"]').first().val();
var newname = $('#ItemNameDiv').find('input[name="updatedName"]').first().val();
$.ajax({
type: "POST",
data: {
mCollection: tdata,
itemName: origname,
updatedName: newname
},
url: "Home/ChangeItemName",
success: function (result) { success(result); }
});
});
}
Then you call this function from the ready event of your main view (the one that contains the partial).
To fix your button not working anymore when you've done an ajax request you've got to update your success-function to call the ajaxCallback (this would be in the partialitemscripts.js together with the ajaxCallback function):
function success(result) {
$('#ItemNameDiv').dialog('close');
$("#My_Partial_V").html(result);
ajaxCallback();
}
I'm not sure it this is the ideal solution, but I managed to get something similar working this way.
Although it is hard from the code posted to make sure. It seems that the reason the scripts fail after the first time is because you use the .html() which adds elements to the DOM and the click() event has not been binded to those new elements.
In this case you should use on() or delegate(). See the jquery documentation for how to use those. It is simple.
Related
Code searchView and PartialResultView
SearchView
#model Shared.Model.Search.GLSearch
#{
ViewData["Title"] = "Search GL";
}
<!-- start page title -->
<div class="row">
<div class="col-12">
<div class="page-title-box">
<div class="page-title-right">
<ol class="breadcrumb m-0">
<li class="breadcrumb-item">UBold</li>
<li class="breadcrumb-item">Forms</li>
<li class="breadcrumb-item active">Elements</li>
</ol>
</div>
<h4 class="page-title">Search Customer</h4>
</div>
</div>
</div>
<!-- end page title -->
<form asp-action="" asp-controller="" method="post">
<div class="row">
<div class="col-lg-12">
<div class="card-box">
<div class="form-row">
<div class="form-group col-md-2">
<label asp-for="Name" class="col-form-label"></label>
<input asp-for="Name" type="text" class="form-control" />
</div>
<div class="form-group col-md-2">
<label asp-for="Code" class="col-form-label"></label>
<input asp-for="Code" type="text" class="form-control" />
</div>
<div class="form-group col-md-3">
<label asp-for="GLSectionId" class="col-form-label">Section </label>
<select asp-for="GLSectionId" asp-items="#(new SelectList(Model.glSections,"Id","Name"))" class="form-control">
<option value="">Choose</option>
</select>
</div>
<div class="form-group col-md-3">
<label asp-for="GLGroupId" class="col-form-label">Group</label>
<select asp-for="GLGroupId" asp-items="#(new SelectList(Model.glGroups,"Id","Name"))" class="form-control">
<option value="">Choose</option>
</select>
</div>
<button type="button" id="search" class="btn btn-primary waves-effect waves-light">Search</button>
</div>
</div> <!-- end card-box -->
</div> <!-- end col -->
</div> <!-- end row -->
</form>
<div id="view-all"></div>
Search_PartiaView
#model PagedResult<Shared.Model.Masters.GLMaster.GLViewModel>
#{
}
#if (Model == null || Model.RowCount == 0)
{
<p>No results found</p>
}
else
{
<div class="col-lg-12">
<div class="card-box">
<h4 class="header-title">Customers</h4>
<p class="sub-header">
</p>
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th data-priority="1">#</th>
<th data-priority="3">Name</th>
<th data-priority="6">Code</th>
<th data-priority="6">Section</th>
<th data-priority="6">Group</th>
<th data-priority="6">
<a onclick="showInPopup('#Url.Action("AddOrEditGL","GLMaster",new {area = "Masters"},Context.Request.Scheme)','New GL')" class="btn btn-success text-white"><i class="fas fa-random"></i> New GL</a>
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Results)
{
<tr>
<th scope="row">#item.Id</th>
<td>#item.Name</td>
<td>#item.Code</td>
<td>#item.GLSection</td>
<td>#item.GLGroup</td>
<td>
<div>
<a onclick="showInPopup('#Url.Action("AddOrEditGL","GLMaster",new { area= "Masters",id = item.Id},Context.Request.Scheme)','Update GL')" class="btn btn-info text-white"><i class="fas fa-pencil-alt"></i> Edit</a>
<form asp-area="Masters" asp-action="DeleteGL" asp-route-id="#item.Id" onsubmit="return jQueryAjaxDelete(this)" class="d-inline">
<input type="submit" value="Delete" class="btn btn-danger" />
</form>
</div>
</td>
</tr>
}
</tbody>
</table>
</div> <!-- end table-responsive-->
</div> <!-- end card-box -->
</div> <!-- end col -->
<!-- Responsive Table js -->
}
Partial View (AddEditGL)
#model Shared.Model.Masters.GLMaster.GLModel
#{
Layout = null;
ViewData["Title"] = "Add Customer";
}
<div class="row">
<div class="col-lg-12">
<div class="card-box">
<form asp-action="AddOrEditGL" asp-controller="GLMaster" asp-area="Masters" asp-route-id="#Model.Id" onsubmit="return jQueryAjaxPost(this);">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="#Model.Id" />
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" type="text" class="form-control">
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label asp-for="NameLang" class="control-label"></label>
<input asp-for="NameLang" type="text" class="form-control">
<span asp-validation-for="NameLang" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-6 offset-md-3">
<input type="submit" value="Submit" class="btn btn-primary btn-block" />
</div>
</div>
</form>
</div> <!-- end card-box -->
</div> <!-- end col -->
</div> <!-- end row -->
I have View with Partial view (is for results in table) . When i click Edit button in Search_PartiaView
i need to open popup (Partial View (AddEditGL))
and data should be loaded ajax and submit the button after update.. I need to use jquery unobtrusive validation in popup and also without refresh the page ..Please let me know hw to do..Thanks
EDIT
I Have implemented similar to this Ajax crud popup
I Have Main view and Partial view. Also AddOrEdit View for Add/edit Master.
My Current solution works.. But inmy main view i have filter based on 2 filds.
After add/edit grid load all the result but if filter applied i also need to filter the grid ..
My Javascript code Here:
jQueryAjaxPost = form => {
try {
$.ajax({
type: 'POST',
url: form.action,
data: new FormData(form),
contentType: false,
processData: false,
success: function (res) {
if (res.isValid) {
$('#view-all').html(res.html) --- here actually data coming all without filter
$('#form-modal .modal-body.p-4').html('');
$('#form-modal .modal-title').html('');
$('#form-modal').modal('hide');
showAll(4, 1); --- it is the javascript fuction call to call the
api again
}
else
$('#form-modal .modal-body.p-4').html(res.html);
},
error: function (err) {
console.log(err)
}
})
//to prevent default form submit event
return false;
} catch (ex) {
console.log(ex)
}
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddOrEditGL(int id,GLModel glModel)
{
if (ModelState.IsValid)
{
var mappedGL = _mapper.Map<GLDTO>(glModel);
//Insert
if (id == 0)
{
await _glService.CreateGL(mappedGL);
}
//Update
else
{
await _glService.UpdateGL(mappedGL);
//Call Update
}
// How do i filter the based on Main view form controls
return Json(new { isValid = true, html = Helper.RenderRazorViewToString(this, "_GLViewAll", null) });
}
return Json(new { isValid = false, html = Helper.RenderRazorViewToString(this, "AddOrEditGL", glModel) });
}
my Current solution call the api again ( 2 server calls) one for update and another for call update table .. i need to do the same in single call ..Please help to do?
Note: I dont need complete solution , I only need to how to get the AddOrEditGL Controller post method Main view form control text fieds text to filter in DB
If you want to update/add and show the searched data in one request,The quick way is to copy the SearchGLPartial code to the AddOrEditGL function and pass the pageSize,pageIndex,name,code,GLSectionId and GlGroupId by ajax:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddOrEditGL(int id,GLModel glModel,string name,string code,string GLSectionId,string GlGroupId...)
{
if (ModelState.IsValid)
{
var mappedGL = _mapper.Map<GLDTO>(glModel);
//Insert or
//Update
//copy the SearchGLPartial code here and return view with data
}
return Json(new { isValid = false, html = Helper.RenderRazorViewToString(this, "AddOrEditGL", glModel) });
}
If you just do not want to remove showViewAll() jquery,I think you could set session for the filter condition when you first search the data in SearchGLPartial action.Then in your AddOrEdit action,get the session and set the correct url.Finally,you could get the url in ajax success response:
public IActionResult SearchGLPartial(string name,string code,string GLSectionId,string GLGroupId)
{
HttpContext.Session.SetString("name", name);
HttpContext.Session.SetString("code", code);
HttpContext.Session.SetString("GLSectionId", GLSectionId);
HttpContext.Session.SetString("GLGroupId", GLGroupId);
var data = Gllist.Where(a => a.Name.Contains(name) & a.Code.Contains(code)).FirstOrDefault();//filter by yourself
return PartialView("_Search", data);
}
AddOrEdit:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddOrEditGL(int id,GLModel glModel)
{
if (ModelState.IsValid)
{
//Insert
//Update
var name = HttpContext.Session.GetString("name");
//other session data...
return Json(new { isValid = true, url="Home/Index?name="+name+"&code="+xxx+"&...."});
}
return Json(new { isValid = false, html = Helper.RenderRazorViewToString(this, "AddOrEditGL", glModel) });
}
Then your ajax:
success: function (res) {
if (res.isValid) {
window.location.href = response.url;
}
else
$('#form-modal .modal-body.p-4').html(res.html);
},
I recommend using the jQuery Unobtrusive AJAX library. Its very easy to use:
It can fetch partial views and place them in a container of your choice using the
data-ajax-update="#panel"
Click here
<div id="panel"></div>
This is an example of a controller action that would return the modal:
public IActionResult GetEditModal() => Partial("ViewName");
Then when the modal is placed in your container, using the data-ajax-success attribute a callback method is called, parse the form using the jQuery Unobtrusive Validation like this:
function SuccessCallback() {
//You can also use the keyword "this" instead of getting the form with jquery
$.validator.unobtrusive.parse($(this));
//or
$.validator.unobtrusive.parse($form);
}
Very useful resources : Link - jQuery Unobtrusive AJAX - GitHub
Update
What i did in previous projects is to include an empty edit modal (a modal with an empty body) then using the library and the data-ajax-update i would replace the body of that modal every time the user pressed the edit button on a table item like this:
<div class="modal fade" id="eModal" data-keyboard="false" data-backdrop="static">
<div class="modal-dialog modal-lg">
<div class="modal-content id="modalContentE">
<div class="modal-header">
<h4 class="modal-title"><i class="nav-icon fas fa-edit"></i> Edit Data</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true"><i class="fas fa-times-circle text-white"></i></span>
</button>
</div>
//New data goes here
</div>
</div>
</div>
And your add Modal would be a regular full modal and each time the user use it to submit data, clear the form.
I have a paged list in a ajax tabbed pane where I load partial views. I have made use of the built in ajax in IPagedlist but partial view is not being replaced properly, what am I doing wrong
My Ajax calls /Account/CustomerTab this finds the correct view and redirectsAction to Customer Controller and calls the partial view and inserts it in the tab div.
When clicking next it calls /Customer/Invoice?page=2 and returns that in the url instead of replacing the div 'replaceDiv'.....Now I sit with just the partial view in the window without the rest of the site.
Here is the main page with the tabbed pane, if you look in the ajax call you will see I insert n div with a class "replaceDiv"
<div class="row">
<div class="col-md-12 col-sm-12 col-xs-12">
<h3>Account Information</h3>
<div class="tab-container left clearfix">
<ul id="tabStrip" class="nav-tabs clearfix">
<li class="active">Business Information</li>
<li class="">Addresses</li>
<li class="">Pro forma Invoice</li>
<li class="">Invoices</li>
<li class="">Order History</li>
</ul>
<div class="tab-content clearfix">
<div class="tab-pane active" id="0">#Html.Action("Information", "Customer")</div>
<div class="tab-pane" id="1"></div>
<div class="tab-pane" id="2"></div>
<div class="tab-pane" id="3"></div>
<div class="tab-pane" id="4"></div>
</div><!-- End .tab-content -->
</div><!-- End .tab-container -->
</div><!-- End .col-md-12 -->
</div><!-- End .row -->
#section scripts{
<script type="text/javascript">
$('#tabStrip a').click(function (e) {
e.preventDefault()
var tabID = $(this).attr("href").substr(1);
$(".tab-pane").each(function () {
$(this).empty();
});
$("#" + tabID).empty().append("<div class='loader'><img src='/Content/img/Loader/ajax-loader.gif' alt='Loading' /></div>");
$.ajax({
url: "/Account/CustomerTab",
data: { Id: tabID },
cache: false,
type: "get",
dataType: "html",
success: function (result) {
$("#" + tabID).empty().append("<div id='replaceDiv'>" + result + "</div>");
}
});
$(this).tab('show')
});
</script>
}
here is my partial view with the page list where I try to replace the html but all i get is a new html page with just my list in and not replacing the div.
<h2 class="sub-title">Invoices</h2>
#using (Html.BeginForm("Invoices", "Customer", FormMethod.Get))
{
<p>
Find by Invoice Number: #Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
<input type="submit" value="Search" />
</p>
}
<table class="table table-hover">
<thead>
<tr>
<th>Invoice Number</th>
<th>Date</th>
<th>Total</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
#foreach (var inv in Model)
{
<tr>
<td>#inv.InvoiceNumber</td>
<td>#inv.DateCompleted</td>
<td>#inv.TotalAmount</td>
</tr>
}
</tbody>
</table>
#Html.PagedListPager(Model, page => Url.Action("Invoices", "Customer", new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(new AjaxOptions { HttpMethod = "get", InsertionMode = InsertionMode.Replace, UpdateTargetId = "replaceDiv" }))
EDIT
I find that if I add if (Request.IsAjaxRequest()) in my controller then it doesn't get hit. so its not an ajax request being sent through. This is what the rendered HTML looks like
<a data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#replaceDiv" href="/Customer/Invoices?page=3">3</a>
I found the problem. All the code is correct except My knowledge of javascript is a bit limited. I needed to include the jquery.unobtrusive-ajax.min.js package and now its working like a charm.
As per subject. The view looks like this.
#using System.Globalization
#model IEnumerable<TaskEngine.WebUI.Models.TaskViewModel>
<script src="../../Scripts/progress-task.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: '#Url.Action("Index", "Home")',
data: { from: "10/01/2014", to: "10/14/2014" },
dataType: "html",
success: function () {
alert('Success');
},
error: function (request) {
alert(request.responseText);
}
});
});
</script>
<div class="container">
<div class="row">
<div class="span16">
<div id="sitename">
xxxxxxxx
<span class="name">Workbasket</span>
</div>
<div class="row">
<div class="span16">
<table class="table table-striped table-condensed" id="task-table">
<thead>
<tr>
<th class="left">Client</th>
<th class="left">Task</th>
<th class="left">State</th>
<th class="left">Assigned By</th>
<th class="left">Assigned To</th>
<th class="left">Date Opened</th>
<th class="left">Date Due</th>
#* <th class="left">Date Closed</th>*#
<th class="left">Outcomes</th>
</tr>
</thead>
<tbody>
#foreach (var task in Model)
{
<tr>
<td><span>#task.ClientId</span></td>
<td><span class="nowrap">#task.TaskDescription</span></td>
<td><span class="nowrap">#task.CurrentState</span></td>
<td><span >#task.AssignedBy.Replace("CORPORATE\\", "").Replace(#".", " ")</span></td>
<td><span>#task.AssignedTo.Replace("CORPORATE\\", "").Replace(#".", " ")</span></td>
<td><span>#task.DateOpened.ToString("dd/MM/yyyy HH:mm")</span></td>
<td><span>#task.DateDue.ToString("dd/MM/yyyy HH:mm")</span></td>
#* <td><span>#(task.DateClosed.HasValue ? task.DateClosed.Value.ToShortDateString() : " - ")</span></td>*#
<td>
<span class="nowrap">
#Html.DropDownList(
"Outcomes",
new SelectList(task.Outcomes, "Id", "Name"), "---Please Select ---",
new Dictionary<string, object>
{
{"data-case-id", #task.CaseId }, {"data-task-log-id", #task.TaskLogId}, {"data-client-id", #task.ClientId}
})
</span>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="modal hide span8" id="complete-task-modal" tabindex="-1" role="dialog" aria-labelledby="complete-task-header" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="complete-task-header">Complete Task</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="span8">
<div class="alert alert-info">
<label id="CurrentState"></label>
<label id="NewState"></label>
<label>Generated Tasks</label>
<ul id="task-list">
</ul>
</div>
</div>
</div>
<form id="form">
<input type="hidden" id="task-log-id" name="taskLogId" />
<input type="hidden" id="case-id" name="caseId" />
<input type="hidden" id="outcome-id" name="triggerId" />
<input type="hidden" id="client-id" name="clientId" />
<div id="popup">
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn" id="close" data-dismiss="modal" aria-hidden="true">Go Back</button>
<button class="btn btn-primary" id="confirm-task">Confirm</button>
</div>
</div>
</div>
This is the controller method.
public ActionResult Index(DateTime? from, DateTime? to)
{
var usergroups = GetGroups(HttpContext.User.Identity.Name);
var model = _taskLogger.GetTasks(from, to)
.Select(task => new TaskViewModel
{
TaskLogId = task.TaskLogId,
CaseId = task.CaseId,
ClientId = task.ClientId,
TaskDescription = task.Description,
AssignedBy = task.AssignedBy,
AssignedTo = task.AssignedTo.Trim(),
DateOpened = task.DateCreated,
DateClosed = task.DateClosed,
DateDue = task.DateDue
}).ToList()
.Where(x => IsAvailableToUser(x.AssignedTo, usergroups))
.OrderBy(x => x.DateDue);
foreach (var task in model)
{
var workflow = _workflowEngine.GetCase(task.CaseId);
task.CurrentState = workflow.State.ToNonPascalString();
task.Outcomes = workflow.GetPermittedTriggers().OrderBy(x => x.Name).ToList();
}
ModelState.Clear();
return View(model);
}
When the model is returned following the ajax post, the dataset is different, as expected, however, within the datatable in the view it still displays the old data.
Having done some googling on this issue, I've tried clearing the modelstate but that makes no difference and from what I've read, it only seems to affect HTMLHelpers anyway.
I'm not sure if this is an issue with the datatable or just a refresh issue with the view itself. Any input would be appreciated.
Ajax calls stays on the same page. If you want to update the page with the view returned by public ActionResult Index(DateTime? from, DateTime? to) then you need to add it to the DOM in the success callback
$.ajax({
url: '#Url.Action("Index", "Home")',
data: { from: "10/01/2014", to: "10/14/2014" },
dataType: "html",
success: function (data) {
$('#someElement').html(data); // add the returned html to the DOM
},
....
});
but looking at the code in the controller it appears to be the same view as the original view so perhaps you really want to redirect rather than stay on the same page
Here is some of the relevant code right away
Markup
<div class="well">
<input type="button" id="addNewCert" value="Add New Certification" class="btn btn-primary" data-bind="click: addCert"/>
</div>
<table class="table table-striped">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Code</th>
<th>Description</th>
<th>Type</th>
</tr>
</thead>
<tbody data-bind="foreach: certs">
<tr>
<td>Edit</td>
<td>Delete</td>
<td data-bind="text: certName"></td>
<td data-bind="text: certCode"></td>
<td data-bind="text: description"></td>
<td data-bind="text: certType.certTypeName"></td>
</tr>
</tbody>
</table>
<div id="selectedCert" data-bind="with: selectedCert">
<div class="well">
<div class="row-fluid">
<div class="span6">
<div class="control-group">
<h6>Certification Name</h6>
<input type="text" id="CertificationName" data-bind="value: certName" style="width:100%;" />
</div>
<div class="control-group">
<h6>Certification Code</h6>
<input type="text" id="CertificationCode" data-bind="value: certCode" style="width:50%;" />
</div>
<div class="control-group">
<h6>Description</h6>
<textarea ID="Description" data-bind="value: description" style="height:250px;width:480px;"></textarea>
</div>
<div class="control-group">
<h6>Certification Type</h6>
<select id="CertificationType" data-bind="options: $parent.availableCertTypes, optionsText: 'certTypeName', optionsValue: 'certTypeId', value: $parent.selectedCertType" style="width:100%;"></select>
</div>
<div class="control-group">
<button class="btn btn-primary" data-bind="click: $parent.save">Save</button>
<button class="btn" data-bind="click: $parent.cancel">Cancel</button>
</div>
</div>
</div>
</div>
</div>
ViewModel
//Bound to a dropdown and populated by an ajax call listed below
self.availableCertTypes = ko.observableArray([]);
self.selectedCert = ko.observable();
self.addCert = function () {
self.selectedCert(new Certification({});
//This line doesn't work :(
//self.selectedCert(new Certification(self.availableCertTypes()[0]));
};
//Populates availableCertTypes
$.getJSON(sf.getServiceRoot('InCertModule') + 'InCert/GetCertTypesByPortal', function (data) {
//Could call 'self.Certs(self.MapItems(data))' here as that would take the fetched data and populate the viewModel's list of certifications used for binding
var mappedCertTypes = $.map(data, function (item) {
return new CertType(item);
});
self.availableCertTypes(mappedCertTypes);
});
Models
var Certification = function (data) {
if (!data) {
//If there is no data, pass an empty intialized object, otherwise get an undefined reference
data = {};
} else {
this.certId = ko.observable(data.CertificationId);
this.certName = ko.observable(data.CertificationName);
this.certCode = ko.observable(data.CertificationCode);
this.description = ko.observable(data.Description);
this.certType = ko.observable(new CertType(data));
//this.certTypeId = ko.observable(data.CertTypeId);
//this.certTypeName = ko.observable(data.CertTypeName);
this.isEditing = ko.observable(false);
this.isValid = ko.observable(true);
}
}
var CertType = function (data) {
this.certTypeId = ko.observable(data.CertTypeId);
this.certTypeName = ko.observable(data.CertTypeName);
}
I also have an edit function set up with a with binding. This works correctly and gets my CertType model nested properly, like so
"selectedCert": {
"certId": 10,
"certName": "AFC Service Training",
"certCode": "AFCST",
"description": "The training required to work AFC service",
"certType": {
"certTypeId": 1,
"certTypeName": "Certification"
},
"isEditing": false,
"isValid": true
},
However, I've tried a few combinations for my addCert function to take the very first item in the availableCertTypes ObservableArray and assign it. Reason being is when I post it to my web service, I need to ensure that something is selected there.
I'm new to Knockout.js so not even sure what's possible or how to really accomplish it but ideally when I call addCert, I'd like to see something similar to this
"selectedCert": {
//The first item in the observable array
"certType": {
"certTypeId": 1,
"certTypeName": "Certification"
},
"isEditing": false,
"isValid": true
}
but instead I wind up with
"selectedCert": {
"certType": {},
"isEditing": false,
"isValid": true
}
If I need to post more code I can, just let me know. Any help is greatly appreciated.
Edit Here is a jsFiddle with most of the markup, although it's not working? http://jsfiddle.net/jtCrw/1/
The array binds correctly when fetched from the web service, just not sure how to hard code those in there. Currently those 2 are the only two options.
The root of your empty certType problem is due to a mismatch/typo in your setup.
self.addCert = function () {
self.selectedCert(new Certification(self.availableCertTypes()[0]));
}
resulting in an eventual call to this.certType = ko.observable(new CertType(data));
var CertType = function (data) {
this.certTypeId = ko.observable(data.CertTypeId); //expecting uppercase property
this.certTypeName = ko.observable(data.CertTypeName);
}
self.availableCertTypes = ko.observableArray([
{
"certTypeId": 1, //data-bind and this declaration are both lowercase
"certTypeName": "Certification"
},
{
"certTypeId": 2,
"certTypeName": "Training"
}
]);
fixing that yields the expected result from your current setup
"selectedCertType":{"CertTypeId":1,"CertTypeName":"Certification"}
To get the desired outcome as you have shown/requested, remove the optionsValue from the select binding and bind the value to the certType of the selected certification (provided by the with binding)
<select id="CertificationType"
data-bind="options: $parent.availableCertTypes,
optionsText: 'CertTypeName',
value: certType"
style="width:100%;"></select>
Result:
"selectedCert":{"certType":{"CertTypeId":1,"CertTypeName":"Certification"},"isEditing":false,"isValid":true}
Consideration: If you want to populate the select with data from an existing certification, some special consideration is needed. Take a look at examples of initialization.
I need a little help with this since I am very new to AJAX in general. In a given page that I have (a view) I display a button which brings up a form. What I ultimately want is to pass the data input in that form to a controller inside my application. I know there are plenty of tutorials on how to do it out there...however, I seem to have a problem understanding how this is done; therefore, I want to traverse this step-by-step. I just simply want to display a different view after the user clicks on the "Save" button on the dialog. I hope that is clear. Here is my HTML + jQuery
#model AccommodationEditViewModel
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<table>
<tr>
<td>
#Html.ActionLink("Back to list", "List", "Accommodation")
</td>
</tr>
<tr>
<td>
#if ( Model.Accommodation.LocaleID != Guid.Empty)
{
#Html.DisplayAccommodation(IAccommodationDisplay);
}
</td>
</tr>
</table>
<div class="genericform">
<form id="form" method="post">
#Html.AccommodationEditDisplay()
<table>
<tr>
<td>
#Html.ActionLink("Add New Address", "", "", new { id = "addaddresses" }, null)
</td>
</tr>
#if (Model != null && Model.Accommodation.Addresses.Count() == 0)
{
<tr>
<td>
This Locale Contains No Addresses
</td>
</tr>
}
else
{
foreach (Address address in Model.Accommodation.Addresses)
{
<tr>
<td>
#address.Address1
</td>
</tr>
}
}
</table>
<br /> <br />
<input type="submit" name="command" value="Save" />
<input type="submit" name="command" value="Delete" />
</form>
</div>
<button id="opener">Add Address</button>
<div id="dialog" title="Add Address" style="display:none;">
<label for="Address1">Address: </label><input id="Address1" />
<label for="Address2">Address 2: </label><input id="Address2" />
<label for="City">City: </label><input id="City" />
<label for="State">State: </label><input id="State" />
<label for="PostalCode">Postal Code: </label><input id="PostalCode" />
</div>
<script type="text/javascript" src="~/Scripts/jquery-1.7.1.js"></script>
<script type="text/javascript" src="~/Scripts/jquery-ui-1.8.20.js"></script>
<link type="text/css" href="~/Content/themes/base/jquery.ui.all.css" rel="stylesheet" />
<script type="text/javascript">
$(document).ready(function () {
$("#dialog").dialog({
autoOpen: false,
show: {
effect: "explode",
duration: 250
},
hide: {
effect: "explode",
duration: 250
},
buttons: {
"Save": {
text: "Save",
class: "",
click: function () {
//**redirect here**
$(this).dialog("close");
}},
"Cancel": {
text: "Cancel",
class: "",
click: function () {
$(this).dialog("close");
}
}},
modal: true
});
$("#opener").click(function () {
$("#dialog").dialog("open");
});
});
</script>
I have tried using $.ajax({}) and setting this: Url: "/Areas/Website/Controller/Action
but scripting stops working at that point.
Any and all help is appreciated! Thank you!
EDIT
Do I even need to use AJAX at all? I just want to pass the information in that form (inside the dialog) to a controller.
Ok, try replacing your <form id="form" method="post"> form fields </form> with
#using (Html.BeginForm("NameOfControllerMethod", "NameOfControllerClass"))
{
<!-- fields for gathering data, your input fields essentially -->
}
THEN you need to go to your controller class, and add [HttpPost] above your controller method, like this:
[HttpPost]
public ActionResult MethodName(AccomodationEditViewModel viewModel) {
//do stuff in here with the viewModel, for example viewModel.Location, or viewModel.Name
}
NOTE that the [HttpPost] requires that you add a new "using" insert at the top of your controller class.
The NameOfControllerMethod is the method that has the HttpPost above it. The name of the controller class is like "MyClass", coming from the controller named MyClassController, as an example.
Try this:
window.location = "/Areas/Website/Controller/Action";
inside your click function.