Webgrid refresh with ajax in MVC3 - javascript

I have a Webgrid that I need to refresh when pressing a button 'Refresh'. I also have a search input.
Everything is working fine, except that everytime that I hit refresh, the pageNumber is being set back to one...
Here is my code...
controller
public ActionResult ListImporting(string searchText = "", int page = 1)
{
ViewBag.RowsPerPage = 2;
searchText = searchText.Trim();
ViewBag.searchText = searchText;
ViewBag.page = page;
DtoPaginatedResult<ListImportingDTO> model = listService.GetListsInProgress(page, ViewBag.RowsPerPage, searchText);
if (Request.IsAjaxRequest())
return PartialView("ListImportingGrid", model);
else
return View(model);
}
Then I have a view List Importing that calls the partial...
<input id="refreshButton" type="button" value="Refresh" style="float:right"/>
<div id="resultList" style="margin-top:20px">
#Html.Partial("ListImportingGrid", Model)
</div>
......
$("#refreshButton").live("click",updateGrid);
And inside the partial I have the grid, and the current function
function updateGrid() {
var pageNumber = #ViewBag.page;
console.log(pageNumber);
$.ajax(
{ type: "GET" ,
url: '/Admin/ListImporting/',
data: { searchText: $("#searchBox").val(),
page: pageNumber
} ,
dataType: "html" ,
success: function (data){
$("#resultList").html(data);
}
})
}

When the page is loaded you are hard coding the pagenumber to ViewBag.page which is always going to be equal to one on page load.
Instead what you need to do is create a hidden input in the partialview and place the javascript code in the main view.
Then when the updateGrid function is triggered look in the patial view, via javascript, and find the value that is in the hidden field for the page number.
Switch to PagedList, it's a lot easier to work with ajax, pagination, and webgrids.
https://github.com/TroyGoode/PagedList

