Last year I did some posts on ASP.Net AJAX and how to use it. The posts were based on my experiences of using it up to that point. Since then, I've been busy on a lot of different project and am finally circling back around to ASP.Net AJAX.
I ran into an issue a couple weeks ago, now, that caused more research time than I was hoping for. The scenario was simple. A user types in an ID into a removal form that would, in essence, remove the record from the database. Instead of blindly "deleting" the record, the new update was to query the record and provide information about it into a new confirmation-like window. At that time the user can commit the removal or opt to not do it. While querying and processing, a "cancel" button was to be placed to prevent the current action from taking place.
This sounds simple enough. In order to boost page performance (the page is already littered with legacy practices that need to be cleaned up), I opted to implement page methods to do the actions instead of Update Panels. This made a noticeable difference; however, it raised the question of how do I cancel an asynchronous page method call.
After a handful more hours than I had hoped, I still couldn't find the answer already posted. Then I finally located this post that was doing what I wanted using Web Services. In it, Kazi Manzur Rashid Amit, does a great job in explaining the technique required cancel a web service call. Since Enabling Page Methods cause the page's public static methods to be exposed as web methods, I thought they were the same; however, there seems to be a slight difference which I'll mention after the I through the process.
PageMethods.myStaticPageMethod(param, onSuccess, onFailure)
Now, where the issue comes in, which Amit's blog points out, is that this "myStaticPageMethod" syntax is references a void method (or a method that doesn't return any value or object). While the page method's code may pass a value into the "onSuccess" or "onFailure" methods, the actual page method call returns no value. In order to have the method return a value, or to be more specific the instance of the request object, we need to change the syntax slightly.
PageMethods._staticInstance.myStaticPageMethod(param, onSuccess, onFailure)
var request; //outside of any function definition
request = PageMethods._staticInstance.myStaticPageMethod(param, onSuccess, onFailure) //inside my functions
Now, that I have the request object assigned, I need to cancel the request. To do this, Amit's blog pointed me to the WebRequestExecutor object. Microsoft's documentation on the WebRequestExecutor object can be found here. This object allows for web requests to be queried and acted upon. For our goal, we want to utilize its "abort()" method; however, we first have to get our request's executor. To do this, we call the request's "get_executor()" method.
var executor = request.get_executor();
After we have the executor object, we have to ensure that it's still started. The WebRequestExecutor has a started property and to access it's value, the API has a "get_started()" method. If the method returns true, then the executor has started processing our web request else it hasn't yet or has already finished. Once we have identified that the executor has started processing our request, we can then abort it. Below is the code snippet that illustrates this.
Here's where I saw a difference between my method and the code posted on Amit's blog. When I called the abort method, my "onFailure" method was being called and providing the error message "The server method 'myStaticPageMethod' failed." When I ran Amit's code which uses Web Services, I didn't see this error. I haven't looked through the code thoroughly to see if the error is being hidden or just not bubbling so I there may not be a true difference here. Regardless, I ended up throwing together a small "hack" to prevent the error for being shown to the end user each time a request was canceled.
Since I could not prevent the error, I ended up having to hide it. I don't like this solution; however, I haven't been able to find a better one. If anyone knows of a better, cleaner way please let me know. In order to hide the error, I declared another global variable, similar to the request variable of these examples. I named the variable "aborted" since it would signify whether or not the request has been or not by my code. Afterwards, I just assigned it the boolean value of true before I abort the call.
var aborted = false; //placed outside of any function
//modified aborted snippet
aborted = true;
Now that I had an indicator when a user attempted to manually abort a method call, I could add the following into my "onFailure" function to prevent the error from appearing.
if (aborted == true)
aborted = false;
So, after all of that, we have done the following to add the functionality of canceling a Page Method to our code:
- Declared the "request" global variable
- Declared the "aborted" global variable
- Set the "request" variable to the WebRequest object returned by the static instance version of our page method
- Set the "request" variable = null at the beginning of each method to prevent confusion
- Created an "executor" variable in our cancel request method.
- Set the "executor" variable to the WebRequest's executor property value
- Ensured the "executor" variable has started processing our request
- If started, set the "aborted" variable to true
- If started, called the "executor" variable's "abort()" method
- Stopped the failure method from fully processing the error by returning if the request was aborted.
That's a lot of steps; however, there may be a better way. Again, if there is an overall better way or at least a better way to prevent that error message when aborting a PageMethod request, please let me know.