Empty string passed to getElementById() at query.unobtrusive-ajax.js:16 - javascript

I'm developing an ASP.NET MVC4 application and have started using jQuery actionlinks.
However when I run the following Razor code (and click the view ticket actionlink) I get a generic jQuery error (twice) saying that an empty string was passed to getElementById().
I have no idea where this error is happening since firefox merely links to the jQuery code.
This is my Razor code: (I know the js functions show and hideticket are empty but that is to simplify the code):
<script>
function ShowTicket(id) {
$("#viewTicketButton" + id).hide();
$("#hideTicketButton" + id).show();
$("#viewTicket").show();
}
function HideTicket(id) {
$("#viewTicketButton" + id).show();
$("#hideTicketButton" + id).hide();
$("#viewTicket").hide();
}
</script>
<h3>Your tickets</h3>
<table border="1">
<tr>
<td>Title:</td>
<td>Urgency:</td>
<td>Status:</td>
</tr>
#foreach (SupportTicketViewData t in Model.supportTicketViewDataList)
{
<tr>
<td>#t.title</td>
<td>#t.text</td>
<td>#t.status</td>
<td>#Ajax.ActionLink("View Ticket", "ViewTicket", new { id = t.id },
new AjaxOptions
{
HttpMethod = "GET",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "viewTicket",
OnComplete = "ShowTicket(" + t.id +");"
}, new { id = "viewTicket" + t.id })</td>
<td><button id="#Html.Raw("HideTicket" + t.id)" onclick="HideTicket(#t.id);">Hide Ticket</button></td>
</tr>
}
</table>
<div id="viewTicket">
</div>
Also I get a result from the GET request just fine since it get's inserted into the div element however I get 2 errors when debugging in firefox.
Also when I click the viewTicketButton the button doesn't hide as it should.

Warnings 'Empty string passed to getElementById()' occurs when sending form created via Ajax.BeginForm or Ajax.ActionLink with unobtrusive validation turned on.
In my case adding handlers to all events supported by Ajax.BeginForm fixed issue with warnings:
#using (Ajax.BeginForm(“SomeAction”, null, new AjaxOptions() {
OnBegin = “someFunction”,
OnComplete = “ShowTicket”,
OnFailure = “someFunction”,
OnSuccess = “someFunction”
}
....
I believe that this should fix your issue.
More details about issue on my blog post.

I believe you cannot simply do
OnComplete = "ShowTicket(" + t.id +");"
The argument must be a javascript function. If what you want to call is parameterless, you can do
OnComplete = "ShowTicket"
where show ticket is the function object, so this is fine.
In your case however, you've got to pass the ID to ShowTicket. Try the following:
OnComplete = "function() { ShowTicket(" + t.id +"); }"

You will likely have to add the slashes to compensate for the double quotes that you need in the id tag
eg:
Html.Raw("id=\"SomeIdString\"")

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/

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

Passing Razor syntax variable into javascript onclick of an Html Helper

Trying to render the page, and trigger a notification on button click. I have set this up to just do a pop up for now, since its an internal application for the higher ups.
The button in question is a simple razor syntax html helper with an onclick event to return a confirm dialog. However it seems I am unable to render the item variable for this.
#Html.ActionLink("Delete", "Delete", new { id = item.Id },
new { #class = "btn btn-warning",
onclick = "return confirm('WARNING: Delete #item.Id?');" })
this fails to render item.Id (the output of the surrounding foreach statement but instead outputs the string literal "#item.Id"
#Html.ActionLink("Delete", "Delete", new { id = item.Id },
new { #class = "btn btn-warning",
onclick = "return confirm('WARNING: Delete ' + item.Id + '?');" })
This renders the variable but since it is not a javascript variable it renders as "Delete undefined ?"
How can I use a variable from the foreach outside of the javascript passed into this dialog, to make the message more descriptive.
I was able to solve my own problem using string.Format in C#.
#Html.ActionLink("Delete", "Delete", new { id = item.Id },
new { #class = "btn btn-warning",
onclick = string.Format("return confirm('WARNING: Delete {0}?');", item.Id) })
You aren't closing your string properly. Try the following:
onclick = "return confirm('WARNING: Delete " + item.Id + "?');"
Your error is saying the javascript item item.Id is not defined because item is probably not defined in your js scripts.

Redirect action with parameters in JQuery