Your pageNumber is being reset back to 1 because every time you call updateGrid you're resetting it to the ViewBag.page
in other words, the compiled razor code looks like
function updateGrid() {
var pageNumber = 1;
....
So you should create pageNumber somewhere else, if you don't want it to be reset when you call updateGrid, like this
var pageNumber = ViewBag.page;
function updateGrid() {
....

Related

How to retain Select value after ajax within a partial view?

In my ASP.NET Core web application I have a partial view that will need to be placed in several views and be able to respond to dynamic data that will vary based on the view that's rendering this partial at the time. The red box in the image blow represents the area the partial is rendered.
The partial is essentially a Select that will call a Stored Procedure and return a datatable and render out the table to the partial view. I am able to select an option on the page and have it call the SP and see all relevant data from the datatable and can write that out on the page no problem. The problem I am having is that every time the partial refreshes via ajax, the Select returns to the default "Select" value and does not keep the previously selected option selected.
For the sake of brevity, assume that the FeedbackQueries object just contains 4 string elements.
_FeedbackQueryResultPartial.cshtml
#using Contract.Shared;
#model FeedbackQueries
<style>
#select {
margin: auto;
text-align: center;
}
</style>
<div id="feedbackQueryResultPartial">
<div style="height:25px;">
<div id="select">
<select name="StoredProcs" id="StoredProcs" onchange="selectQuery()">
<option value="Select">Select</option>
#foreach (FeedbackQuery query in Model.Queries)
{
#Html.Raw($"<option value='{query.SprocName}'>{query.SprocName}</option>");
}
</select>
</div>
<div id="feedbackQueryDiv" class="row">
#if (Model.FeedbackQueryResults.Rows.Count > 0)
{
<h3>DataTable goes here</h3>
}
else
{
<h3>No rows were returned from your query. Please select another.</h3>
}
</div>
</div>
</div>
Processing.cshtml
#using Contract.Parent
#using Contract.Shared
#model Processing
<script>
function showFeedbackPartial(x, y, z, q) {
$.ajax({
cache: false,
url: '#Url.Action("GetFeedbackQueryDatatable", "Client")',
type: 'POST',
data: { databaseConnectionString: x, storedProcedure: y, page: z, Model: q },
success: function (result) {
var selected = $('#StoredProcs').val();
console.log(selected);
if (result.rowCount > 0) {
console.log(result.rowCount);
var databaseConnectionString = x;
var storedProcedure = y;
var page = z;
var model = q;
var url = '#Url.Action("ViewFeedbackQueryPartial", "Client")';
$("#feedbackQueryResultPartial").load(url, { databaseConnectionString, storedProcedure, page, model });
}
else {
document.getElementById('feedbackQueryDiv').innerHTML = '<h3>No rows were returned from your query. Please select another.</h3>';
}
$('#StoredProcs').val(selected);
$("#StoredProcs option[value='Select']").remove();
}
});
}
</script>
<script>
function selectQuery() {
var e = document.getElementById('StoredProcs');
var ev = e.options[e.selectedIndex].text;
var p = 'Processing';
var model = #Html.Raw(Json.Serialize(Model.FeedbackQueries));
console.log(model);
showFeedbackPartial('#Model.Client.DatabaseConnectionString', ev, p, model);
}
</script>
<script>
$(document).ready(function () {
document.getElementById('feedbackQueryDiv').innerHTML = '<h3>Select a query to view feedback.</h3>';
});
</script>
}
<form method="post" enctype="multipart/form-data">
...
<partial name="_FeedbackQueryResultPartial" for="#Model.FeedbackQueries" />
...
</form>
Controller actions that render the Processing view
[HttpGet]
public IActionResult Processing(int Id)
{
ViewBag.Id = Id;
Processing processing = new Processing();
//Get pertinent information for Client
processing.Client = _clientProcessingService.GetSingleClient(Id, _appSettings.MOPConfigConnectionString);
processing.Client.DatabaseConnectionString = _clientProcessingService.GetClientConnectionFromConfig(processing.Client, _appSettings);
processing.Steps = _clientProcessingService.GetClientSteps(processing.Client.DatabaseConnectionString, "Processing");
processing.CurrMoInfo.CurrMo = _clientProcessingService.GetProcessingCurrMo(processing.Client.DatabaseConnectionString);
processing.FeedbackQueries = _clientProcessingService.GetFeedbackQueriesFromDb(processing.Client.DatabaseConnectionString, "Processing");
return View(processing);
}
[HttpPost]
public IActionResult Processing(Processing Model)
{
//Get pertinent information for Client
Model.Client = _clientProcessingService.GetSingleClient(Model.Client.ClientID, _appSettings.MOPConfigConnectionString);
Model.Client.DatabaseConnectionString = _clientProcessingService.GetClientConnectionFromConfig(Model.Client, _appSettings);
Model.Steps = _clientProcessingService.GetClientSteps(Model.Client.DatabaseConnectionString, "Processing");
Model.CurrMoInfo.CurrMo = _clientProcessingService.GetProcessingCurrMo(Model.Client.DatabaseConnectionString);
Model.FeedbackQueries = _clientProcessingService.GetFeedbackQueriesFromDb(Model.Client.DatabaseConnectionString, "Processing");
return View(Model);
}
Controller action that renders the partial
public IActionResult ViewFeedbackQueryPartial(string DatabaseConnectionString, string StoredProcedure, string Page, FeedbackQueries Model)
{
if(StoredProcedure == "Select")
{
return PartialView("_FeedbackQueryResultPartial", Model);
}
Model.FeedbackQueryResults = _clientProcessingService.GetFeedbackQueryDataTable(DatabaseConnectionString, Page, StoredProcedure);
return PartialView("_FeedbackQueryResultPartial", Model);
}
I have tried so many different ways of maintaining this value. Adding it to the model, adding it to the Viewbag and countless other methods of attempting to retain this value somewhere and regardless of success or failure, keep the value and change it to the selected option via javascript. It resets to "Select" every time the partial is reloaded after the ajax call is made.
This also presents another problem wherein, when I submit the form on the Processing view by clicking RUN the page will refresh and go to the next step in the process but ideally what should also happen is that the value in the partial is kept, the query is ran again and the user doesn't need to select a new value at any point unless they want to run a different SP to see different data in the table.
Is this even possible or am I trying to do this the entirely wrong way?
For your issue, you may need to pass the selected SprocName from Parent View to partial view with Model.
Add SelectedSprocName to FeedbackQueries
public class FeedbackQueries
{
public string SelectedSprocName { get; set; }
public List<FeedbackQuery> Queries { get; set; }
public FeedbackQueryResults FeedbackQueryResults { get; set; }
}
Change View to set SelectedSprocName
function showFeedbackPartial(x, y, z, q) {
$.ajax({
cache: false,
url: '#Url.Action("GetFeedbackQueryDatatable", "Process")',
type: 'POST',
success: function (result) {
var selected = $('#StoredProcs').val();
model.SelectedSprocName = selected;
var url = '#Url.Action("ViewFeedbackQueryPartial", "Process")';
$("#feedbackQueryResultPartial").load(url,{ databaseConnectionString, storedProcedure, page, model });
console.log('after load' + selected);
// your rest code
}
});
}
Partial View set selected option
#foreach (FeedbackQuery query in Model.Queries)
{
if (query.SprocName == Model.SelectedSprocName)
{
#Html.Raw($"<option value='{query.SprocName}' selected='true'>{query.SprocName}</option>");
}
else
{
#Html.Raw($"<option value='{query.SprocName}'>{query.SprocName}</option>");
}
}

