Retrieving multiple values from C# to JavaScript - javascript

So I have an a tag with an onclick:
<a onclick="join(4);">Join</a>
Now when the a tag is clicked, it calls this code in this order:
JavaScript function:
function join(gymID) {
PageMethods.getGymInformation(gymID);
}
C# method:
[WebMethod]
public gymData getGymInformation(string gymID)
{
gyms gym = new gyms();
DataTable dt = gym.getNewGymInfo(System.Convert.ToInt32(gymID));
DataRow dr = dt.Rows[0];
return new gymData { name = dr["name"].ToString(), strength = dr["strength"].ToString(), speed = dr["speed"].ToString(), defence = dr["defence"].ToString()};
}
public DataTable getNewGymInfo(int gymID)
{
// This returns a datatable with 1 row
return gymTableApapter.getNewGymInfo(gymID);
}
[Serializable]
public sealed class gymData
{
public string name { get; set; }
public string strength { get; set; }
public string speed { get; set; }
public string defence { get; set; }
}
As you can see, the JavaScript join function calls the C# method which then retrieves a DataTable with 1 row, then using a custom data type it populates the strings with data to be returned..
Now I'm trying to figure out how to get the information returned from the C# method to be extracted in the JavaScript join function?
Is their a way to do this?

Add success/error callbacks in your JavaScript code. It might look something like this:
PageMethods.getGymInformation(gymID, onSuccess, onError);
function onSuccess (result) {
// result contains the returned value of the method
}
function onError (result) {
// result contains information about the error
}
Then in the onSuccess function you would have the returned value of the server-side method (of type gymData) in the result variable. At that point you can do whatever you need to do with it in your client-side code.
If the functions don't have any applicable re-use, you can even just add them in-line:
PageMethods.getGymInformation(gymID,
function (result) {
// success
}, function (result) {
// error
});

Related

Is it possible to have a method in C# that implicitly deserializes an argument if it's passed as a JSON string?