In my ASP.Net project I'm trying to redirect a page to other action after select a row table and click on a button. So I have this code:
JQuery:
function editItem(tableId, url) {
if ($("#" + tableId + " .selected").exists()) {
var thisRowId = $("#" + tableId + " .selected").attr("id");
window.location.replace(url, { operation: "edit", carId: thisRowId });
}
};
//more code
View (ManagementCar):
#model myProject.Model.Car.Car[]
<table class="table" id="tableCars">
<thead>
#*some code to header*#
</thead>
<tbody>
foreach (var item in Model)
{
<tr id="#(item.Id)" onclick="selectRow('tableCars', '#(item.Id)')">
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.OwnerName)
</td>
<td>
#(item.IsSold == true ? "Yes" : "No")
</td>
</tr>
}
</tbody>
</table>
<br />
<button id="btEdit" type="button" class="disable" onclick="editItem('tableCars','CarOperation')">Edit</button>
Controller:
[HttpGet]
public ActionResult CarOperation(string operation, int carId)
{
//some code...
return RedirectToAction("ManagementCar", "Backoffice");
}
But I have a Server Error after redirect saying carId parameter is null. I debug my jquery and that parameter isn't null. I tried also doing
$.get(url, { operation: "edit", carId: thisRowId });
instead
window.location.replace(url, { operation: "edit", carId: thisRowId });
but it don't redirect.
How can I solve this?
set it by giving it a new value like this.
window.location = window.location.replace(url, "shouldBeAstringNotAJsonObject");
The problem with using window.location is that the referrer is not passed on the request as this behaviour simply mimics a new request as if you had typed the URL into the address bar. If you intend to use website analytics, a reliable referrer will be quite important. I use jQuery to generate a dynamic link upon which I call click().
var url = '/my/url';
$('')[0].click();
Notice I click() the underlying element not the jQuery selected object, the reason being that a jQuery click only raises the event and the href is not navigated to whereas indexing to the element and clicking that will cause the same behaviour you would expect as if you had actually clicked a link on the page.
I have put together a jQuery.navigate plugin that neatly wraps this up and abstracts your site map away from your UI logic, which you might find useful. For example, using my plugin would mean you could remove your editItem function altogether and simply change your markup to something like this?
<tr id="#(item.Id)" onclick="$.navigate('to', 'edit', { id : '#(item.Id)' })">
Ok, I finally solved the problem with a new url routing config and the following code:
My RouteConfig:
routes.MapRoute(
name: "ManipulatingCar",
url: "{controller}/{action}/{operation}/{carId}"
);
My JQuery:
editItem = function (tableId, url) {
if ($("#" + tableId + " .selected").exists()) {
var thisRowId = $("#" + tableId + " .selected").attr("id");
var fullUrl = url + "/edit/" + thisRowId;
window.location = fullUrl;
}
};
Basically, controller action parameters must match with the configurations specified in RouteConfig.cs

Does JavaScript get excuted when partial view is loaded?

Background
I'm working with ASP.NET MVC. I've got a partial view which contains JavaScript. I'm using AJAX get to load the partial view into a <div> tag. The JavaScript registers a click event for a group of radio buttons.
Problem
It doesn't seem to be executing: when the radio buttons are clicked, the form doesn't get submitted.
Here is my partial view:
<% using (Ajax.BeginForm(ActionName.Approve, ControllerName.Supervisor, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "Result"}, new { id = "IsSupervisorApprovalRequiredForm" }))
{%>
<p>Is supervisor approval required?</p>
<label for="IsSupervisorApprovalRequired">Yes</label><%=Html.RadioButton("IsSupervisorApprovalRequired", "0", new { #class = "IsSupervisorApprovalRequiredYes" })%>
<label for="IsSupervisorApprovalRequired">No</label><%=Html.RadioButton("IsSupervisorApprovalRequired", "1", new { #class = "IsSupervisorApprovalRequiredNo" })%>
<%} %>
<script type="text/javascript">
$("#IsSupervisorApprovalRequired").click(function() {
$("form#IsSupervisorApprovalRequiredForm").submit();
});
</script>
Question
Does JavaScript get executed when partial view is loaded?
Yes and no. The order of execution in your scenario is as follows:
Page gets requested
ASP.NET Renders Partial View into the parent page
Javascript gets executed on that entire page
For your particular problem. You'll need to load that Javascript snippet on page load before it can actually bound to the events. Your code should look like the following:
<% using (Ajax.BeginForm(ActionName.Approve, ControllerName.Supervisor, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "Result"}, new { id = "IsSupervisorApprovalRequiredForm" }))
{%>
<p>Is supervisor approval required?</p>
<label for="IsSupervisorApprovalRequired">Yes</label><%=Html.RadioButton("IsSupervisorApprovalRequired", "0", new { #class = "IsSupervisorApprovalRequiredYes" })%>
<label for="IsSupervisorApprovalRequired">No</label><%=Html.RadioButton("IsSupervisorApprovalRequired", "1", new { #class = "IsSupervisorApprovalRequiredNo" })%>
<%} %>
<script type="text/javascript">
$(function() {
$("#IsSupervisorApprovalRequired").click(function() {
$("form#IsSupervisorApprovalRequiredForm").submit();
});
});
</script>
Wrap the statement in $(function() {...}); so it will get called when the document is ready.
So it would look something like this:
$(function() {
$("#IsSupervisorApprovalRequired").click(function() {
$("form#IsSupervisorApprovalRequiredForm").submit();
});
});
This might also be caused by the HTML generated by the HtmlHelper. Multiple HTML elements with the same ID are not allowed, but the helper will generate something like:
<input id="IsSupervisorApprovalRequired" name="IsSupervisorApprovalRequired" type="radio" />
<input id="IsSupervisorApprovalRequired" name="IsSupervisorApprovalRequired" type="radio" />
As a result, when you match "#IsSupervisorApprovalRequired" with jQuery, it's looking for an element with that ID. Since two of them exist, the function will only be bound to the first one, causing the second radio button's "click" event to never fire.
As an alternative, try this:
$("input[name=IsSupervisorApprovalRequired]").click(function () { /* ... */ });
This approach checks the "name" attribute of the element instead of its ID. Since "name" values, unlike IDs, don't have to be unique, jQuery is able to handle multiple elements matching that pattern and should bind the event correctly.

Categories

Resources