How to dynamically show a button based on conditions where the value being used to determine what button to show isn't known until it's clicked?

If someone has a better title feel free to edit. I inherited a project from a developer who is leaving the company and I'm scratching my head trying to find a solution to a problem the existing code provides.
Code from the view:
<div>
<table class="table">
<tr>
<th class="border-bottom border-top-0">Action</th>
</tr>
#foreach (Step actionItem in Model.Steps)
{
#if (actionItem.HasRun == false)
{
<tr class="border-top-0">
<td>
#if (actionItem.ReturnsInfo == true)
{
<input type="button" value="Run Check" onclick="loadProcessingFeedbackPartial('#actionItem.StepID', '#Model.Client.DatabaseConnectionString' )" />
}
else
{
<input type="submit" value="Run Check" name="btnRunStoredProcedure" asp-action="CallStepStoredProcedure" asp-route-StepID="#actionItem.StepID" asp-route-StepCompleted="#actionItem.HasRun" />
}
</td>
</tr>
break;
}
}
</table>
</div>
Javascript being called from the button click:
<script type="text/javascript">
function loadProcessingFeedbackPartial(x, y) {
var url = '#Url.Action("ViewProcessingFeedBackPartial", "Client")';
var stepId = x;
var databaseConnectionString = y;
$("#processingFeedbackPartialDiv").load(url, { stepId, databaseConnectionString },
function () {
$("#confirmButton").removeAttr("style");
});
}
</script>
Controller action:
public IActionResult ViewProcessingFeedBackPartial(int StepId, string DatabaseConnectionString)
{
FeedbackDetails feedbackDetails = new FeedbackDetails();
feedbackDetails.Data = _clientProcessingService.GetProcessingFeedbackDetails(StepId, DatabaseConnectionString);
return PartialView("_ViewFeedback", feedbackDetails);
}
The button in the view has an Onclick event that goes to the Javascript function, which loads a partial view with the data from the controller calling a service method. Here's where the problem is. If no rows are returned, I want to bypass the partial being drawn entirely.
So I changed the controller action around a bit to include a condition where if the feedbackDetails.Data has 0 rows to just call a different method from the service, process as normal, but return the View instead of a partial.
public IActionResult ViewProcessingFeedBackPartial(int StepId, string DatabaseConnectionString, int ClientId)
{
FeedbackDetails feedbackDetails = new FeedbackDetails();
feedbackDetails.Data = _clientProcessingService.GetProcessingFeedbackDetails(StepId, DatabaseConnectionString);
if(feedbackDetails.Data.Rows.Count == 0)
{
_clientProcessingService.RunProcessStepConfirmation(DatabaseConnectionString, StepId, ClientId, "No information returned, automatically proceeding to next step.");
return RedirectToAction("Processing", new { Id = ClientId });
}
return PartialView("_ViewFeedback", feedbackDetails);
}
This "worked", except since in the view it's being called in a Javascript function that loads a partial regardless, the view is returned inside that partial instead of the view being returned.
But I'm unsure how to fix this because without first clicking the button and attempting to populate that collection with data, I don't know if it's empty (and skip the partial) or it has rows (and draw the partial).
I attempted creating an intermediary controller action that returns a boolean and attempted to use the result of that inside the javascript function to either draw the partial or skip it based on the bool, but I'm not really the greatest at Javascript so I wasn't able to get it to work.
I'm unsure if the way to solve this involves creating logic that displays multiple buttons that route to different controller actions or javascript functions or just handling it all via Javascript somehow.
What would be a good way to go about solving this?
#Mkalafut, your jQuery function is loading the controller result directly into "#processingFeedbackPartialDiv" regardless of the result received. Better to pull this initially into a variable, then add some simple logic to decide what to do next. Potentially the controller can help by returning a null result that is easy to identify.
e.g.
$.get("url", { stepId, databaseConnectionString }, function (data) {
var result = data;
// Some example conditional logic - adjust as required
if (result != null){
$("#processingFeedbackPartialDiv").html(result);
$("#confirmButton").removeAttr("style");
}
});
Remember, jQuery load & get are both just shorthand functions for ajax, so if needs be you can customise the code further to get the flexibility you need.
https://api.jquery.com/jQuery.get/
https://api.jquery.com/load/

