ScriptArguments: An easy way to programmatically pass arguments to script from codebehind January 13, 2012
Posted by codinglifestyle in AJAX, ASP.NET, C#, CodeProject, Javascript.Tags: ajax, arguements, codebehind, function signature, Javascript, programmatic
1 comment so far
During my on-going adventures AJAXifying a crusty old business app I have been using a methodology by which most client events are setup in codebehind. The reason for this is I have easy access to my client ids, variables, and resources in codebehind. By constructing the script function calls at this stage, I can avoid messy and fragile in-line code. What I am endeavouring to do is remove all script from the markup itself. So instead of having MyPage.aspx with script mixed with markup I have MyPage.js and all functions here. Separate js files avoid fragile in-line code which only fails at runtime, can’t be refactored, and doesn’t play as nice with the debugger. Besides, separation of markup and script is good!
The downside to setting up all this script in the codebehind is it didn’t take long for the number of arguments to grow and become unruly. My script function signature looked like this:
function fnAddressChange(ddId, labelId, checkId, sameAsId, hidSelectId, hidSameAsId, onSelectEvent)
And in the codebehind I had this:
string selectArgs = string.Format("'{0}', '{1}', '{2}', '{3}', '{4}', '{5}'", _DropDownAddress.ClientID, _LabelAddress.ClientID, _RowSameAs.ChildClientID, (SameAs && _SameAsAddress != null) ? _SameAsAddress.LabelControl.ClientID : "-1", _HiddenSelectedID.ClientID, _HiddenSameAs.ClientID); string selectScript = string.Format("fnAddressSelect({0}); ", selectArgs); string changeScript = string.Format("fnAddressChange({0}, '{1}'); ", selectArgs, OnClientSelect);
We can see selectArgs is getting out of control. Not only is it getting ridiculous to add more to it, the function signature in script is getting huge and the ordering is easier to mess up. So I came up with this solution:
ScriptArguments args = new ScriptArguments (); args.Add("ddId", _DropDownAddress.ClientID); args.Add("labelId", _LabelAddress.ClientID); args.Add("checkId", _RowSameAs.ChildClientID); args.Add("sameAsId", (SameAs && _SameAsAddress != null) ? _SameAsAddress.LabelControl.ClientID : "-1"); args.Add("hidSelectId", _HiddenSelectedID.ClientID); args.Add("hidSameAsId", _HiddenSameAs.ClientID);
Not only is the codebehind cleaner but I don’t have to worry about string.Format or the order in which I add arguments in. The resulting script generated is:
args.ToString() "{ ddId : 'ctl00__ContentMain__ControlOrderSoldTo__AddressSoldTo__DropDownAddress', labelId : 'ctl00__ContentMain__ControlOrderSoldTo__AddressSoldTo__LabelAddress', checkId : 'ctl00__ContentMain__ControlOrderSoldTo__AddressSoldTo__RowSameAs_FormField_CheckBox', sameAsId : '-1', hidSelectId : 'ctl00__ContentMain__ControlOrderSoldTo__AddressSoldTo__HiddenSelectedID', hidSameAsId : 'ctl00__ContentMain__ControlOrderSoldTo__AddressSoldTo__HiddenSameAs' }"
This is a javascript Object with a property per key set to the corresponding value. So in script I only need to take in one argument, the argument object. I can then access every piece of information inserted in to ScriptArguments via the correct key:
function fnAddressIsReadOnly(args) { alert(args.ddId); alert(args.labelId); }
Will alert me with “ctl00__ContentMain__ControlOrderSoldTo__AddressSoldTo__DropDownAddress” and “ctl00__ContentMain__ControlOrderSoldTo__AddressSoldTo__LabelAddress”.
The great thing is how simple this was to implement:
public class ScriptArguments : Dictionary<string, string> { public override string ToString() { StringBuilder script = new StringBuilder("{ "); this.Keys.ToList().ForEach(key => script.AppendFormat("{0} : '{1}', ", key, this[key])); script.Remove(script.Length - 2, 2); script.Append(" }"); return script.ToString(); } }
This simple class solves a simple problem. I hope you find it useful.
Pass a Name Value Pair Collection to JavaScript August 8, 2011
Posted by codinglifestyle in ASP.NET, CodeProject, Javascript.Tags: ASP.NET, Javascript, jQuery
1 comment so far
In my crusade against in-line code I am endevouring to clean up the script hell in my current project. My javascript is littered these types of statements:
var hid = <%=hidSelectedItems.ClientId%>; var msg = <%=GetResourceString('lblTooManyItems')%>;
Part of the cleanup is to minimize script on the page and instead use a separate .js file. This encourages me to write static functions which take in ids and resources as parameters, allows for easier script debugging, and removes all in-line code making maintenance or future refactoring easier.
While moving code to a proper .js file is nice there are times we might miss the in-line goodness. Never fear, we can build a JavaScript object containing properties for anything we might need with ease. This equates to passing a name/value pair collection to the JavaScript from the code behind. Take a look at this example:
ScriptOptions options = new ScriptOptions(); options.Add("ok", GetResourceString("btnOK")); options.Add("oksave", GetResourceString("btnOkSave")); options.Add("cancel", GetResourceString("btnCancel")); options.Add("viewTitle", GetResourceString("lblAddressEditorView")); options.Add("editTitle", GetResourceString("lblAddressEditorEdit")); options.Add("createTitle", GetResourceString("lblAddressEditorCreate")); options.RegisterOptionsScript(this, "_OptionsAddressEditorResources");
Here we’re using the ScriptOptions class to create an object called _OptionsAddressEditorResources we can access in our script. Now let’s see these options in use:
function fnAddressEditDialog(address, args) { //Define the buttons and events var buttonList = {}; buttonList[_OptionsAddressEditorResources.ok] = function() { return fnAddressEditOnOk(jQuery(this), args); }; buttonList[_OptionsAddressEditorResources.oksave] = function() { return fnAddressEditOnOkSave(jQuery(this), args); }; buttonList[_OptionsAddressEditorResources.cancel] = function() { jQuery(this).dialog("close"); }; //Display the dialog jQuery("#addressEditorDialog").dialog({ title: _OptionsAddressEditorResources.editTitle, modal: true, width: 535, resizable: false, buttons: buttonList }); }
Above we see the jQuery dialog using the resources contained within the _OptionsAddressEditorResources object.
So this seems simple but pretty powerful. Below is the ScriptOptions class which simply extends a Dictionary and writes out the script creating a named global object. Good luck cleaning up your script hell. Hopefully this will help.
/// <summary> /// Class for generating javascript option arrays /// </summary> public class ScriptOptions : Dictionary<string, string> { /// <summary> /// Adds the control id to the options script /// </summary> /// <param name="control">The control.</param> public void AddControlId(WebControl control) { this.Add(control.ID, control.ClientID); } /// <summary> /// Registers all the key/values as an options script for access in the client. /// </summary> /// <param name="page">The page</param> /// <param name="optionsName">Name of the options object</param> public void RegisterOptionsScript(Page page, string optionsName) { if (!page.ClientScript.IsStartupScriptRegistered(page.GetType(), optionsName)) { StringBuilder script = new StringBuilder(string.Format("var {0} = new Object();", optionsName)); this.Keys.ToList().ForEach(key => script.Append(string.Format("{0}.{1}='{2}';", optionsName, key, this[key]))); page.ClientScript.RegisterStartupScript(page.GetType(), optionsName, script.ToString(), true); } } }
Currency TextBox with Javascript April 24, 2009
Posted by codinglifestyle in Javascript.Tags: currency, Javascript, regular expression, textbox, validator
1 comment so far
This took me a long time so here it is for prosperity. I started by looking at Ajax’s masked edit field in order to enforce a simply currency field. This was a waste of time and not up for the job. I accomplished it with javascript instead. Very simple, no symbols, just #####.## with the cents being optional.
Here is the script:
/*
** Returns the caret (cursor) position of the specified text field.
** Return value range is 0-oField.length.
*/
function doGetCaretPosition(oField) {
// Initialize
var iCaretPos = 0;
// IE Support
if (document.selection) {
// Set focus on the element
oField.focus();
// To get cursor position, get empty selection range
var oSel = document.selection.createRange();
// Move selection start to 0 position
oSel.moveStart(‘character’, -oField.value.length);
// The caret position is selection length
iCaretPos = oSel.text.length;
}
// Firefox support
else if (oField.selectionStart || oField.selectionStart == ‘0’)
iCaretPos = oField.selectionStart;
// Return results
return (iCaretPos);
}
function fnCurrencyOnly(o) {
var sValue = o.value;
var sKey = String.fromCharCode(window.event.keyCode);
var range = document.selection.createRange();
var pos = doGetCaretPosition(o);
if (range.text == sValue) {
sValue = sKey;
} else {
sValue = sValue.substr(0, pos) + sKey + sValue.substr(pos);
}
var re = new RegExp(“^\\d+(?:(\\.|\\,)\\d{0,2})?$”);
if (!sValue.match(re))
window.event.returnValue = false;
}
<div>
<input type=”text” onkeypress=”fnCurrencyOnly(this);” />
</div>
I’m sure sure who the original author is, but I found the script for doGetCaretPosition() here: http://blog.vishalon.net/index.php/javascript-getting-and-setting-caret-position-in-textarea/
Enjoy!
Numeric input only for a HtmlInputField January 12, 2006
Posted by codinglifestyle in ASP.NET, Javascript.Tags: Javascript, regular expression, validator
1 comment so far
Sometimes a validator control is overkill, especially when you are dynamically creating all your web controls and just want a simple restriction. Below is a little javascript to require numeric input only:
HtmlInputField myField = new HtmlInputField();
myField.Attributes.Add(“onkeyup”, “this.value=this.value.replace(/[^\\d]*/gi,\”\”);”);
Exposing C# controls as Active-X and the effects on developing with the download cache January 5, 2006
Posted by codinglifestyle in C#, Javascript, Winform.Tags: active-x, control, GAC, Javascript, Winform
add a comment
A C# assembly can be easily exposed as an Active-X control using the object tag like so:
<OBJECT id=”Bob” classid=”url to assembly#full name of control></OBJECT>
So for example my assembly is located in a virtual directory on server VM2003S2:88 called MyObject. MyObject has a namespace of Chris.ActiveX.Controls and the class is MyControl. My object tag would be: http://vm2003s2:88/myobject.dll#chris.activex.controls.mycontrol
This complete I can now use javascript to talk to “Bob” via MyControl’s public methods. This is all well and good however you may experience frustration in seeing your updates during development.
The assembly is automatically pulled down to the download cache which is located in c:\windows\assembly\downloads on Win2003. Unfortunately, its not smart enough to read version strings and realize a newer copy is available on the server. Therefore you must clear this cache using:
gacutil /cdl
But that’s not all! A copy is also made in your temporary internet files. You won’t be able to simply delete it using del from the post-build event as Window’s treats the directory as special. So either write a small program utilizing the API to delete your assembly from the cache or use one of the miriad of privacy utilities to clean this for you. I found CleanUp! which can be quietly executed from the command line and added to my post build event. The result is the assembly is pulled every time (on the development machine) so you see your changes with each build.
Here is a good example explaining Embedded Windows User Controls into Internet Explorer:
http://www.devhood.com/tutorials/tutorial_details.aspx?tutorial_id=187