views:

882

answers:

10

Hi all

I've created a page which combines an inline jQuery UI datepicker . I wan't to initiate a callback to the updatepanel when the user clicks a new date to update some data. Now, this page has multiple updatepanels (don't ask :) ), so I need to check which updatepanel did the reload in order to do some stuff clientside. I'm using __doPostBack to do the postback and Sys.WebForms.PageRequestManager.getInstance().add_endRequest(function(sender, args) {} to listen for when an update is done.

The problem is that in FF the callback tells me that it's not an async operation, and the data I need to have to check which updatepanel did the update is set to null.

My problem is that this works fine in IE, but not in any other browser, which could be caused by 2 things: IE goes into a quirksmode which resolves the issue (this is what I think) or that IE has some sort of native support for the updatepanel that other browsers dont.

I've narrowed the problem down, and produced a testpage:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head runat="server">
    <title>Test</title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js "></script>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js "></script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server" />

    <script type="text/javascript">
        function doAspNetPostback() {
            __doPostBack('<%= hiddenOnSelectionChangedButton.ClientID %>', '');
        }

        $(document).ready(function() {
            /* Create the datepicker */
            buildDatepicker();
            /* Add callback-method for when the scriptmanager finished a request */
            Sys.WebForms.PageRequestManager.getInstance().add_endRequest(function(sender, args) {
                /* Show the panelID - This is 'upd1|hiddenOnSelectionChangedButton' in IE as it should be, and null in Chrome / FF */
                alert(sender._postBackSettings.panelID);
            });
        });

        function buildDatepicker() {
            var dp = $("#datepicker").datepicker({
                onSelect: function(dateText, inst) {
                    /* Do a postback when someone clicks a date */
                    doAspNetPostback();
                }
            });
        }        
    </script>

    <div id="datepicker">
    </div>
    <asp:UpdatePanel UpdateMode="Conditional" ID="upd1" runat="server">
        <ContentTemplate>
            <asp:Button ID="hiddenOnSelectionChangedButton" Text="Press me" runat="server" />
            <div id="lala" runat="server">
                Upd1
            </div>
        </ContentTemplate>
    </asp:UpdatePanel>
    </form>
</body>
</html>

Firing this testpage up in IE will result in a messagebox which shows what it's supposed to, while loading it in FF or Chrome results in a messagebox which says "null" :)

I've tried all sorts of things, but I'm unsure of what could cause this behaviour as I'm not really that deep into the inner workings of jQuery or ASP.NET Ajax. However, if I step through the code in FireBug slow enough it works ... Which leads me to think it might be a problem with interoperability between jQeury callbacks and ASP.NET Ajax callbacks?

Any help or ideas will be highly appreciated.

Thanks.

[UPDATE] Tim solved it! Thanks a lot! - Also thanks to everyone who spent their time trying to figure this out :)

A: 

A common problem with combinations of AJAX and jQuery is that this...

$(document).ready(function() {
       buildDatepicker();

Only happens on the first page load. If you replace the area of HTML that has been affected by buildDatapicker(); you lost any events that were attached to it (even if you've replaced it with the same elements).

All you need to do is call buildDatepicker against the newly loaded HTML... and pass in the element that has been loaded just to ensure you don't double up...

$(document).ready(function() {
       buildDatepicker(document);
...

function buildDatepicker(element) {
    var dp = $("#datepicker", element).datepicker({
        onSelect: function(dateText, inst) {
            /* Do a postback when someone clicks a date */
            doAspNetPostback();
        }
    });
}

Now you can call buildDatepicker(myNewlyLoadedElement); to re-bind the data picker functionality.

Sohnee
Well, the datepicker is already outside the updatepanel's content-scope, so it shouldn't be affected. I tried your solution, but it still shows null in firefox :(
cwap
A: 

You could try using datepicker to set an altField and check that for changes to do a postback.

DaMayan
Yes, seems like that's the only way to go :(
cwap
A: 

I have tried your code for reproducing the error and I can reproduce the error. And I've come to the conclusion that there is some problem with jQuery/UpdatePanels.

If I replace the button with a LinkButton, you can see that the link directly calls __doPostBack(...), the exact same function with the exact same arguments that you are calling. And clicking the link button directly works, but clicking a date doesn't.

So that means that something happens inside jQuery that changes some context. But I have no idea what that would be.

Pete
A: 

I found another script a while back that I've been using for a while now and have never had any problems with.

 <script type="text/javascript">
        /*
        [__doPostBackAsync]
        Will send an async postback to the backend
        */
        function __doPostBackAsync(eventName, eventArgs) {
            var prm = Sys.WebForms.PageRequestManager.getInstance();

            //check first if the request queues have this item
            if (!Array.contains(prm._asyncPostBackControlIDs, eventName)) {
                prm._asyncPostBackControlIDs.push(eventName);
            }
            if (!Array.contains(prm._asyncPostBackControlClientIDs, eventName)) {
                prm._asyncPostBackControlClientIDs.push(eventName);
            }
            __doPostBack(eventName, eventArgs);
        }
    </script>

You can still manually set a callback function exactly like you do in your above script.

You may also have to manually add a scriptresource handler (Can't remember which one) but I specifically remember having problems calling async and non-async post backs. Hope that helps.

Doesn't work. Thanks for trying though :)
cwap
+1  A: 

I have fixed various async issues with jQuery/Updatepanels using the ScriptMode="Release" argument on the ScriptManager.

  <asp:ScriptManager ID="scriptManager" runat="server" ScriptMode="Release">
  </asp:ScriptManager>
Tim Santeford
Didn't work, sadly :(
cwap
A: 

Looks like a race condition, try UpdateMode="Conditional" and ChildrenAsTriggers="false", reference.

slf
Didnt help, though I do agree it looks like a race-condition :)
cwap
it was a shot in the dark :(
slf
A: 

However, if I step through the code in FireBug slow enough it works

Oh? Why not give it a second to work it out?

setTimeout(function(){alert(sender._postBackSettings.panelID);},1000);
lod3n
Hehe.. Yeah, tried that, but it's not really the kind of solution I'm looking for xD
cwap
+3  A: 

I think you my find this article from MSDN interesting: http://msdn.microsoft.com/en-us/magazine/cc163413.aspx

The article says that "Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(" also attaches to Updatepanel complete events.

It also fires each time an asynchronous callback launched on behalf of an UpdatePanel control completes and the content inside that UpdatePanel is updated.

then in your handler you can loop through the updatedpanels with

var panels = args.get_panelsUpdated();
for (i=0; i < panels.length; i++) { alert(panels[i]); }

I would stray away from using the _ postBackSettings because of the " _ " denotes privacy and may be discontinued in future versions of asp.net ajax.

Tim Santeford
This works. Simple and great :) Thanks a lot!
cwap
A: 

I liked Tim's answer, surrounding the idea that you should be using args.get_PanelsUpdated() in the pageLoaded event. It seems like the appropriate means for doing what you're attempting to do. Try it out -- if it doesn't work, then I've got another idea up my sleeve (though its kind of dirty).

Skone
A: 

I thought this was interesting, if you add an alert after the doPostBack then FF doesn't return null.

    function doAspNetPostback() {

        __doPostBack('<%= hiddenOnSelectionChangedButton.ClientID %>', '');
        alert('<%= hiddenOnSelectionChangedButton.ClientID %>');
    }
Noah