I am attempting to send an AJAX request from an ASP.NET Gridview row. Each row in the gridview shows completed jobs. Some jobs have files that can be downloaded. The end goal here is to show the download urls in a colorbox overlay, but I am having trouble just returning the list of files to the base page now. To show that there are files to download from an order I unhide a button control in the order number column. Javascript on the page follows.
$(document).ready(function () {
$("#<%=gvMessenger.ClientID%> input").filter(":not(:has(table, th))").click(function (e) {
e.preventDefault();
var $cell = $(e.target).closest("td");
//$("#<%=gvMessenger.ClientID%> td").removeClass("highlight"); $cell.addClass("highlight"); $("#message").text('You have selected: ' + $cell.text());
$("#Message").html("");
var orderID = $cell.text()
orderID = $.trim(orderID);
sendData(orderID);
function sendData(orderID) {
var loc = window.location.href;
loc = (loc.substr(loc.length - 1, 1) == "/") ?
loc + "CompletedOrdersNew.aspx" : loc;
$.ajax({
type: "POST",
url: loc + "/GetFiles",
data: "{'orderID':'" + orderID + "'}",
contentType: "application/jason; charset=utf-8",
datatype: "json",
success: function (msg) {
$("ContentArea").html(msg.d);
},
error: function () {
alert("An error occured in getting your files.");
}
});
}
});
});
The function in the page codebehind that should fire on the ajax request follows.
<WebMethod()> _
Public Shared Function GetFiles(ByRef orderID As Integer) As String
Dim dict As New Dictionary(Of String, Object)
Dim dt As DataTable
dt = Dac.ExecuteDataTable("GetS3Files", Parameter("#OrderID", orderID))
Dim arrr(dt.Rows.Count) As Object
For i As Integer = 0 To dt.Rows.Count - 1
arrr(i) = dt.Rows(i).ItemArray
Next
Dim json As New JavaScriptSerializer
Return json.Serialize(dict)
End Function
When watching the page in FireBug I see the request go out to the GetFiles function with the orderID number {'orderID':'10000315'}
POST http://localhost:57210/AMSSite/Customer/CompletedOrdersNew.aspx/GetFiles
But the call does not fire the GetFiles function and the response I am getting is the page html.
Not sure what is going on here.
Try sending orderID as a number since you webmethod expects an int, also in JSON keys and strings are quoted with double quotes(")
data: "{\"orderID\":" + orderID + "}",
You made a typo in contentType option. Should be: contentType: "application/json; charset=utf-8"
I'll answer my question for the sake of any other VB.NET/ASP.NET folks running into this problem. Aside from my misspelling the issue here was that my WebMethod function was ByRef. It needs to be BYVAL. After banging my head against the wall and reading some great stuff on javascript and json services I found the real answer at http://projectsmm.com/technet/asp/index.shtml. VB.Net by default sets function variables to ByRef. For some reason .NET web services don't accept this. Change to ByVal and it works.
Hope this helps some one else not spend the hours I looked at this.
Related
Can we create a simple post method in ASP.NET web service which runs like a REST API method?
Because I don't want to provide an envelop for post and get request.
Can anyone provide a solution for this?
Not only can you do this, but in fact, if you create a web method in .net, right out of the box:
It can accept soap 1.1, 1.2 - no extra code on your part
It can accept a rest call - even json data - no extra code on your part
It can accept a plane jane URL (again rest) call - no extra code on your part
So, in fact, you get all 3 of the above - in fact the above counts as 4, since your ajax call can use (data) or the URL to pass the information.
Say, I add a I create a web service page like this:
Do note, you don't HAVE to crate a web service page, you can drop into a EXISTING page a function (returns values), or drop in a sub into that page. As long as you ta the function/sub with WebMethod(), then you are good to go.
So, with above page, lets add a simple function. It will take a paramter FirstName, and LastName, and return the full name.
So, we have this code in the above web service page:
<WebMethod()>
Public Function GetName(FirstName As String, LastName As String) As String
Dim sResult As String = ""
sResult = FirstName & " " & LastName
Return sResult
End Function
Above is VERY simple.
So, you can use jQuery, and call this method via post like this:
<asp:Button ID="cSoap" runat="server" Text="Soap Example"
OnClientClick="webcalltest();return false" />
<script>
function webcalltest() {
var First = 'Albert'
var Last = 'Kallal'
var url = '../WebService1.asmx/GetName'
$.ajax({
type: "POST",
url: url,
dataType: "json",
data: JSON.stringify({ FirstName: First, LastName: Last }),
contentType: "application/json; charset=utf-8",
success: function (msg) {
alert(msg.d)
},
error: function (msg) {
alert("Failed: " + msg.status + ": " + msg.statusText);
}
});
As noted, that web method will also accept soap calls.
And you can even skip the headers (in the above post), and toss out a 100% plane jane REST call - just the URL, say like this:
<asp:Button ID="Button2" runat="server" Text="Soap Example"
OnClientClick="webtest2();return false" />
<script>
function webtest2() {
var First = 'Albert'
var Last = 'Kallal'
var url = '../WebService1.asmx/GetName?FirstName=' + First + '&LastName=' + Last
$.get(url, function (data, status) {
alert($(data).find('string').text())
alert("Data: " + data + "\nStatus: " + status);
})
}
</script>
So, in above, we just used the URL for the rest call. (and did not set up any headers, or even a soap xml record). So, just keep in mind, even older legacy soap methods in most cases can be passed and consume JSON data - and you in general don't have to re-write or even change the web methods.
And as above shows, you can just feed the URL with the parameters, and not pass any headers at all - let alone the soap ones.
I have a page where i have to save some data [into the database] every few seconds.This is similar to how gmail or stackoverflow saves a draft every few seconds. I am using jQuery Ajax to achieve this. Following is my Ajax call :
Question: Is this the correct way to do this? I am not comfortable with opening and closing the connection every few seconds.
function ShowHtml() {
var SaveStoryForEditing = '<%= Page.ResolveUrl("~")%>Webservices/WebService.asmx/SaveStoryForEditing';
$('#savedata').html($('#InPlaceEdit').html());
$("#InPlaceEdit").find("textarea").each(function (idx) {
$("#savedata").find("textarea").eq(idx).text($(this).val());
});
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: SaveStoryForEditing,
dataType: 'json',
async: false,
data: "{'StoryEditId':" + $('#hfStoryEditID').val() + ",'AccountId':" + $('#hfAccID').val() + ",'StoryHtml':'" + $('#savedata').html() + "'}", // Page parameter to make sure we load new data
success: function (data) {
var myObject = eval('(' + data.d + ')');
$('#InPlaceEdit').effect("highlight", { color: "#ff0000" }, 1000);
$('#savedata').html('');
AutoSave();
},
error: function (result) {
AutoSave();
alert(JSON.stringify(result));
}
});
};
function AutoSave() {
setTimeout("ShowHtml()", 30000);
};
This is the function i am calling from the webservice:
Public Shared Function SaveStoryForEditing(ByVal StoryEditId As Integer, ByVal AccountId As Integer, ByVal StoryHtml As String) As Object
Dim db As SqlDatabase = Connection.Connection
Dim scalar As Object
Dim cmdIf As SqlCommand = CType(db.GetSqlStringCommand("UPDATE StoryEdit SET StoryHtml=#StoryHtml WHERE AccountID=#AccountID AND StoryEditID=#StoryEditID"), SqlCommand)
db.AddInParameter(cmdIf, "AccountID", DbType.Int32, AccountId)
db.AddInParameter(cmdIf, "StoryEditID", DbType.Int32, StoryEditId)
db.AddInParameter(cmdIf, "StoryHtml", DbType.String, StoryHtml)
scalar = db.ExecuteNonQuery(cmdIf)
Return scalar
End Function
This is my connection object class:
Imports Microsoft.Practices.EnterpriseLibrary.Data.Sql
Imports System.Data.SqlClient
Public Class Connection
Public Shared Function Connection() As SqlDatabase
Dim db As SqlDatabase = New SqlDatabase(System.Configuration.ConfigurationManager.ConnectionStrings("TripNestConStr").ConnectionString)
Return db
End Function
End Class
I believe this is not a good way to go. You're creating (probably) unnecessary traffic. If you really need such functionality, I'd at least consider some kind of mechanism that will track changes and execute ONLY if something has changed after the last server call. You don't really need to send a request to the server and access the database, if the data haven't changed.
Just some idea for you to consider, it all depends on your specific situation and needs.
I am thinking why don't use the hidden field kind of flag to check if the value is updated or not. On keypressup of the textarea you can set the hidden value as true. In the autoSave method check the value of the hidden field , if it is true then only go back to server. Once you picked the value update it back to false again . So that it will help you to save some time overload . Another way is rather than saving true false , you can also save the time stamp of change and have a global variable in javascript which will tell the last time you have updated in the database. If the global value is in past compare to current update in the hidden field , you need to sync.
I have the folowing script which does not seem to work. The aspx page returns json similar to the json in the script below which has been commented out. If I stick that json directly into the source as an array, it works perfectly.
But as soon as I try to use the script below, I get no error messages or anything, nothing happens when i type into the autocomplete field.
$(document).ready(function(){
$('#button').click(function() {
alert($("#txtAllowSearchID").val());
});
//var $local_source = [ {id:0,value:"c++"}, {id:1,value:"java"}, {id:2,value:"php"}, {id:3,value:"coldfusion"}, {id:4,value:"javascript"}, {id:5,value:"asp"}, {id:6,value:"ruby"} ];
$("#txtAllowSearch").autocomplete({
source: function(request, response) {
$.ajax({
type: "POST",
url: "test_array.aspx",
data: "{'prefixText': '" + $('#txtAllowSearch').val() + "'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data) {
response(data.d);
},
failure: function(errMsg) {
$('#errMessage').text(errMsg);
}
});
},
select: function (event, ui) {
$("#txtAllowSearch").val(ui.item.value); // display the selected text
$("#txtAllowSearchID").val(ui.item.id); // save selected id to hidden input
}
});
});
EDIT: I think the problem is in the aspx page:
objSQLCommand = New SqlCommand("select id, value from table1 where value like '%#prefixText%'", objSQLConnection)
objSQLCommand.Parameters.Add("#prefixText", SqlDbType.VarChar, 255).Value = "ing"
I believe that you have to make the source of the ajax call to a web service, which in your case seems to be a Page Method. So if you have a function in your code behind page look like this:
public static List<string> GetData(string prefixText){ }
You would need to decorate that method with [WebMethod] from the System.Web.Services namespace.
So it would finally look like:
using System.Web.Services;
...
[WebMethod()]
public static List<string> GetData(string prefixText){ }
HTH
EDIT: also you would need to update your ajax call to look like this for the source:
source: 'test_array.aspx/GetData'
It looks to me like the issue might be the source function. When you make the ajax call, it is done asynchronously. So, the function kicks off the ajax call and continues, which returns nothing for the source.
I discovered this when I was doing loading screens. If I put the close for the loading screen after the ajax call, the loading screen would not appear. I had to move it inside the ajax call success and error events to get it to appear and disappear correctly.
Your source should just be a reference to the url
source: "test_array.aspx",
From the documentation:
The datasource is a server-side script
which returns JSON data, specified via
a simple URL for the source-option.
I'm doing the following ajax call:
//exif loader
function LoadExif(cImage) {
$.ajax({
type: "POST",
url: "http://localhost:62414/Default1.aspx/GetImageExif",
data: "{iCurrentImage:" + cImage + "}",
contentType: "application/json; charset=utf-8",
dataType: "json",
error: ajaxFailed,
success: function (data, status) {
var sStr = '';
for (var count in data.d) {
sStr = sStr + data.d[count];
};
alert(sStr);
}
});
};
In Firefox the request works really fine. When I try to run the code in Internet Explorer, the response is empty.
Here is the webmethod witch is called:
<WebMethod()> _
<ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
Public Shared Function GetImageExif(ByVal iCurrentImage As Integer) As String
Dim sbTable As New StringBuilder
sbTable.AppendLine("<table>")
sbTable.AppendLine("<tr>")
sbTable.AppendLine("<td>Name</td><td>" & gGallery.Images(iCurrentImage).File.Name & "</td>")
sbTable.AppendLine("</tr>")
sbTable.AppendLine("</table>")
Return sbTable.ToString
End Function
Any ideas?
Jan
When you have alert(data); what do you see?
Thanks for this hint. This was the issue. I've copied the code from another of my projects.
alert(data.d);
... works in both browsers. Thanks for the quick answers.
Jan
Starting with ASP.NET 3.5 MS introduced a security feature where they encapsulate any JSON response in a parent object ("d").
Doing this helps against a XSS vulnerability (described here: http://haacked.com/archive/2009/06/25/json-hijacking.aspx)
That's the reason why it exists.
Here's how to handle it, if you code against multiple versions of ASP.NET, you can simply use the following check in your success function to detect it's existence:
if (data.hasOwnProperty('d'))
//use data.d in your code;
else
//use data in your code
As for why Firefox handles it appropriately and IE8 does not, I would assume it has something to do how each parses the JSON object.
I'm working on a web project that is multilingual. For example, one portion of the project involves some custom google mapping that utilizes a client-side interace using jquery/.net to add points to a map and save them to the database.
There will be some validation and other informational messaging (ex. 'Please add at least one point to the map') that will have to be localized.
The only options I can think of right now are:
Use a code render block in the javascript to pull in the localized message from a resource file
Use hidden fields with meta:resourcekey to automatically grab the proper localized message from the resource file using the current culture, and get the .val() in jquery when necessary.
Make a webservice call to get the correct message by key/language each time a message is required.
Any thoughts, experiences?
EDIT:
I'd prefer to use the .net resource files to keep things consistent with the rest of the application.
Ok, I built a generic web service to allow me to grab resources and return them in a dictionary (probably a better way to convert to the dictionary)...
<WebMethod()> _
<ScriptMethod(ResponseFormat:=ResponseFormat.Json, UseHttpGet:=False, XmlSerializeString:=True)> _
Public Function GetResources(ByVal resourceFileName As String, ByVal culture As String) As Dictionary(Of String, String)
Dim reader As New System.Resources.ResXResourceReader(String.Format(Server.MapPath("/App_GlobalResources/{0}.{1}.resx"), resourceFileName, culture))
If reader IsNot Nothing Then
Dim d As New Dictionary(Of String, String)
Dim enumerator As System.Collections.IDictionaryEnumerator = reader.GetEnumerator()
While enumerator.MoveNext
d.Add(enumerator.Key, enumerator.Value)
End While
Return d
End If
Return Nothing
End Function
Then, I can grab this json result and assign it to a local variable:
// load resources
$.ajax({
type: "POST",
url: "mapping.asmx/GetResources",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: '{"resourceFileName":"common","culture":"en-CA"}',
cache: true,
async: false,
success: function(data) {
localizations = data.d;
}
});
Then, you can grab your value from the local variable like so:
localizations.Key1
The only catch here is that if you want to assign the localizations to a global variable you have to run it async=false, otherwise you won't have the translations available when you need them. I'm trying to use 'get' so I can cache the response, but it's not working for me. See this question:
Can't return Dictionary(Of String, String) via GET ajax web request, works with POST
I've done this before where there are hidden fields that have their values set on Page_Init() and Page_Load() with the appropriate values from the global and local resource files. The javascript code would then work with those hidden values.
Code Behind
this.hfInvalidCheckDateMessage.Value = this.GetLocalResourceObject("DatesRequired").ToString();
Page.aspx
$('#<%= btnSearch.ClientID %>').click(function(e) {
if (!RequiredFieldCheck()) {
var message = $("#<%= hfInvalidCheckDateMessage.ClientID %>").val();
alert(message);
e.preventDefault();
$("#<%= txtAuthDateFrom.ClientID %>").focus();
}
});
Disclaimer... Not sure if this was the best route or not, but it does seem to work well.
I've implemented a modified version of the solution described here: http://madskristensen.net/post/Localize-text-in-JavaScript-files-in-ASPNET.aspx
I changed it to allow for multiple resource file names, if the need for it arises, by changing the regular expression and by using the HttpContext.GetGlobalResourceObject method to retrieve the resource string.
I use this technique. It makes it easy for anyone with little coding experience to edit the phrase JavaScript file.
var lang = 0 // 0 = english, 1 = french
var phrases=[]
phrases["plans"] = "Rate Plans and Features|Forfaits et options").split("|")
then you output it as:
phrases["plans"][lang]
...add more columns as required.