Populate data from groovy controller in pop up

I have a gsp page with a delete button for each row of a table. On the button click I want a pop up which tells the consequences of the delete. These consequences depends on the data present in the row and a few other constraints known to the grails service which is called from the grails controller associated to the gsp page. If the user confirms these consequences the row should be deleted from the table, else the table remains unchanged.
How should i go about to achieve this behavior?
Currently, I have in my gsp
<tr>
<td>name</td>
<td>parentName</td>
<td>data</td>
<td>
<g:link action="deleteRow" params="${[name: row.name, parentName: row.parentName]}">
<button class="deleteSnapshot">Delete</button>
</g:link>
</td>
</tr>
and in my .js file
$(document).on('click', ':button', function (e) {
var btn = $(e.target);
btn.attr("disabled", "disabled"); // disable button
alert('getting deletion details');
//var deletionDetails -->not sure how to get these
//var deletionDetails will get data from controller action:"getDetails"
if (confirm('/*print deletion details and ask*/ Do you want to proceed?')) {
alert('will delete')
return true
}
else {
btn.removeAttr("disabled"); // enable button
return false
}
});
and in my controller
class DeleteController{
DeleteService deleteService
def index() {
[list:deleteService.getTableList()]
}
def getDeletionDetails(string name, String parentName){
return deleteService.getDetails(name,parentName)
}
def deleteRow(String name, String parentName){
service.deleteRow(name, parentName)
redirect controller:"DeleteController", action:"index"
}
}
I know the deletion works fine, because it works even with in the current state. Just that the confirmation box asks Do you want to proceed, without displaying the details.
Any help on how i could achieve what I am looking for will be appreciated.
P.S. I am new to stackoverflow, so if i missed out on certain convention do let me know.
Thanks in advance.
I can think of two ways of doing it:
The first one is using ajax to both get deletion details and delete the row
Assuming that deleteService.getDetails(name, parentName) returns a String,
first you need to change an getDeletionDetails action so it renders the response:
def getDeletionDetails(String name, String parentName){
render deleteService.getDetails(name, parentName)
}
and change g:link-s to buttons in gsp:
<button data-name="${row.name}" data-parent-name="${row.parentName}">
Delete
</button>
In your .js then put:
$(document).on('click', ':button', function (e) {
var btn = $(e.target);
btn.attr("disabled", "disabled"); // disable button
var name = btn.data('name');
var parentName = btn.data('parentName');
$.ajax({
url: "/delete/getDeletionDetails",
data: {
name: name,
parentName: parentName
},
success: function (data) {
if (confirm(data + '\nDo you want to proceed?')) {
$.ajax({
url: '/delete/deleteRow',
data: {
name: name,
parentName: parentName
},
success: function (d) {
console.log("Success: " + d);
}
});
} else {
btn.removeAttr("disabled"); // enable button
}
}
});
});
What this code does is it sends an ajax call to /delete/getDeletionDetails, then uses its response (rendered by getDeletionDetails action in DeleteController) to show a confirmation alert. If user confirms the question, another ajax call is sent - now to deleteRow action of DeleteController - with parameters taken from data attributes of clicked button. If user cancels - nothing happens, except for reenabling a button.
Your deleteRow should only change the return statement - it also must render the response:
def deleteRow(String name, String parentName){
service.deleteRow(name, parentName)
render "You deleted an item $name - $parentName."
}
You don't need redirect here, because - thanks to using ajax - user will never leave delete/index. You can just display some kind of confirmation on page after successful ajax call.
The second option is to put deletion details in hidden fields or data- attributes in each row and then just retrieve them in js:
You can create a method getDeletionDetails() in row's domain class (presumably Row) that returns the details (using services in domain classes is not perfect, but is should work ok if the service is not very complex). Then, in your .gsp place:
<td>
<g:link action="deleteRow" params="${[name: row.name, parentName: row.parentName]}">
<button class="deleteSnapshot" data-details="${row.deletionDetails}">Delete</button>
</g:link>
</td>
You should then be able to get details in .js like this:
var deletionDetails = btn.data('details');