Question
I have a handful of ViewComponents that look like so:
public IViewComponentResult Invoke(BuyerViewModel buyer)
I'd like them to be able to accept either a BuyerViewModel, or a JSON string representing a BuyerViewModel. For example, when you pass JSON to a controller method from JavaScript, if that method expects an argument of type Dog, the controller automatically attempts to deserialize the JSON to an instance of Dog. I'm trying to mimic that behavior.
The goal would be that both of these examples work:
var buyer = new BuyerSummaryViewModel() { FirstName = "John" };
ViewComponent("Buyer", buyer);
ViewComponent("Buyer", "{\"Name\":\"John Smith\"}");
Why?
I'm trying to make a generic JavaScript method that can fetch a ViewComponent on the fly:
const fetchViewComponent = async (viewComponentName, viewModel) => {
let data = { viewComponentName, viewModel };
let html = await $.get(`/Order/FetchViewComponent`, data);
return html;
}
//Get a BuyerViewComponent (example)
(async () => {
let component = await fetchViewComponent("Buyer", `#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.Buyer))`);
console.log(component);
})();
What I've Tried
If I specify that the ViewModel is a BuyerViewModel, it works. The JSON string is automatically deserialized into a BuyerViewModel.
public class FetchViewComponentRequest
{
public string ViewComponentName { get; set; }
public BuyerViewModel ViewModel { get; set; }
// ^^^^^^^^^^^^^^
}
[HttpGet]
public IActionResult FetchViewComponent(FetchViewComponentRequest request)
{
return ViewComponent(request.ViewComponentName, request.ViewModel);
}
The Issue
However, I don't want to specify the type; I want this to be generic. So I tried this:
public class FetchViewComponentRequest
{
public string ViewComponentName { get; set; }
public string ViewModel { get; set; }
// ^^^^^^
}
[HttpGet]
public IActionResult FetchViewComponent(FetchViewComponentRequest request)
{
return ViewComponent(request.ViewComponentName, request.ViewModel);
}
But as expected, request.ViewModel isn't the correct type; it ends up null in the Invoke method. I was hoping there was a flag or something more global I could specify so that it tries to implicitly deserialize this string into the expected type.
Is there an easier way to do this that I haven't considered? Or, if not, is the way I'm envisioning even possible?
(I'm using .NET Core 2.2)
Maybe make your FetchViewComponentRequest generic?
public class FetchViewComponentRequest<T>
{
public string ViewComponentName { get; set; }
public T ViewModel { get; set; }
// ^^^^^^^^^^^^^^
}
[HttpGet]
public IActionResult FetchViewComponent(FetchViewComponentRequest<BuyerViewModel> request)
{
return ViewComponent(request.ViewComponentName, request.ViewModel);
}
The method needs to have some knowledge of what type to make the object coming in.
public T Convert<T>(dynamic obj) where T:class,new()
{
T myob = null;
if (obj !=null && obj is T)
{
myob = obj as T;
}
else if (obj is string)
{
//convert to type
myob = JsonConvert.DeserializeObject<T>(obj);
}
return myob;
}
Ok, im not sure about what you need.
But here is a dynamic way to do it, without specifying <T>.
//Assume that the namespace is DynamicTypeDemo
public class DynamicType {
// eg "DynamicTypeDemo.Cat, DynamicTypeDemo"
public string TypeName { get; set; } // the full path to the type
public string JsonString { get; set; }
}
Now you could simple DeserializeObject
public object ToObject(DynamicType dynamicType){
var type = Type.GetType(dynamicType.TypeName);
// Here you could check if the json is list, its really upp to you
// but as an example, i will still add it
if (dynamicType.JsonString.StartsWith("[")) // its a list
type =(List<>).MakeGenericType(type);
return JsonConvert.DeserializeObject(dynamicType.JsonString, type);
}
And here is how it work
var item = new DynamicType(){
TypeName = "DynamicTypeDemo.Cat, DynamicTypeDemo", // or typeof(Cat).AssemblyQualifiedName
JsonString = "{CatName:'Test'}"; // And for a list "[{CatName:'Test'}]"
}
object dynamicObject= ToObject(item); // return it to the javascript
Cat cat = dynamicObject as Cat; // Cast it if you want

Ajax success function not receive data

Below is webmethod of my web form which is returning a List of data and it works fine:
[WebMethod]
public static List<SalesInvoiceFinalCalculationEntity> salesInvoiceFinalCalculaiton(string InvoiceNo)
{
List<SalesInvoiceFinalCalculationEntity> list = new List<SalesInvoiceFinalCalculationEntity>();
list = SalesInvoiceManager1.salesInvoiceFinalCalculaiton(InvoiceNo);
return list;
}
But in below Ajax function, I can't retrieve the data. When I bind data to textbox in ajax success function, it displays Undefined text in Html textBox.
function salesInvoiceFinalCalculaiton() {
var InvoiceNo = $("#txt_InvoiceNo").val();
$.ajax({
async: false,
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/AjaxRequestToServer.aspx/salesInvoiceFinalCalculaiton", //URI
data: "{InvoiceNo:'" + InvoiceNo + "'}",
dataType: "json",
success: function (data) {
document.getElementById("txtinvoicevalue").value=(data.totalprice);
document.getElementById("txtTotalDiscount").value = data.discountamt;
document.getElementById("txtTotalTaxableValue").value = data.taxableamt;
document.getElementById("txtTotalCGST").value = data.cgstamt;
document.getElementById("txtTotalSGST").value = data.sgstamt;
document.getElementById("txtGrandTotal").value = data.grandtotal;
},
error: function (xhr) {
if (xhr.statusText == "Invalid Request") {
sessionStorage.clear();
}
}
});
}
Here is Data Layer and the stored procedure:
public static List<SalesInvoiceFinalCalculationEntity> salesInvoiceFinalCalculaiton(string InvoiceNo)
{
try
{
List<SalesInvoiceFinalCalculationEntity> SalesInvoiceFinalCalculation = new List<SalesInvoiceFinalCalculationEntity>();
DataSet ds = SqlHelper.ExecuteDataset(Util.ConnectionString, CommandType.StoredProcedure, "sp_salesInvoiceFinalCalculaiton",
new SqlParameter("#InvoiceNo", InvoiceNo));
foreach (DataRow dr in ds.Tables[0].Rows)
{
SalesInvoiceFinalCalculationEntity list = new SalesInvoiceFinalCalculationEntity(dr);
SalesInvoiceFinalCalculation.Add(list);
}
return SalesInvoiceFinalCalculation;
}
catch (Exception ex)
{
throw ex;
}
}
And this is my entity Class:
public class SalesInvoiceFinalCalculationEntity
{
public int InvoiceNo { get; set; }
float totalprice { get; set; }
float discountamt { get; set; }
float taxableamt { get; set; }
float cgstamt { get; set; }
float sgstamt { get; set; }
float grandtotal { get; set; }
public SalesInvoiceFinalCalculationEntity() { }
public SalesInvoiceFinalCalculationEntity(DataRow dr)
{
InvoiceNo = Convert.ToInt32(dr["InvoiceNo"]);
totalprice = float.Parse(dr["totalprice"].ToString());
discountamt = float.Parse(dr["discountamt"].ToString());
taxableamt = float.Parse(dr["taxableamt"].ToString());
cgstamt = float.Parse(dr["cgstamt"].ToString());
sgstamt = float.Parse(dr["sgstamt"].ToString());
grandtotal = float.Parse(dr["grandtotal"].ToString());
}
}
why is data is not received in success function!
First of all, using async: false it is a bad practice because it's freeze your window during to your request. Don't use it.
The issue is that you have to return a json object from your server-side method in order to receive response in success callback function of your ajax method.
[WebMethod]
public static string salesInvoiceFinalCalculaiton(string InvoiceNo)
{
List<SalesInvoiceFinalCalculationEntity> list = new List<SalesInvoiceFinalCalculationEntity>();
list = SalesInvoiceManager1.salesInvoiceFinalCalculaiton(InvoiceNo);
return JsonConvert.SerializeObject(list);
}
Web requests work with json format.
Finally resolved it. I forgot to mentioned
Public
in
SalesInvoiceFinalCalculationEntity
entity all variables and document.getElementById("txtinvoicevalue").value=(data.d[0].totalprice); this should be instead of
document.getElementById("txtinvoicevalue").value=(data.totalprice);
I think the problem is, that you're trying to send a Java Class to your JavaScript file. You can only send simple data types numbers, letters. As I understand, you're trying to access the member variables of SalesInvoiceFinalCalculationEntity.
If that's the case, you need to send it as a JSON object and get the data like this and then parse it.
The idea behind AJAX is to make the experience for the user better by not freezing the website using asynchronous requests. By calling the AJAX call with
async: false
removes the idea behind AJAX. You could then simply make a normal call to the server.
Use this:
https://www.newtonsoft.com/json
Serialize your list and return it as a string.
Then, in your javascript:
success: function (data) {
data = JSON.parse(data);
document.getElementById("txtinvoicevalue").value=(data.totalprice);
document.getElementById("txtTotalDiscount").value = data.discountamt;
document.getElementById("txtTotalTaxableValue").value = data.taxableamt;
document.getElementById("txtTotalCGST").value = data.cgstamt;
document.getElementById("txtTotalSGST").value = data.sgstamt;
document.getElementById("txtGrandTotal").value = data.grandtotal;
},
Try
success: function (data.d) rather than success: function (data). If I recall when using webmethods the return object is within data.d and not simply data.
Also, despite what other answers say. When using the [webmethod] attribute and jquery ajax, you do not have to convert your response object to json. It will do so automatically.

Cannot post complex object

I have the function on my view:
function setMessengerState() {
var serviceURL = $("#messenger-set-state-url").val();
var data = {
messengerState: g_messengerState,
};
$.ajax({
type: "POST",
url: serviceURL,
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: successFunc,
error: errorFunc
});
function successFunc(data, status) {
console.log("saved");
}
function errorFunc(data, status) {
console.log("failed?");
}
}
This is how the JSON.stringify(data) is formated on Chrome debugger:
"{"messengerState":{"IsOpen":true,"ConversationStates":[{"PartnerId":"64c71990-9ddc-4967-8821-a8e5936560a3","IsEnabled":true},{"PartnerId":"64c71990-9ddc-4967-8821-a8e5936560a3","IsEnabled":true}]}}"
And this is the value of $("#messenger-set-state-url").val():
"/Messenger/Messenger/SetMessengerState"
The controller method:
[HttpPost]
public async Task<ActionResult> SetMessengerState(MessengerStateInfo messengerState)
{
var user = User.ApplicationUser();
if (user == null)
return null;
bool success = await MvcApplication.Messenger.SetState(user, messengerState) != null;
return Json(success, JsonRequestBehavior.DenyGet);
}
And, finally, this is MessengerStateInfo and ConversationStateInfo:
public class MessengerStateInfo
{
public bool IsOpen { get; set; }
public ICollection<ConversationStateInfo> ConversationStates { get; set; }
public MessengerStateInfo()
{
ConversationStates = new ConversationStateInfo[0];
}
}
public class ConversationStateInfo
{
public string PartnerId { get; set; }
public bool IsEnabled { get; set; }
}
I cannot find where I'm doing it wrong. The post never gets to the controller method. I tried before with basic (string) parameters and it works just fine, but it simply doesn't get through with complex objects.
Thank you for writing a detailed question that allows reproducing the problem.
The model binder expects your ICollection to be writable, but arrays are not writable in this manner. Do a simple experiment:
ICollection<int> a = new int[0];
a.Clear();
A "Collection is read-only" exception will be thrown.
Now, how do you fix this. Change your MessengerStateInfo class definition to the following:
public class MessengerStateInfo
{
public bool IsOpen { get; set; }
public ICollection<ConversationStateInfo> ConversationStates { get; set; }
}
Here we removed the constructor, which allows the model binder to create a new instance of List<> type. This one, of course will be read-write and the binding succeeds.
Here are relevant code snippets from the model binder source code. This one is from System.Web.ModelBinding.CollectionModelBinder<TElement> class:
protected virtual bool CreateOrReplaceCollection(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext, IList<TElement> newCollection)
{
CollectionModelBinderUtil.CreateOrReplaceCollection<TElement>(bindingContext, newCollection, () => new List<TElement>());
return true;
}
And this one is one is from System.Web.ModelBinding.CollectionModelBinderUtil:
public static void CreateOrReplaceCollection<TElement>(ModelBindingContext bindingContext, IEnumerable<TElement> incomingElements, Func<ICollection<TElement>> creator)
{
ICollection<TElement> model = bindingContext.Model as ICollection<TElement>;
if ((model == null) || model.IsReadOnly)
{
model = creator();
bindingContext.Model = model;
}
model.Clear();
foreach (TElement local in incomingElements)
{
model.Add(local);
}
}
As you can clearly see from this code, if the collection not empty Clear method is called on it, which, in your case leads to an exception. If, on the other hand the collection is null, new List<> is executed which results in a brand new (writable) object.
Note: implementation details may differ depending on software version. The code above may differ from the actual code in the version of the library that you are using. The principle remains the same though.
Here is a tip how to find the reason faster than typing the question to Stackoverflow.
Put a breakpoint on public async Task<ActionResult> SetMessengerState(MessengerStateInfo messengerState) and observe that the breakpoint is not hit. Open chrome console and take notion of the error icon.
Click on the icon to see the error message at the bottom.
Now click on then link in the error message, you'll see this screen:
Finally click on the request in the "Name" column. You will see this:
This gets you the actual error message with the stack trace. In most cases you will be able to tell what's wrong from this message. In this particular case you will immediately see that an array is tried to be cleared and fails.
The error is actually modifying ICollection<ConversationStateInfo> object in ConversationStates = new ConversationStateInfo[0];
Changing it to List should solve the issue.
Set $.ajaxSettings.traditional to true. I already have the same problem, and works for me
$.ajaxSettings.traditional = true;
$('#btn').click(function () {
var array = [];
var url = '/Controller/Action';
$.post(url, { array : array });
});
});

