Sharing Variables Between JavaScript and C#
11. January 2008 17:53
You know, I think the most difficult part of this blog post was coming up with a descriptive title for it – it just needs more words to be explained. The story goes like this: While working on a JavaScript-heavy web application, I found myself wanting to share a set of variables between my server side C# code and the client-side JavaScript code. I wanted to be able to set a value in the JavaScript code running in the clients browser, and then later retrieve that same value on the server in the code behind of the page upon the next postback – and vice versa. It might sound a bit tricky (or maybe not?), but it proved to be quite simple – in fact, its perfect for some Friday afternoon blogging indulgence, before we get started on dinner 🙂
Aim First…
Whenever I decide to create a component, the first thing I do is think about how I want to use it. I find writing some mock code the perfect way to settle on an interface for the component, before even writing a line of code for it (TDD, anyone?). This way, I’m sure I’m actually making what I need, and not what I think I might need. So, let’s look at some code that mimics what we want to accomplish with our component.
What I’d like to do, is to be able to drop it onto a Web Form and access it both from my JavaScript and my code behind. So, something like this:
<idc:ClientDictionary
ID="_myDictionary"
runat="server" />
Having declared that in my markup, I want to be able to do this from the client-side JavaScript:
$find('_myDictionary').setValue("key", "value");
And then, when the form is submitted, I want to be able to retrieve that value from the server-side C#:
…and Fire at Will
Okay, so hopefully you’ve got an idea of what I’m up to. Basically, what I want is a hidden input field – but one that can store more than one value. Instantly, I’m thinking Asp.Net Ajax – or more precisely, a Control Extender. I could write one that targets the HiddenField control, transporting my dictionary between the client and server by serializing it into the hidden input field. But while that would take care of the client-side part, I need to extend the HiddenField serverside aswell. To do that, I’ll write a new component that wraps the HiddenField and the said extender control into a composite control, ready to be dropped onto a form and just work.
Without any further ado, lets look at the source code I came up with for this control:
public class ClientDictionary : CompositeControl
{
private Dictionary<string,string> _values;
/// <summary>
/// The name of the dictionary, used for accessing the dictionary on the client using $find().
/// </summary>
public string Name
{
get
{
EnsureChildControls();
return _hiddenFieldExtender.BehaviorID; // we map the name to the behavior ID of the hidden field extender
}
set
{
EnsureChildControls();
_hiddenFieldExtender.BehaviorID = value;
}
}
public override bool EnableViewState
{
get
{
// this control does not need view state
return false;
}
set
{
throw new InvalidOperationException();
}
}
/// <summary>
/// Gets or sets the values stored in the dictionary
/// </summary>
public Dictionary<string, string> Values
{
get { return _values; }
protected set { _values = value; }
}
protected override void OnInit(EventArgs e)
{
EnsureChildControls();
// the hidden field contains our javascript dictionary, which we'll want to deserialize into a C# dictionary
string serializedDictionary = this.Page.Request.Form;
if (!String.IsNullOrEmpty(serializedDictionary))
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
this.Values = serializer.Deserialize<Dictionary<string, string>>(serializedDictionary);
}
else
{
// no client-side dictionary was present, so create a new blank one
this.Values = new Dictionary<string, string>();
}
base.OnInit(e);
}
private HiddenField _field;
private ClientDictionaryExtender _hiddenFieldExtender;
protected override void CreateChildControls()
{
_field = new HiddenField(); // we use the hidden field to transport our data between the server and client parts
_field.ID = "fl";
this.Controls.Add(_field);
// the extender makes sure a client-side behavior component representing the dictionary is available
this._hiddenFieldExtender = new ClientDictionaryExtender();
this._hiddenFieldExtender.BehaviorID = this.Name;
this._hiddenFieldExtender.ID = "xt";
this._hiddenFieldExtender.TargetControlID = _field.ID;
this.Controls.Add(this._hiddenFieldExtender);
}
protected override void OnPreRender(EventArgs e)
{
// its time to save the server-side dictionary into the hidden field, which will transport it to the client side component
JavaScriptSerializer serializer = new JavaScriptSerializer();
_field.Value = serializer.Serialize(this.Values);
base.OnPreRender(e);
}
}
The comments should make the above code pretty much self-explanatory, so lets just keep rolling and dive right into the javascript code:
Type.registerNamespace('Iridescence.Web.UI');
Iridescence.Web.UI.ClientDictionaryBehavior = function(element)
{
Iridescence.Web.UI.ClientDictionaryBehavior.initializeBase(this, );
this._dictionary = null;
this._onSubmitDelegate = null;
}
Iridescence.Web.UI.ClientDictionaryBehavior.prototype =
{
initialize : function()
{
Iridescence.Web.UI.ClientDictionaryBehavior.callBaseMethod(this, 'initialize');
this._onSubmitDelegate = Function.createDelegate(this, this._onSubmit);
$addHandler(window, "submit", this._onSubmitDelegate);
this._deserialize();
},
getValue : function(name)
{
var value = this._dictionary;
return value;
},
setValue : function(name, value)
{
this._dictionary = value;
},
_onSubmit : function()
{
// serialize the dictionary to the hidden field
this.get_element().value = Sys.Serialization.JavaScriptSerializer.serialize(this._dictionary);
},
_deserialize : function()
{
// deserialize the dictionary from hidden field
this._dictionary = Sys.Serialization.JavaScriptSerializer.deserialize(this.get_element().value);
},
clear : function()
{
this._dictionary = null;
this._dictionary = new Object();
},
dispose : function()
{
Iridescence.Web.UI.ClientDictionaryBehavior.callBaseMethod(this, 'dispose');
}
}
Iridescence.Web.UI.ClientDictionaryBehavior.registerClass('Iridescence.Web.UI.ClientDictionaryBehavior', AjaxControlToolkit.BehaviorBase);
The only thing left to define is the ClientDictionaryExtender, which is dead simple:
internal class ClientDictionaryExtender : ExtenderControlBase
{}
There’s no server-side functionality in the extender, its job is just to hook up the JavaScript component to the hidden field. I’ve made it internal as well, as it makes little sense to use it without the plumbing being handled by the ClientDictionary control.
And that is all there is to it! Dropping the ClientDictionary component onto a form, I can now set and get values from its dictionary both from the client-side JavaScript and the server-side codebehind, and it will be synchronized both ways.
Comments
This post is also available in: English
2/21/2008 11:31:24 AM
Hi! I think you might know tha answet to my question. I don’t really understand the ‘dictionary’ things, I cannot see where exactly the javascript-C# connection is, but my question also concerns sharing of variables between javascript and C#. In Google Maps (javascript) I can get Latitude and Longitude, but now I need these values for in a method in the C# code. How do I get it there?
I hope you can help me.
gjsonke (AT)gmail(DOT)com
Cheers – Gj
Gj Sonke
6/30/2008 7:07:32 PM
Thank you for this post. I’m looking to solve this very problem although I do not have VS2008 nor Ajax experience. I’m hoping to install Ajax on my dev machine to work with ASP.NET 2.0, could you kindly provide a VS 2005 solution of your sample code?
Thanks for your help
Mike
12/2/2008 11:38:22 AM
hi,
im wondering how to call my application variable in my code behind to javascript
ex:
code behind sample.aspx.cs
Application = «something»;
javascript:
<i want to do this in javascript like in the ex code below but i cant>
if (Application == «something») {
alert(«msg»);
}
can you do some code for me? thx.
Kimi
3/2/2009 11:23:08 AM
Thanks for your post. It help me a lot. 2 Thumbs up
jeffrey
3/9/2009 10:11:04 AM
TO pass the value from javascript to server side do this :
Declare a variable (or if already you have a variable) – var a = «this is a demo»;
// now declare a hidden variable which holds the value
var hidden = ‘<%= inpHide.ClientID %>’;
// Now put the variable to the hidden state
document.getElementById(hiddenControl).value=a ;
// access it from the server side
TextBox1.Text = inpHide.Value;
That’s it.
Anuj Tripathi
3/17/2009 7:08:45 PM
Beautiful! All I wanted was to access some ClientIDs in a .js file. No luck!
Then along came you. What a simple, elegant solution. Very clean, very reusable.
Thanks!
T. Ray Humphrey