Using iterative variable from C# for loop in JavaScript function

On one of my pages I have a for loop to iterate through a list of "Projects" (which is the main model for my website) and display some of their data. The following code is nested in a table and the middle cells removed for redundancy.
foreach (var item in Model.Projects)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.SubmissionNumber)</td>
<td>#Html.DisplayFor(modelItem => item.Status)</td>
<!-- and so on -->
<td>#Html.ActionLink("Detail", "DisplayDetails", new { id = item.ProjectID })</td>
</tr>
}
The "Detail" link in the last cell will ideally make a box pop up (I'm thinking of using a Modal via Bootstrap) containing all of the data for the project. The "DisplayDetails" controller action returns a partial view that presents this information, but since I'm not using JavaScript or anything to render the partial view on the current page it renders it as it's own unformatted page. This is the controller action:
[HttpGet]
public ActionResult DisplayDetails(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Project project = db.Projects.Find(id);
if (project == null)
{
return HttpNotFound();
}
return PartialView("~/Views/Projects/_Detail.cshtml", project);
}
Ideally I would pass the ID to the controller using AJAX like I did below (which is code from another page of my website, again edited to remove redudancy):
$("#show").on("click", function () {
$.ajax({
url: '/Projects/SearchTable',
type: "GET",
data: {
Line1: $('#' + '#Html.IdFor(m => m.Project.ProjectAddress.Line1)').val(),
// and so on
County: $('#' + '#Html.IdFor(m => m.Project.ProjectAddress.County)').val(),
}
}).done(function(partialViewResult) {
$(".wrapper").html(partialViewResult);
$(".wrapper").css('display', 'block');
});
});
And by doing this I can embed the partial view onto the current page instead of it opening as a new page. I'm just not sure how to pass the project ID for a specific row in the table as data to the controller. Is this even possible? If not is there another way to achieve the same result?
You can replace your ActionLink with this:
<td>Details</td>
Then,
$(".details").on("click", function (e) {
e.preventDefault();
var projectId = $(this).data('id');
// Make the AJAX call here...
});

How do I dynamically update a div instantly when an item in a dropdownlist is selected?

Edit for clarity: When an item in the dropdownlist is selected, call a method in my controller which will return a partialview and update a div on my page.
I have a ActionLink PartialView which I want to be called every time I select a new item in the dropdown list to update a table I have on my webpage - how do I make this happen without needing to press a button?
#Html.DropDownListFor(m => m.SelectedCustomerId, Model.CustomerIDItem)
I can do a Request["selectedCustomerId"] to retrieve the value from it without any problems but my question really is about the dynamic part. The first idea was to connect AJAX to the dropdownlist or using JQuery but I have no idea how to actually make this work.
Thanks in advance.
Edit 4:
Adding all of my code:
_DefaultValuesView.cshtml:
#Html.DropDownListFor(m => m.SelectedCustomerId, Model.CustomerIDItem)
<div id="divValues">
#{ Html.RenderPartial("_DefaultValuesPartialView");}
</div>
DefaultValuesController.cs
[HttpPost]
public PartialViewResult DefaultValuesPartialView(string SelectedCustomerId)
{
Session["theCustomerId"] = Request["selectedCustomerId"];
var model = new DefaultValuesModel
{
CustomerIDItem = GetCustomerIds(),
FieldIDItem = GetValues(),
CurrentValuesItem = GetCurrentValues()
};
model.TriggerOnLoad = true;
this.customerId = Convert.ToInt32(Request["selectedCustomerId"]);
errorMessage = "PartialView is loaded!";
model.TriggerOnLoadMessage = errorMessage;
return PartialView("_DefaultValuesPartialView", model);
}
I would do it like this:
#Html.DropDownListFor(m => m.SelectedCustomerId, Model.CustomerIDItem, new { #id = "customId" })
<div id="divValues">
</div>
$(function() {
$("#customId").change(function() {
var id = $(this).val();
$('#divValues').load('#Url.Action("DefaultValuesPartialView", "DefaultValues")?selectedCustomerId=' + id, function (response, status, xhr) {
$("#divValues").html(response);
});
});
});
Your controller Action then looks like this:
public PartialViewResult DefaultValuesPartialView(int? selectedCustomerId)
{
Session["theCustomerId"] = selectedCustomerId.Value;
var model = new DefaultValuesModel
{
CustomerIDItem = GetCustomerIds(),
FieldIDItem = GetValues(),
CurrentValuesItem = GetCurrentValues()
};
model.TriggerOnLoad = true;
this.customerId = selectedCustomerId.Value;
errorMessage = "PartialView is loaded!";
model.TriggerOnLoadMessage = errorMessage;
return PartialView("_DefaultValuesPartialView", model);
}
Please consider that your Partial views name needs to bee the same as your controller action name.
You load your data with the jQuery function .load this is a common way and I use everytime if I need to do something like you want to do
If I understood you correctly this should do it (it uses JQuery)... JSFiddle here
<select id="myDdl">
<option value="AM">AM</option>
<option value="PM">PM</option>
</select>
<div id="babylonAndTing">
A up lad.
</div>
$(function() {
$("#myDdl").change(function() {
$("#babylonAndTing").text( $('option:selected', this).text() );
});
});
P.S. Hmm on second thoughts, I answered your question title 'How do I dynamically update a div instantly when an item in a dropdownlist is selected?', but your question text is asking something different?!?!? Ah well I'll leave this answer for the people who read your title and come here :p
Edit:
As for your question in the text, try something like this...
#using (Ajax.BeginForm("SelectCustomer", "MyControllerName", FormMethod.Post, null))
{
#Html.DropDownListFor(m => m.SelectedCustomerId, Model.CustomerIDItem)
}
<script type="text/javascript">
$('#SelectedCustomerId').change(function () {
$(this).parents('form').submit();
});
</script>
[HttpPost]
public ActionResult SelectCustomer(int? selectedCustomerId)
{
// Stuff goes here.
}
You can make an ajax request to the partial view and get back just the HTML content of it. The key is using url: "#Url.Action("DefaultValuesPartialLayout")" to get the right URL to load your partial from.
<div id="divValues">
#{ Html.RenderPartial("_DefaultValuesPartialLayout");}
</div>
$(document).ready(function() {
$("##Html.IdFor(m => m.SelectedCustomerId)").change(function() {
var selectedCustomerId = $("##Html.IdFor(m => m.SelectedCustomerId)").val();
$.ajax({
url: "#Url.Action("DefaultValuesPartialView")",
method: "GET",
data: { SelectedCustomerId: selectedCustomerId },
success: function(data, status, xhr) {
$("#divValues").html(data);
}
});
})
});

Categories

Resources