Using iterative variable from C# for loop in JavaScript function - javascript

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...
});

Related

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/

Populating a modal with data-id

My goal is:
Click on a link
Open a modal
View information related to the row where the user clicked
Currently, my modal opens but it displays all my records, what I wish is simply to display the good record with the corresponding information.
I have a data-id on the <a> which is a unique identifier, I get it but I do not know how to use it later.
I also noticed with my Chrome debugger that when I open the modal it goes into shown.bs.modal but then it goes directly without executing what is in the function.
I do not know if this is really important in relation to my question but I would like to specify that my model contains a List <_Event>, _Event being a class that serves as a model for events.
Any help?
[Authorize]
[HttpGet]
public async Task<ActionResult> GetEventsDetails(int Zkp)
{
ViewBag.sessionv = HttpContext.Session.GetInt32("idMember");
FileMakerRestClient client = new FileMakerRestClient(serverName, fileName, userName, password);
var toFind = new Models.EventsLines { Zkp = 1392 };
var results = await client.FindAsync(toFind);
bool isEmpty = !results.Any();
if (isEmpty)
{
return View();
}
Models.EventsLines oEventViewModel = new Models.EventsLines();
oEventViewModel = results.ToList().First();
Console.WriteLine(oEventViewModel);
return PartialView(oEventViewModel);
}
function GetEventsDetails(id) {
$.get("#Url.Action("GetEventsDetails", "Events")/" + id,
function (data) {
$('.modal-body').html(data);
})
$('#myModal').show();
}

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');

Populate and display an html table based on the values from two dropdown menus

I have two cascading dropdown boxes controlled with JQuery and Ajax objects. The first determines the values in the second. Then, once a selection is made in the second, the values from the two dropdowns would be used to find a record in an SQL table and display the record in an html table.
So far the dropdowns work correctly but I'm having difficulty getting the record from the database and then displaying it on screen. I've done this before by getting the database values, sending them to the view in a Json object, and using an Ajax object to to create the table with Jquery. However, in this case I don't mind if the page reloads and would like to use a simpler method.
What is a simple method of sending two values from two dropdowns to the controller, using those values to find a record in an sql table, sending the values from the record back to the view to be displayed? Also, I don't want anything to be displayed until the second dropdown box has a selection made.
Here is what I have so far:
Controller methods:
List<Car> GetCars()
{
using (var service = new Service())
{
return service.GetCars().OrderBy(x => x.CarName).Select(x => new Car
{
CarId = x.CarId,
CarName = x.CarName
}).ToList();
}
}
List<Color> GetColors(int carId)
{
using (var service = new Services())
{
return service.GetColors(carId).OrderBy(x => x.ColorName).Select(x => new Color
{
ColorId = x.ColorId,
ColorName = x.ColorName
}).ToList();
}
}
[HttpPost]
public ActionResult CurrentSaus(int townCode, int fiscalYear)
{
var colors = GetColors(carId);
return Json(new SelectList(colors, "ColorId", "ColorName"));
}
Jquery methods:
$(document).ready(function () {
$("#Car_CarId").change(function () {
var carId = $(this).val();
var carName = $(":selected", this).text();
// put the car name into a hidden field to be sent to the controller
document.getElementById("Car_CarName").value = carName;
getColors(carId);
})
});
function getColors(carId) {
if (carCode == "") {
$("#Color_ColorId").empty().append('<option value="">-- select color --</option>');
}
else {
$.ajax({
url: "#Url.Action("Colors", "HotWheels")",
data: { colorId: clrId },
dataType: "json",
type: "POST",
error: function () {
alert("An error occurred");
},
success: function (data) {
var colors = "";
var numberOfColors = data.length;
if (numberOfColors > 1) {
colors += '<option value="">-- select color --</option>';
}
else {
var colorId = data[0].Value;
var colorName = data[0].Text;
document.getElementById("Color_ColorName").value = colorName;
}
$.each(data, function (i, color) {
colors += '<option value="' + color.Value + '">' + color.Text + '</option>';
});
$("#Color_ColorId").empty().append(colors);
}
});
}
and some of the html:
#Html.HiddenFor(x => x.Car.CarName)
#Html.HiddenFor(x => x.Color.ColorName)
<table>
<tr>
<td> Select Car:</td>
<td style="text-align:left">
#Html.DropDownListFor(
x => x.Car.CarId,
new SelectList(Model.CarList, "CarId", "CarName"),
"-- select town --")
<br />
#Html.ValidationMessageFor(x => x.Car.CarId)
</td>
</tr>
<tr>
<td> Select Color:</td>
<td colspan="4">
#Html.DropDownListFor(
x => x.Color.ColorId,
new SelectList(Model.ColorList, "ColorId", "ColorName"),
"-- select color --")
<br />
#Html.ValidationMessageFor(x => x.Color.ColorId)
</td>
</tr>
</table>
}
The easiest method is to use an old fashion FORM element and POST the values of the two drop downs to an action in your controller. That action would expect a carId and a colorId and use them to retrieve a record from the DB and then pass the result to your 'view' where you would take care of render/display the result.
Of course using this method has some caveats:
The entire page will refresh after a user selects a value from the
second drop down.
You would have to POST the form using JavaScript
when the user picks the second option, or at least enable a button so
the form can be POSTed.
You would have to keep track of the carId and
colorId in your controller and view
Another option is to use AJAX to POST (send to the server) the carId and colorId where and action in a controller will take care of using those parameters to find a record in the DB and then return a JSON object with the result. The response will be handled by a 'success' handler where you will take care parsing the JSON object and add rows to a table.
So if you feel more comfortable working on the server side of the code pick the first option, however if you prefer to use AJAX and do this in the front end use the later.

Webgrid refresh with ajax in MVC3

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() {
....

Categories

Resources