ASP.NET MVC - How to "reverse" model binding to convert a C# model back to a query string representation

I have a custom javascript on the client side that I use to build up a querystring and pass over to my asp.net-mvc controller
var templateQueryString = BuildTemplate();
$.ajax({
url: '/MyController/Save?' + templateQueryString,
type: 'post',
dataType: 'json',
success: function (data) {
}
}
and on my controller all of the properties leverage the model binding so it comes in as a single object on the server side. NOTE: that this is a pretty complex object with arrays and arrays of sub objects:
public ActionResult Save(MyTemplate template)
{
}
the issue now is that I need to be able to convert from my C# object back to a string that represents "myTemplateQueryString" on the client side.
Is there any recommended way to take an object and do the "reverse" model binding. They key here is that it generates a string that I could use as a query string again in the future to pass into another asp.ent-mvc controller action.
Here is an example of the querystring that I am storing locally:
<input type="hidden" value="showIds=false&showRisks=false&
amp;statusIds=2&statusIds=1&statusIds=6&statusIds=8&
amp;statusIds=3&statusIds=9&showCompleted=0"
name="filterQueryString" id="filterQueryString">
As #haim770 said it would be easier if you used JSON in the request payload, and not the query string to pass your complex object to the server.
Regarding creating the query string from a model there is not a built-in method that does something like that or any recommended approach as far as i know. An obvious solution is to use reflection and build the query string from your properties.
Assuming your BuildTemplate class looks something like:
public class BuildTemplate
{
public bool ShowIds { get; set; }
public bool ShowRisks { get; set; }
public bool ShowCompleted { get; set; }
public int[] StatusIds { get; set; }
}
You can develop an extension method to convert any object to a QueryString. Here is some initial code you can start with:
public static class ObjectExtensions
{
public static string ToQueryString(this Object obj)
{
var keyPairs = obj.GetType().GetProperties().Select(p =>
new KeyValuePair<string, object>(p.Name.ToLower(), p.GetValue(obj, null)));
var arr = new List<string>();
foreach (var item in keyPairs)
{
if (item.Value is IEnumerable && !(item.Value is String))
{
foreach (var arrayItem in (item.Value as IEnumerable))
{
arr.Add(String.Format("{0}={1}", item.Key, arrayItem.ToString().ToLower()));
}
}
else
arr.Add(String.Format("{0}={1}", item.Key, item.Value.ToString().ToLower()));
}
return "?" + String.Join("&", arr);
}
}
Then you can easily invoke this code on any object to generate a query string:
var person = new BuildTemplate() { StatusIds = new []{ 1, 5, 8, 9 }, ShowRisks = true };
var queryString = person.ToQueryString();
This would generate a query string like:
"?showids=false&showrisks=true&showcompleted=false&statusids=1&statusids=5&statusids=8&statusids=9"
This query string should work just fine with the default model binder for the BuildTemplate class.

How to call page method with a parameter through in a javascript event?

I have method like this in my .cs :
[System.Web.Services.WebMethod]
public static void GetServiceInformation(IInfo x) //IInfo is an interface
{
x.l_power = true;
x.lb_InboxCount = UserTrans.GetInbox(int.Parse(emp_num), 0);
}
Now i want to call this method through a javascript method as a page method but it doesn't work .
<script type ="text/javascript">
function GetInfo() {
PageMethods.GetServiceInformation(this);
}
window.onload = setTimeout("GetInfo()", 3000);
</script>
<telerik:RadScriptManager ID="RadScriptManager1" runat="server" EnablePageMethods="true">
</telerik:RadScriptManager>
My .cs :
public partial class AppMaster : Log, IInfo //My page
{
public string Inbox
{
get
{
return hpl_Inbox.NavigateUrl;
}
set
{
hpl_Inbox.NavigateUrl = value;
}
}
public string Draft
{
get
{
return hpl_Draft.NavigateUrl;
}
set
{
hpl_Draft.NavigateUrl = value;
}
}
public string New
{
get
{
return hpl_New.NavigateUrl;
}
set
{
hpl_New.NavigateUrl = value;
}
}
public string Approved
{
get
{
return hpl_Approved.NavigateUrl;
}
set
{
hpl_Approved.NavigateUrl = value;
}
}
//------- etc
}
My interface :
public interface IInfo
{
string Inbox { get; set; }
string Draft { get; set; }
string New { get; set; }
string Approved { get; set; }
string archive { get; set; }
string search { get; set; }
string cand { get; set; }
string pri { get; set; }
string power { get; set; }
string admin { get; set; }
string help { get; set; }
bool l_cand { get; set; }
bool l_pri { get; set; }
bool l_power { get; set; }
bool l_admin { get; set; }
string lb_ApprovedCount { get; set; }
string lb_InboxCount { get; set; }
string lb_archive { get; set; }
string lb_DraftCount { get; set; }
}
function GetServiceInformation(x) {
$.ajax({
type: "POST",
url: "page.aspx/GetServiceInformation",
data: "{'x':'" + x + "'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: on_sucess,
error: on_error
});
function on_sucess(data, status) {
alert(data);
}
function on_error(request, status, error) {
alert(error);
}
}
Sorry, if it doesn't work
Answer Edit Based On Chat Discussion
First, thanks for clarifying your question. It was bit hard to understand the problem you were trying to solve. The reason? Because your code wasn't clear enough and that usually happens when there are design issues. That's effectively what your facing here a bit of a design issue. First, I'll point out some mistakes...
In this javascript function...
function GetInfo() {
PageMethods.GetServiceInformation(this);
}
this is NOT an instance of your page. So there's no use to make your page implement an interface...
public partial class AppMaster : Log, IInfo{}
and expect that a javascript call would page an instance of System.Web.UI.Page to your class (not to mention an implementation of the IInfo interface). You can blindly ditch this approach because it's a permanent design issue and it's not even going to work.
Now, if what you want is to serve the page, then do some further processing and finally send the results of this processing back to the client asynchronously using javascript/ajax you have a couple of approaches here:
Using SignalR which is my favourite approach (but you already stated your solution doesn't meet the requirements to use SignalR)
Using jQuery ajax which is also a very valid approach
Now, I'll explain the second approach
Using jQuery Ajax
Simply render the page as you would normally do in ASP.NET. Then on the client-side, when the page loads, make an ajax request to start processing the information you want to display. You can start the request as soon as the page loads to make the processing on the server
$(function(){
$.ajax({
type: 'POST',
url: 'AppMaster.aspx/GetServiceInformation',
data: "{}",
contentType: 'application/json;charset=utf-8',
dataType: 'json',
success: function(d) {
//load data received
},
error: function() {
//process the error
}
});
});
In the success handler you need to load the values received from the ajax call on your web controls. Then change your IInfo interface to a concrete object in a separate code file. But, remember that this class should NOT hold any references whatsoever to your web controls
public class JSInfo
{
string Inbox { get; set; }
string Draft { get; set; }
string New { get; set; }
string Approved { get; set; }
string archive { get; set; }
string search { get; set; }
string cand { get; set; }
string pri { get; set; }
string power { get; set; }
string admin { get; set; }
string help { get; set; }
bool l_cand { get; set; }
bool l_pri { get; set; }
bool l_power { get; set; }
bool l_admin { get; set; }
string lb_ApprovedCount { get; set; }
string lb_InboxCount { get; set; }
string lb_archive { get; set; }
string lb_DraftCount { get; set; }
}
then change your page method to...
[System.Web.Services.WebMethod]
public static JSInfo GetServiceInformation()
{
//you need to get the emp_num from session
//construct the JSInfo object
JSInfo info = new JSInfo();
//get the data from the database
var data = UserTrans.GetInbox(int.Parse(emp_num), 0);
//set the properties of the JSInfo...similar to the line below for each property...Draft, New, Approved, etc
info.Inbox = data.Inbox;
//return the object to the client
return info;
}
Notice that you need to get the emp_num value from Session since you stated in the chat discussion that this value comes from a Session variable. Now, going back to the success handler of your jQuery ajax call which executes soon after the response is received back from the server. You will receive a json object in the handler parameter d with the properties of the JSInfo class that you just sent from the server. Then you set the controls on the page...
success: function(d) {
$('#id_inbox_control').val(d.Inbox);
$('#id_draft_control').val(d.Draft);
$('#id_new_control').val(d.New);
//and keep doing the same for the rest of the controls
},
That should be a neater solution. Of coure, I cannot cover every single details here. But for sure you will get the idea. If not, let me know if I need to expand on something.
If your page implements the interface, you don't have to pass it! In your c# code write:
this.l_power=true;
If you need to pass values from JavaScript to page method, define each property as a parameter and pass values to the page method:
[System.Web.Services.WebMethod]
public static string GetServiceInformation(int value1, string value2)
{
l_power = value1;
something = value2;
return "some string to indicate the result of call";
}
And:
<script type ="text/javascript">
var v1 = 15;
var v2 = "some value";
function GetInfo() {
PageMethods.GetServiceInformation(v1, v2, success, fail);
}
window.onload = setTimeout("GetInfo()", 3000);
</script>
in which success and fail are the names of two JS functions that will be called after the request is completed. Note that a page method can return a string value to inform the client about what happened on the server.
I can only think of one method.
You should somehow marshal the this object, and send it as parameter. I mean you should write a method that marshalls an object to equivalent json or xml, and POST that to your server.
I believe you can do it as you did above only through a clean API and compiler tool between C# and javascript to implement RPC just like GWT was written for java and javascript.
Can you do a little test?
Declare a public class JSInfo: IInfo{} in your page code, and in your web method declare that parameter of yours as JSInfo.
As JSInfo implements IInfo, your program logic can work with it without any problem.
Just to let you know, your code does not work because you cannot serialize interfaces as they
are not concrete types, if you think about it, interfaces have no real correlation in XML schema. There's no way to represent the data. Base classes will work however.
If you fill bad in declaring the JSInfo in the asp.net page class, then create a class called
WebMethodsHelper and declare your JavaScript WebMethod Interfaces (Adaptors) there.
public class JSInfo: IInfo{
private ControlsCollection controls;
public JSInfo(ControlsCollection constrols){
this.controls = controls
FillObjects();
}
private void FillObjects(){
//iterate through controls and extract you data to you
//class properties/fields
}
public void Update(ControlsCollection controls){
this.controls=controls;
FillObjects();
}
public void Update(JSInfo info, ControlsCollection controls){
this.controls=controls;
//populate your object based on info
//then extract data from page controls
FillObjects();
}
}
public class MyPage: System.Web.UI.Page{
protected void Page_Load(object sender, EventArgs e){
if(!IsPostBack && Session["info_obj"])
Session["info_obj"] = new JSInfo(this.Controls);
}
[System.Web.Services.WebMethod]
public static string GetServiceInformation(JSInfo data)
{
JSInfo info = new JSInfo(this.Controls);
info.Update(data);
//or if you stored the info in the session
JSInfo info = (JSInfo)Session["info_obj"];
info.Update(this.Controls, data);
}
}
The JSInfo is just to give your IInfo interface some structure so it can be serialized.
From JavaScript you should be able to call you page method like this:
<script type ="text/javascript">
function GetInfo() {
var info = new JSInfo();
info.PropertyXPTO="something";
PageMethods.GetServiceInformation(info, onSuccess, onError);
}
function onSuccess(result) {
alert(result);
}
function onError(result) {
alert('error: ' + result);
}
window.addEventListener("load", function(){
setTimeout("GetInfo()", 10 * 1000);
}, false);
</script>
Not that you should have a ScriptManager at the top of your page
<asp:ScriptManager ID="ScriptManager1" EnablePageMethods="true" runat="server" />
The ScriptManager is responsible for giving you the PageMethods class in the JavaScript, along
with other things.
Also, confirm the following:
The page method must have the System.Web.Services.WebMethod attribute. [WebMethod]
The page method must be public. [WebMethod] public ...
The page method must be static. [WebMethod] public static ...
The page method must be defined on the page (either inline or in the code-behind). It cannot be defined in a control, master page, or base page.
The ASP.NET AJAX Script Manager must have EnablePageMethods set to true.
function GetServiceInformation(x) {
$.ajax({
type: "POST",
url: "page.aspx/GetServiceInformation",
data: x, //Attention: there is no {}
contentType: "application/json; charset=utf-8",
dataType: "json",
success: on_sucess,
error: on_error
});
function on_sucess(data, status) {
alert(data);
}
function on_error(request, status, error) {
alert(error);
}
}
And then
<script type ="text/javascript">
function GetInfo() {
var myInfo = {
Inbox: "",
Draft: "",
New: "",
l_cand: ""
......//Attention, you should make this class corresponding to your server class IInfo
};
PageMethods.GetServiceInformation(myInfo);
}
window.onload = setTimeout("GetInfo()", 3000);
Referred to #anotherdie. And tell you how to transfer "X"
In your .js
function GetInfo() {
var parameter = {};
parameter.name = "test";
parameter.id = 123;
parameter.state = true;
PageMethods.GetServiceInformation(parameter,
function (res) {
if (res == true) {
//do some
alert("ok");
} else {
//do some
alert("bad");
}
}, function(err){
alert("ERROR: "+err._message);
});
}
in your apsx.cs (you can return a string, a list, a bool, an int or a json object //for json use json.net http://james.newtonking.com/json) for this i'll return a bool.
using System.Web.Services;
[WebMethod]
public static bool GetServiceInformation(ClassData parameters)
{
try
{
//do some
return true;
}
catch(Exception ex)
{
return false;
}
}
in a interface ClassData .cs
public string name { get; set; }
public int id { get; set; }
public bool state { get; set; }
public ClassData(){}
public ClassData(string _name, int _id, bool _state)
{
this.name = _name;
this.id= _id;
this.state = _state;
}
I do the following :
Create New Page and called it : Counts.aspx
protected void Page_Load(object sender, EventArgs e)
{
emp_num = int.Parse(Session["empnum"].ToString());
Thread.Sleep(3000);
string res = GetCounts(emp_num);
Response.Write(res);
}
/***********************************************************************************************/
protected string GetCounts(int empNum)
{
string outbox = UserTransaction.getoutboxCount(empNum, 0);
string inbox = UserTransaction.getinboxCount(empNum, 0);
string archive = UserTransaction.getarchivecount(empNum, 0);
string draft = UserTransaction.getdraftcount(empNum, 0);
return outbox + "~" + inbox + "~" + archive + "~" + draft + "~";
}
and in my main page :
<script type="text/javascript">
function loadXMLDoc() {
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
}
else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var split = xmlhttp.responseText.split('~');
var outbox = split[0];
var inbox = split[1];
var archive = split[2];
var draft = split[3];
document.getElementById("lbl_DraftCount").innerHTML = draft;
document.getElementById("lbl_InboxCount").innerHTML = inbox;
document.getElementById("lbl_ApprovedCount").innerHTML = outbox;
document.getElementById("lbl_archive").innerHTML = archive;
}
}
xmlhttp.open("GET", "Counts.aspx", true);
xmlhttp.send();
}
loadXMLDoc();
</script>

Categories

Resources