views:

505

answers:

5

Ok: I've got an UpdatePanel on an aspx page that contains a single Placeholder.

Inside this placeholder I'm appending one of a selection of usercontrols depending on certain external conditions (this is a configuration page).

In each of these usercontrols there is a bindUcEvents() javascript function that binds the various jQuery and javascript events to buttons and validators inside the usercontrol.

The issue I'm having is that the usercontrol's javascript is not being recognised. Normally, javascript inside an updatepanel is executed when the updatepanel posts back, however none of this code can be found by the page (I've tried running the function manually via firebug's console, but it tells me it cannot find the function).

Does anyone have any suggestions?

Cheers, Ed.

EDIT:

cut down (but functional) example:

Markup:

<script src="/js/jquery-1.3.2.min.js"></script>
  <form id="form1" runat="server">
    <div>

    <asp:ScriptManager ID="Script" runat="server" />

    <asp:Button ID="Postback" runat="server" Text="Populate" OnClick="PopulatePlaceholder" />

    <asp:UpdatePanel ID="UpdateMe" runat="server">
    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="Postback" EventName="Click" />
    </Triggers>
        <ContentTemplate>
            <asp:Literal ID="Code" runat="server" />
            <asp:PlaceHolder ID="PlaceMe" runat="server" />
        </ContentTemplate>
    </asp:UpdatePanel>
    </div>
 </form>

C#:

protected void PopulatePlaceholder(object sender, EventArgs e)
{
    Button button = new Button();
    button.ID = "Push";
    button.Text = "push";
    button.OnClientClick = "javascript:return false;";
    Code.Text = "<script type=\"text/javascript\"> function bindEvents() { $('#" + button.ClientID + "').click(function() { alert('hello'); }); }  bindEvents(); </script>";
    PlaceMe.Controls.Add(button);
}

You'll see that the button does not poput the alert message, even though the code is present on the page.

EDIT2:

Ok, just to make it clear, the production code is significantly more complex than just a single function bound onto a literal, and contains a large number of

<%= Control.ClientID %>

bits of code that'll be very difficult to factor off into non-specific functions, and pointless as they're each only used in one place (we're talking very specific validation and the odd popout trigger + some logic).

+2  A: 

Add the scriptmanager (Ajax Extensions) to your page and add your scriptreferences inside that manager. The manager will load each script tags on ajax-based-pageload (and not just the initial load) - move all needed javascript code inside the updatepanel and you're pretty much done.

    <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Scripts>
            <asp:ScriptReference Path="../lib/jquery-1.2.6.pack.js" />

Didn't read the question good enough (and without the sample). See my new response.

riffnl
You need a scriptmanager to use updatepanels, and the code is all internal to the usercontrol (embedded on the page) as it uses control-specific IDs, so this is not really goign to help.
Ed Woodcock
+2  A: 

To re-run some js after an update-panel has fired I've always used this

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(
    function(sender, args) {
        //update whatever here
    });

Also if you have any references to elements within the update panel you can refresh those variables within that function as well, otherwise you'll have old refs that wont work.

Justin
Yeah, that unfortunately only works if the code is present on the original page in the first place. As I'm using dynamic controls and loading it via an UpdatePanel (i.e. via javascript) the new javascript functions are not recognised as functions!
Ed Woodcock
Hmmm... I know evals are not the best thing to fall back on, but what about placing this call within the UpdatePanel callback listed above?...//update whatever hereeval(document.getElementById('Code').innerHtml);...
Justin
@Justin might work, but that's pretty much the last thing I'd like to resort to !
Ed Woodcock
A: 

Somehow it seems your Code.Text gets lost in translation somewhere - don't know specifics, perhaps it's by design; anyway the next does work:

    protected void PopulatePlaceholder(object sender, EventArgs e)
    {
        Button button = new Button();
        button.ID = "Push";
        button.Text = "push";
        button.OnClientClick = "javascript:return false;";
//        Code.Text = 
        PlaceMe.Controls.Add(button);
        PlaceMe.Controls.Add(new LiteralControl("<script type=\"text/javascript\"> function bindEvents() { $('#" + button.ClientID + "').click(function() { alert('hello'); }); }  bindEvents(); </script>"));
    }

Since you're already building the javascript code from codebehind you might as well add the control dynamically too.

riffnl
This small example is only indicative of my process, the real code is around 200 lines of javascript that does some very complex stuff. I need to maintain it, so there's not really an option of keeping the javascript in the code behind!
Ed Woodcock
In your sample you already building the Code.Text from codebehind; you're now stating you don't want to execute the javascript from codebehind?? I assumed that you'd replace the alert with the call to a new function, passing the ID as parameter or something like that, anyway - just trying to help
riffnl
I understand that, however I did say that there was more to it in my original post.
Ed Woodcock
One last option; perhaps enabling the viewstate of the code literal control would do something on callback?
riffnl
+2  A: 

Get rid of that Literal control and use the ScriptManager to register your script. What you are doing doesn't work for the same reason

window.document.getElementById('someId').innerHtml = "<script type=\"text/javascript\">alert('hey');</script>";

doesn't work. Try this:

protected void PopulatePlaceholder(object sender, EventArgs e)
{
    Button button = new Button();
    button.ID = "Push";

    button.Text = "push";
    button.OnClientClick = "return false;";


    string script = "function bindEvents() { $('#" + button.ClientID + "').click(function() { alert('hello'); }); }  bindEvents();";

  ScriptManager.RegisterClientScriptBlock(this.Page, typeof(SomeClass), Guid.NewGuid().ToString(), script, true);


  PlaceMe.Controls.Add(button);
} 

The parameters to RegisterClientScriptBlock may need to change, but you get the idea.

internet man
maintainability drops to zero though if I do this: we're talking about 200 lines of JS that does a lot of stuff, most of which using control clientIDs to do it. that kind of code is very difficult to maintain when it's in the backend...
Ed Woodcock
You need to refactor your scripts then. This is the correct way of doing this. Sorry.
internet man
i.e., you will have to rewrite you scripts so that they can be placed in js files. Then you will have to call ScriptManager.RegisterClientScriptBlock and ScriptManager.RegisterStartupScript as needed. There's no way you are going to get what you are trying to do to work without calls to the ScriptManager.Scripts in ascx/aspx isn't good practice anyway and doesn't help performance.
internet man
@internet man : erm. sorry to pee on your bonfire but when you're referencing control names in order to manipulate the UI it's MUCH faster to simply write the name of the control in using the <%= %> syntax than it is to have to reference them via a variable passed into a function. Yeah, in theory it's faster to import a script via a JS and use function calls than it is to simply append the script to the page, but this results is a bunch of unneeded imports when you don't know which scripts to import at the page load.
Ed Woodcock
Oh, and this is not the 'correct' way of doing this: you can just use a custom control to render the script into the ScriptManager as a string by overriding the Render() method. I found out how to do that earlier, I was hoping someone would know the answer on here and answer it before the bounty ran out. Shame.
Ed Woodcock
Christ, sorry for posting an answer. Post the answer then.
internet man
Not bothered about your answer, it was valid (if a little naive). Bothered about the rightousness of your comments and the fact that you got about 200 rep for a waste of my time.
Ed Woodcock
So sorry to waste your time. Can you post your answer?
internet man
A: 

Maybe you are looking for this: http://api.jquery.com/live/

Nearly .bind() compatible (exceptions listed in the link above) - but also binds to elements added/changed later on.

Or maybe give also .delegate() a try - can not directly replace .bind() calls, but is in most cases much faster.

DanielAt