Wednesday, February 9, 2011

Is it possible to interpolate my angle bracket, percent, equals <%= %> syntax in external javascript files?

Often times when mixing jquery with asp.net I need to use asp .net angle bracket percent, <% %>, syntax within a jquery selector.

If I would like to seperate the javascript from markup into different files is there still a way to evaluate my javascript file so the angle bracket percents are interpolated before reaching the client browser?

  • No, you'll need to refactor your JavaScript to accept that information as parameters.

    So, instead of

    jQuery('#<%=MainPanel.ClientId%>').hide('slow');
    

    do something like this:

    function hidePanel(panelId) {
            jQuery('#' + panelId).hide('slow');
    }
    

    which you can call from your page with

    hidePanel('<%=MainPanel.ClientId%>');
    
    eniac : that still necessitates mixing javascript and markup. Which I'm trying to avoid. I use jQuery's $(document).onReady event to hook up the rest of my handlers, thus eliminating the need to mix markup and script.
    From bdukes
  • If you want to evaluate <% code blocks %> as ASP.NET code in a JavaScript file, you can just put the JavaScript in an ASPX file and reference it from a SCRIPT element.

    script.js.aspx:

    function hideElements()
     { <% foreach(var elementId in Request.QueryString["hide"].Split(',') { %>
       jQuery('#' + <%= elementId %>).hide('slow');
       <% } %>
     }
    

    page.aspx:

    <script src="script.js.aspx?hide=<%= GetElementsIds() %>"
            type='text/javascript'></script>
    

    page.aspx.cs:

    public string GetElementIds() 
     {
       return string.Join(",", new []{control1.ClientID, control2.ClientID});
     }
    
    mattlant : nice little tip you got there!
    Sugendran : you might want to use a .ashx instead - it's lighter than the .aspx
    Sugendran : Oh, and I'd also set the content type to plain/text
    AnthonyWJones : An ashx doesn't parse the <% %> and why plain/text (which should have been text/plain)? use text/javascript.
    AnthonyWJones : Note that it is not a good idea to allow content that has been supplied by the client to be injected directly into the Javascript. You should ensure that the value is validated.
    eniac : But does this method lend itself to interpolating the ClientIDs of my asp .net server controls within jQuery selectors if the controls exist in two seperate .aspx files?
    Mark Cidade : I updated my solution. You only need one script.js.aspx and the example for page.aspx can be identical for every ASPX file provided that you supply a custom GetElementIds() implementation.
  • You could also handle .js files as .aspx files; this way you won't lose intellisense and code formatting while you're editing them. Just add this to web.config:

    <system.webServer>
        <handlers>
           <add name="Dynamic JS" path="*.js" verb="*" type="System.Web.UI.PageHandlerFactory" resourceType="Unspecified"/>
    
    Mark Cidade : The problem with this is that all .js files will create a Page object when all you need is a static file for some.
    eniac : Marxidad is not the .aspx file already wired to the PageHandlerFactory also creating a Page object?
    Mark Cidade : Yes but if you only use it for the javascript files that need it then it's no so bad as doing it for every single .js file.
  • I made an attempt to separate javascript on the search grid user control from the html in the .ascx file. In the first iteration I used the jQuery(document).onReady function to attach my intialization.

    The problem with this is that <%= %> tags used within jQuery selectors were not interpolated correctly and the controls the javascript acted on were not found with the jQuery selectors.

    Next, I attempted to create a json object in the Page initialization and write that out using the asp.net method Page.ClientScript.RegisterClientScriptBlock. This worked ok, but with drawbacks: hard wired the json object's name and keys in the asp.net file and javascript file. This is disadvantageous because now there exists "two points of truth" to maintain and further more there is the potential for name collision in the final rendered page.

    The most elegant solution within the asp .net and utilizing jQuery is to create an ajax script behavior in javascript. Then within the asp codebehind register the script behavior's properties in the GetScriptDescriptors() method of the IScriptControl interface, adding the server side control's ClientID as a property to the script descriptor.

    // Ajax Javacsript Code below:
    
    Type.registerNamespace('SearchGrid');
    
    // Define the behavior properties
    //
    ButtonBehavior = function() {
        ButtonBehavior.initializeBase(this);
        this._lnkSearchID = null;
    }
    
    // Create the prototype for the behavior
    //
    //
    SearchGrid.ButtonBehavior.prototype = {
    initialize: function() {
        SearchGrid.ButtonBehavior.callBaseMethod(this, 'initialize');
        jQuery('#' + this._lnkSearchID).click(function() { alert('We clicked!'); });
    },
    
    dispose: function() {
        SearchGrid.ButtonBehavior.callBaseMethod(this, 'dispose');
        jQuery('#' + this._lnkSearchID).unbind();
        }
    }
    
    // Register the class as a type that inherits from Sys.Component.
    SearchGrid.ButtonBehavior.registerClass('SearchGrid.ButtonBehavior', Sys.Component);
    
    
    if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
    


    Asp .Net code below:

        public partial class SearchGrid : System.Web.UI.UserControl, IScriptControl
        {        
            // Initialization    
            protected override void OnPreRender(EventArgs e)
            {
                if (!this.DesignMode)
                {
                    // Test for ScriptManager and register if it exists
                    ScriptManager sm = ScriptManager.GetCurrent(Page);    
                    if (sm == null)
                        throw new ApplicationException("A ScriptManager control must exist on the current page.");    
                    sm.RegisterScriptControl(this);
                }    
                base.OnPreRender(e);
            }
            protected override void Render(HtmlTextWriter writer)
            {
                if (!this.DesignMode)
                    sm.RegisterScriptDescriptors(this);
    
                base.Render(writer);
            }
    
            // IScriptControl Members
            public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
            {
                ScriptBehaviorDescriptor desc = new ScriptBehaviorDescriptor("SearchGrid.ButtonBehavior", this.ClientID);         
                desc.AddProperty("lnkSearchID", this.lnkSearch.ClientID);
                yield return desc;
            }
    
            public IEnumerable<ScriptReference> GetScriptReferences()
            {
                ScriptReference reference = new ScriptReference();
                reference.Path = ResolveClientUrl("SearchGrid.ButtonBehavior.js");
                return new ScriptReference[] { reference };
            }       
        }
    


    The advantage here is that you may create stand alone reusable controls with javascript behavior contained in its own separate file (or as a web resource) while passing state and context, which might otherwise would be interpolated with angle,percent, equals syntax, necessary for jQuery to do its work.

    From eniac

0 comments:

Post a Comment