Вы находитесь на странице: 1из 8

Welcome to Drawloop Technologies, Inc.

Less spam, plenty of space and access from anywhere.


Sign in to your account at 
Drawloop Technologies, Welcome to your email for Drawloop Technologies, Inc., powered by Google, 
Inc. where email is more intuitive, efficient and useful. 
Username: 
 Keep unwanted messages out of your inbox with Google's powerful 
@drawloop.com  spam blocking technology 
Password:   Keep any message you might need down the road, and then find it fast 
Remember me on this  with Google search 
gfedc
computer. 
 Send mail, read new messages and search your archives instantly from 
Sign in your phone 

I cannot access my account 
New! One-stop information sharing with Google Sites
Building a site is as simple as editing a document, and you don't need 
anyone's help to get started.
Check out these example sites: Company intranet, Team project, Employee 
profile, Classroom

©2008 Google   Privacy Policy - Terms of Service Powered by 

https://www.google.com/a/drawloop.com/ServiceLogin?service=mail&passive=true&rm=false&continue=http%3A%2F%2...
Life, Surf, Code and everything in between White Papers  |  Free Tools  |  Products  |  Message Board  |  News  | 

Navigate

Web Log Home

Site Home

Subscribe to Feed

Embedding ASP.NET Server Variables in Client JavaScript
Contact
February 12, 2008 @ 2:23 am-  from Maui, Hawaii 18 comments
Search

Posts - 976

Comments - 10118 Jon Galloway had an interesting post over the weekend regarding Getting JavaScript and ASP.NET talking (outside of AJAX). 
Ajax
Easily develop RIAs with AJAX using our open  The post discusses a problem Jon was having in getting server variables to be accessible in client-side JavaScript code and 
source platform.  his solution as part of a control using the ASP.NET AJAX IScriptControl interface. 
SkywayPerspectives.org

In the comments I remarked that this process seems awfully complex to do an essentially simple thing: Embed script code 
Open Source Ajax Toolkit
Download Open Source Ajax Toolkit For Fast,  into a page. However,the problem addressed in the post is a real one - but one that I think can be solved with a more 
Easy Ajax App Development  generic and maybe possibly easier solution. 
www.TIBCO.com

So to reiterate, the problem at hand is: How do you effectively get server variables published into the client page and
JavaScript Grid Component 
Grouping and Column resizing Templates and  accessible from JavaScript? The reality is: It's not easy! 
Row Summaries 
www.extjs.com
The easiest solution might seem to be using <%= %> expressions to embed values into script, but it's really not quite that 
since 1997 simple because .NET values - especially strings and dates - need to be properly formatted in order to work correctly as
JavaScript values. So writing out a string as:

var Company = '<%= Customer.Company %>';

is actually not good enough. For example if the string contains a single quote the code will fail. You can use double quotes but then a double quote will fail instead. 
Because the string is embedded into script as a literal the string also needs to be properly escaped. So quotes, slashes, and escape characters have to be marked 
Escape the limits of shared 
hosting with your own  up as well. Using a plain <%= %> tag is clearly not going to cut it. Some time ago I posted a JavaScript string encoding function that deals with strings, but this 
Windows VPS Server! Click  still leaves you with the ugly solution of script tags and global variables you have to declare inside script code.
to Learn More!

Ads Via The Lounge There's also a problem if you need access to the variables in .js files - you can't embed script tags into a .js file so getting a dynamic value into a static file is 
problematic (although my workaround below would also work with <%= %> tags in the main page).
My Links
Anyway, after reading Jon's post I thought that it would be nice to have a generic tool to 'publish' server variables to the client. So I spent a couple hours last night 
Photo Album
to create a very easy to use class that does the following:
My Noise

Message Board
 Adds key/value pairs to a statebag that gets rendered automatically 
Now Reading  Dynamically adds any object/control property that gets rendered automatically 
 Keys are rendered as properties of an JavaScript object with the static or dynamic values applied 

To use the class inside of an ASP.NET Page this code to use this class looks like this:

protected void Page_Load(object sender, EventArgs e)
{
    wwScriptVariables scriptVars = new wwScriptVariables();
 
    // *** Add any values static or dynamic
Learning WCF: A Hands-     scriptVars.Add("name", "West Wind Technologies");
on Guide     scriptVars.Add("entered", DateTime.Now);
by Michele Bustamante     scriptVars.Add("counter",12.22M);
 
    // *** A cleaner way to get ClientIDs into client code?
    scriptVars.Add("txtNameId", txtName.ClientID);
    scriptVars.Add("btnSubmitId", btnSubmit.ClientID);
 
    // *** Add a control's value
    scriptVars.AddDynamicValue("txtName", this.txtName, "Text");
 
    // *** Done
}

This code adds several values to the ScriptVariables object which hold on to these values and then generates a client side object with a name specified in the
LINQ in Action
by Fabrice Marguerie, Steve constructor (or the default which is 'serverVars'). The AddDynamicValue method adds a property that is essentially mapped to a property of a control or object. You 
Eichert, Jim Wooley can basically assign this value anywhere and still ensure that final rendered value will be embedded into the script object. This is useful so that you can keep all the 
assignment code in one place putting both static and dynamic values into a single method rather than scattering calls into various page event methods (although 
you can still do that if you choose).

The class then renders a JavaScript object into the page with each of the key values as property names. For the definition above you'd end up with a Client Script
Block in the page:

<script type="text/javascript">
//<![CDATA[
var serverVars = {
    "name": "West Wind Technologies",
    "entered": new Date(1202809550345),
    "counter": 12.22,
    "txtNameId": "txtName",
    "btnSubmitId": "btnSubmit",
    "txtName": ""
}
//]]>
</script>

The script is added a ClientScriptBlock which is rendered at the top of that page, which also means it's visible embedded script files (.js files). Now from anywhere 
you can access these variables via the static object instance:

var name = serverVars.name;
var entered = serverVars.entered;
var counter = serverVars.counter;

http://www.west-wind.com/WebLog/posts/252178.aspx
And using a control Id that might from within a master page or other naming container in script:

var txtName = document.getElementById(serverVars.txtNameId);
var name = jQuery("#" + serverVars.txtNameId).css("border","solid 1px red");

which surely beats using <%= %> script tags inside of the script code.

Note that you can specify the name of the defined JavaScript variable by changing ClientObjectName or passing in the name in the constructor, so you can have 
multiple separate server object variables embedded into the page. This makes this work for control developers as well, as long as you can come up with a unique 
name for the control.

This approach can also be useful for localization. Because the code is generated on the server, you can assign values with HttpContext.GetLocalResourceObject() or
HttpContext.GetGlobalResourceObject() to assign values or - if you're binding a control value - get the localized control value at render time.

Values that are assigned are encoded using JSON serialization to ensure they are 'literal' values that evaluate. One very cool side effect of this is that you can 
actually embed fairly complex objects in addition to simple types. For example, you can directly serialize an object, a DataSet/DataTable/DataRow, an array, list or 
enumerable type.

I'm using the JSONSerializer from the West Wind Ajax Toolkit in the control (because it supports a couple of date formatting options) but you can also use the 
System.Web.Extensions.JavaScriptSerializer along the same lines.

Here's my first cut at this class:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web;
using System.Reflection;
 
namespace Westwind.Web.Controls.Controls
{
    /// <summary>
    /// Provides an easy way for server code to publish strings into client script code.
    /// This object basically provides a mechanism for adding key value pairs and embedding
    /// those values into an object that is hosted on the client.        
    /// </summary>
    public class wwScriptVariables
    {
 
        /// <summary>
        /// Internally holds all script variables declared
        /// </summary>
        Dictionary<string, object> ScriptVariables = new Dictionary<string,object>();
 
 
        /// <summary>
        /// Internally tracked reference to the Page object
        /// </summary>
        Page Page = null;
 
 
        /// <summary>
        /// The name of the object generated in client script code
        /// </summary>
        public string ClientObjectName
        {
            get { return _ClientObjectName; }
            set { _ClientObjectName = value; }
Log In         }
        private string _ClientObjectName = "serverVars";
User Name:
 
        /// <summary>
Password:         /// Determines whether the output object script is rendered
        /// automatically as part of Page PreRenderComplete. If false
        /// you can manually call the RenderClientScript() method to
gfedc Rembember me         /// retrieve the script and embed it yourself.
Log In         /// </summary>        
        public bool AutoRenderClientScript
        {
            get { return _AutoRenderClientScript; }
            set { _AutoRenderClientScript = value; }
        }
        private bool _AutoRenderClientScript = true;
 
 
        /// <summary>
        /// Full constructor that receives an instance of any control object
        /// and the client name of the generated script object that contains
        /// the specified properties.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="clientObjectName"></param>
        public wwScriptVariables(Control control, string clientObjectName) 
        {
            if (control == null)
                // Note: this will fail if called from Page Contstructor
                //       ie. wwScriptVariables scripVars = new wwScriptVariables();
                this.Page = HttpContext.Current.Handler as Page;
            else
                this.Page = control.Page;
 
            if (this.Page == null)
                throw new ArgumentException("Could not retrieve a Page instance in wwScriptVariables.\r\n Either provide a Control or Page refere
 
            if (!string.IsNullOrEmpty(clientObjectName))
                this.ClientObjectName = clientObjectName;
 
            // *** Force RenderClientScript to be called before the page renders
            this.Page.PreRenderComplete +=new EventHandler(Page_PreRenderComplete);
        }
        /// <summary>
        /// This constructor only takes an instance of a Control. The name of the
        /// client object will be serverVars.
        /// </summary>
        /// <param name="control"></param>

http://www.west-wind.com/WebLog/posts/252178.aspx
        public wwScriptVariables(Control control) : this(control,"serverVars")
        {            
        }
        /// <summary>
        /// This constructor can only be called AFTER a page instance has been created.
        /// This means OnInit() or later, but not in the constructor of the page.
        ///
        /// The name of the client object will be serverVars.
        /// </summary>
        public wwScriptVariables() : this(null, "serverVars")
        { 
        }            
 
        private void Page_PreRenderComplete(object sender, EventArgs e)
        {
            this.RenderClientScript();
        }
 
        public void Add(string variableName, object value)
        {
            this.ScriptVariables.Add(variableName, value);
        }
 
        /// <summary>
        /// Adds the dynamic value of a control or any object's property
        /// that is picked up just before rendering. 
        ///
        /// This allows you to specify a given control or object's value to 
        /// added to the client object with the specified property value 
        /// set on the JavaScript object and have that value be picked
        /// up just before rendering. This is useful so you can do all
        /// client object declarations up front in one place regardless
        /// of where the values are actually set.
        ///
        /// Dynamic values are retrieved with Reflection so this method
        /// is necessarily slower than direct assignment.
        /// </summary>
        /// <param name="variableName"></param>
        /// <param name="control"></param>
        /// <param name="property"></param>
        public void AddDynamicValue(string variableName, object control, string property)
        {
            // *** Special key syntax: .varName.Property syntax to be picked up by parser
            this.ScriptVariables.Add("." + variableName + "." + property, control);            
        }
 
        /// <summary>
        /// Explicitly forces the client script to be rendered into the page.
        /// This code is called automatically by the configured event handler that
        /// is hooked to Page_PreRenderComplete
        /// </summary>
        private void RenderClientScript()
        {            
            if (!this.AutoRenderClientScript || this.ScriptVariables.Count == 0)
                return;
 
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("var " + this.ClientObjectName + " = {");
 
            // *** We'll serialize single values into the client
            JSONSerializer ser = new JSONSerializer();
            ser.SerializeDateAsString = false;  // use new Date() output
 
            foreach(KeyValuePair<string,object> entry in this.ScriptVariables)
            {
                if (entry.Key.StartsWith("."))
                {
                    // *** It's a dynamic key
                    string[] tokens = entry.Key.Split(new char[1] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                    string varName = tokens[0];
                    string property = tokens[1];
 
 
                    object propertyValue = null;
                    if (entry.Value != null)
                        propertyValue = entry.Value.GetType().
                                    GetProperty(property, BindingFlags.Instance | BindingFlags.Public).
                                    GetValue(entry.Value, null);  
 
                    sb.AppendLine("\t\"" + varName + "\": " + ser.Serialize(propertyValue) + ",");                
                }
                else
                    sb.AppendLine("\t\"" + entry.Key + "\": " + ser.Serialize(entry.Value) + ",");
            }
 
            // *** Strip off last comma plus CRLF
            if (sb.Length > 0)
                sb.Length -= 3;                
 
            sb.AppendLine("\r\n}");
 
            if (this.Page == null)
                this.Page = HttpContext.Current.Handler as Page;
 
            // *** Use ClientScriptProxy from West Wind Ajax Toolkit to be MS Ajax compatible - otherwise use ClientScript
            ClientScriptProxy.Current.RegisterClientScriptBlock(this.Page, typeof(ControlResources), "_ClientScriptStrings", sb.ToString(), true)
 
            //this.Page.ClientScript.RegisterClientScriptBlock(typeof(ControlResources), "_ClientScriptStrings", sb.ToString(), true);
        }
    }
}

The code has a couple of dependencies that are part of the West Wind Ajax Toolkit. ClientScriptProxy is used to manage client script as a front end to 
ScriptManager or the Page.ClientScript object depending on availability of the ScriptManager. The JSONSerializer is used here rather than JavaScriptSerializer from 
System.Web.Extensions because of the obvious System.Web.Extensions dependency as well as a problem with Date formatting by JavaScriptSerializer which uses 
encode date strings (ie."\/Date(1198874600025)\/") which doesn't actually turn into a date value. JSONSerializer can optionally output the required new Date() 
sytnax to produce a live date (this may be possible with JavaScriptSerializer but offhand I don't know how).

You can pick up the source code for this class along with the JSONSerializer dependency by downloading the West Wind Ajax Toolkit.

http://www.west-wind.com/WebLog/posts/252178.aspx
What now?

There might be a few improvements to this concept. Maybe it'd be useful to allow creating pre and post rendering script code so you can do things like scope the 
object and add namespaces potentially.

Another thing that keeps flicking around in my head is that damn problem with ClientIDs. It might be nice to have an optional method that can add properties for 
each control on the page automatically. Something like:

.AddClientIds(control)

which would then add each control and its clientID with properties generated by ID name. I'm not quite sure just how far this could go though and whether this 
would just be too expensive. Maybe you could specify a container and it would only retrieve control Ids at that container level rather than digging into the container
hierarchy. So if you specified Page it would only pick up top level controls.

I don't know - need to mull that one over, but the ClientID mess has been one of the uglier things to do with AJAX code and this class at least provides an option 
for a cleaner way to address ControlIds even if it requires an extra line of code on the server to expose it. <shrug>

[Update: I've posted an update with a few of the enhancements above as well as some from the comments below]

Posted in AJAX  ASP.NET  JavaScript  

Creating Reports is a Pain in the Neck
Tired of bloated reporting frameworks that frustrate and limit your creativity? Ready for a 
tool that just works without hassles or aggravation? Grab your FREE trial copy of 
XtraReports for Windows and ASP.NET now.

Feedback for this Weblog Entry
 

Embedding ASP.NET Server Variables in Client JavaScript
by DotNetKicks.com February 12, 2008 @ 2:25 am

You've been kicked (a good thing) - Trackback from DotNetKicks.com 

re: Embedding ASP.NET Server Variables in Client JavaScript
by Fredrik February 12, 2008 @ 3:08 am

I wrote a similar solution a couple of weeks ago, which keeps a server-side dictionary synchronized with a client-side dictionary: http://www.iridescence.no/Posts/Sharing-Variables-Between-JavaScript-
and-C.aspx :) 

re: Embedding ASP.NET Server Variables in Client JavaScript
by Jon Galloway February 12, 2008 @ 8:29 am

Looks very cool, and I like what you're doing with "those damn ClientID's"...

Maybe I'm missing it, but how does this work for a control? Your examples work for a page, but what if I'm building a control (user or server) which might be instanced several times on a page? In that 
case, wouldn't you just be writing copies of the script block with the same name but different values?

var serverVars = {
    "name": "West Wind Technologies",
    "entered": new Date(1202809550345),
    "counter": 12.22,
    "txtNameId": "txtName",
    "btnSubmitId": "btnSubmit",
    "txtName": ""
}
var serverVars = {
    "name": "Northwind Database Technologies",
    "entered": new Date(1102809550345),
    "counter": 55.55,
    "txtNameId": "txtName",
    "btnSubmitId": "btnSubmit",
    "txtName": "Freddy"
}

Also, while I really like the ClientID's part (especially .AddClientIds(control)), I think you'd get into nesting issues there, too. What if I've got three user controls, each with a txtName, and I 
call .AddClientIds() with all three controls? You'd have the client id's as returned by controls (control1.txtName.ClientID, control.txtName.ClientID, control3.txtName.ClientID), but they'd all have the 
same name in serverVars so you'd have no way to look them up on the client side.

Apologize if I'm missing something, I just don't see how you're handling multi-instanced controls in the code or examples. 

re: Embedding ASP.NET Server Variables in Client JavaScript
by jmorris  February 12, 2008 @ 12:59 pm

Jon, I think this overload would handle that situation:

public wwScriptVariables(Control control, string clientObjectName)

You would just prefix the clientObjectName with the naming container name (or whatever you wish). 

re: Embedding ASP.NET Server Variables in Client JavaScript
by Rick Strahl February 12, 2008 @ 1:21 pm

________________________________________
Jon, the name of the object var is configurable so each control can have its own name. You can pass it in the constructor or assign the ClientObjectName property. So if you have a control you can 
decide if you need one object per control (ie. you have a fixed name like say __MyControl) or one per instance (in which case it'll probably have to be based off ID).

As I mentioned you could probably also apply namespacing to this - that's why I mentioned the pre and post script for so that a namespace could be defined. If you're using MS Ajax you could add a
RegisterNamespace directive and then use a namespace scoped name for the ClientObjectName. I haven't added that yet though.

As to the client ID - yes there are issues there that's why I haven't gone down this path just yet. This is why I think you wouldn't want to drill into containers automatically, but you'd have to pick a 
container and only do each of its children. You certainly wouldn't want to drill into a control like a grid or any other repeating control - that would only result in garbage.

But each container level alone will give you unique Ids. If dupes occur they would overwrite each other - a non-fatal problem though possibly confusing. The user would have to resolve this most by 
adding explicit property names for those name conflict properties. 

I don't think there's a perfect solution to the ClientID problem, but one that comes close would still be pretty useful I think.

http://www.west-wind.com/WebLog/posts/252178.aspx
re: Embedding ASP.NET Server Variables in Client JavaScript
by Rick Strahl February 12, 2008 @ 1:32 pm

Frederik - ah that's a cool approach too. I really like the fact that you can also post values back to the server with your approach, which is slick. For the outbound end though I like the object approach 
better - it's just easier to use an instance than having to write to a dictionary. Even writing would also work this way since expando properties would automatically created for any updates.

Great idea! 

Setzen von ASP.NET Server Variables in JavaScript 
by .NET - Software & DotNetNuke (DNN) Blog February 12, 2008 @ 2:56 pm

re: Embedding ASP.NET Server Variables in Client JavaScript
by Steve  February 12, 2008 @ 4:10 pm

This is why I like JQuery selectors, I've been selecting by class instead of by ID

re: Embedding ASP.NET Server Variables in Client JavaScript
by John McGuinness February 13, 2008 @ 9:00 am

This is a very innovative approach to the server/client marriage. When ever I've used '<%= txtName.ClientID %>' with in a javascript block I always felt dirty afterward wondering to myself "there 
must be a better way". This has opened my eyes as to what a better way can be.

re: Embedding ASP.NET Server Variables in Client JavaScript
by Tim Giorgi February 13, 2008 @ 11:08 am

Well done Rick,

You've tackled a problem I've been wanting to go after for some time. Nice and elegant solution. I especially like the object encapsulation - keeps it very clean without a ton of naming references all 
over the place. 

re: Embedding ASP.NET Server Variables in Client JavaScript
by Grant Palin February 14, 2008 @ 1:46 pm

I like. I have been wrestling with much this same issue in a ASP.NET web app I'm working on. To send server side variables to client-side JavaScript, I would use a stringbuilder to manually build up the 
necessary JS, and write it out tot he page. Very messy.

But this generic solution is a godsend - I have had similar ideas but hadn't gotten around to implementing them. The ClientID aspect (giving that to the JS) is a good idea, and one I had not considered.

My project's in VB.NET, so I'll have to write code from scratch...Hopefully I can get a start on that this weekend.

Thanks for the inspiration! 

re: Embedding ASP.NET Server Variables in Client JavaScript
by Rick Strahl February 14, 2008 @ 2:16 pm

@Steve - yes, definitely. Adding a CSS class with the same name as the control and then using CSS selectors is a good way to force client selection. 

For those not familiar with this you can do the following (assuming your page has control naming issues due to naming containers such as MasterPages):

<asp:TextBox runat="server" id="txtName" CssClass="inputControl txtName" />

and you can then search with jQuery:

jQuery(".txtName").val("New Value");

Note that the CSS class doesn't have to actually exist. 

本周ASP.NET英文技术文章推荐[02/03 - 02/16]:MVC、Visual Studio 2008、安全性、性能、LINQ to JavaScript、jQuery
by Dflying Chen February 15, 2008 @ 5:57 am

摘要继续坚持,继续推荐。本期共有 9篇文章:最新的ASP.NETMVC框架开发计划VisualStudio2008Web 开发相关的Hotfix发布ASP.NET安全性教程系列ASP...

re: Embedding ASP.NET Server Variables in Client JavaScript
by Atom February 29, 2008 @ 1:04 pm

The problem with that is that looking up classes by javascript is a LOT slower than by ID, as the Browser maintains a hashtable of IDs on the page. 

re: Embedding ASP.NET Server Variables in Client JavaScript
by Andy Bantly  March 10, 2008 @ 12:57 pm

I am very new to ASP.NET. A few years ago I used to be a veteran of javascript and HTML but my development skills in that area have been dormant. Recently I took up ASP.NET to help my wife build a 
website. I was faced with this problem, of communicating server information to the client, and I honestly didn't even know at first that I could use <%=...> nomenclature. Your post actually solved my 
problem in that regard. Now, as I am a C++ programmer, I can appreciate your class for the merits of being wrapped up and tidy, as a problem solver my question for you is this: "Seeing that your 
problem was scoped by data that could have quotes or other beasties in it, I wonder why you didn't make more use Base64 encoding and decoding to pass data?

re: Embedding ASP.NET Server Variables in Client JavaScript
by Rick Strahl March 10, 2008 @ 1:05 pm

@Andy - And how would you decode Base64 in JavaScript? Besides being inefficient there's no native support for it. The above is all native in JavaScript. JavaScript type encoding is easy enough to do 
on the server side and automatic on the client side. 

Interesting 'control' for passing ASP.NET variables into client-side Javascript
by Dave Amphlett's Personal Blog March 12, 2008 @ 7:54 am

Anyone using ASP.NET and Javascript combo should consider this to prevent code injection attacks... ...

re: Data-driven ResourceProvider w/ Ajax Resource Admin form

http://www.west-wind.com/WebLog/posts/252178.aspx
by Sam  March 13, 2008 @ 10:38 am

Data-driven ResourceProvider w/ Ajax Resource Admin form

Rick,
When i add masterpage to the project and add content tags to customerlist.aspx , the page not working, not showing any of the customer codes it used to show if i put the form tag instead.

Regards
Samarjit Chavan 

  Add a Comment

 © Rick Strahl, West Wind Technologies, 2005 - 2008

http://www.west-wind.com/WebLog/posts/252178.aspx
Web Images Maps News Shopping Gmail more ▼ iGoogle | Sign in

    Advanced Search
  Preferences
  Language Tools
Google Search I'm Feeling Lucky

Over 16,000 children drew doodles for Google's homepage.
Vote for the one that will appear here.

Advertising Programs - Business Solutions - About Google

©2008 Google

http://www.google.com/