There is no point to doing all these client-side tests if you are not going to do anything based on their results. Earlier, this chapter pointed out that the view state is encrypted prior to going to the client browser. Because it is encrypted, it isn't really helpful to handle things on the client side. To you, as a developer, this means you need to find a way to get items onto the client side that you can use, and then pass those back to the server side where you can also make use of them.
The "Using the DotNetNuke Client API" section touched on how to register a variable, namespace, or script block. This was primer to show you how to get something over to the client side that you can communicate with. Normally this is done in one of two ways:
Setting the value of a hidden form field on the server side and then reading its value on the client side using the Document Object Model (DOM)
Setting the JavaScript variable directly on the server side by writing out a string the client side can read
With the Client API, you need to use the method RegisterClientVariable in your .NET code to register a variable on the server side for use on the client side. You will need to pass this method the Page object, a variable name/value pair, and a Boolean value to determine if the variable should be appended or overwritten. As shown in Listing 10-4, the first thing RegisterClientVariable does is create the client variable control. This is an HTML input control that is hidden — the input will always have a name and id of _dnnVariable. The method then builds a name/value set so it can then be added as the value for the hidden HTML input control.
Listing 10-4: Registering a Variable on the Client Side
Public Shared Sub RegisterClientVariable(ByVal objPage As Page, ByVal strVar As String, ByVal strValue As String, ByVal blnOverwrite As Boolean) 'only add once Dim ctlVar As HtmlInputHidden = ClientVariableControl(objPage) Dim strPair As String = GetClientVariableNameValuePair(objPage, strVar) If strPair.Length > 0 Then strPair = strPair.Replace("""", ClientAPI.QUOTE_REPLACEMENT) 'because we are searching for existing string we need it in its posted format (without quotes) If blnOverwrite Then ctlVar.Value = ctlVar.Value.Replace(ROW_DELIMITER & strPair, ROW_DELIMITER & strVar & COLUMN_DELIMITER & strValue) Else 'appending value Dim strOrig As String = GetClientVariable(objPage, strVar) ctlVar.Value = ctlVar.Value.Replace(ROW_DELIMITER & strPair, ROW_DELIMITER & strVar & COLUMN_DELIMITER & strOrig & strValue) End If Else ctlVar.Value &= ROW_DELIMITER & strVar & COLUMN_DELIMITER & strValue End If ctlVar.Value = ctlVar.Value.Replace("""", ClientAPI.QUOTE_REPLACEMENT) 'reduce payload of " System.Diagnostics.Debug.WriteLine(GetClientVariableNameValuePair(objPage, strVar)) System.Diagnostics.Debug.WriteLine(GetClientVariable(objPage, strVar)) End Sub
The method starts to build the name/value set by first calling the GetClientVariableNameValuePair function. This function parses the hidden HTML input control's current value and returns the delimited name/value pair. If there is a matching variable name/value pair returned, based on the passed-in strVar value, it uses the Boolean value passed in to determine if it should append or overwrite the value of the current name/value pair. After all of this is finished, the set of name/value pairs is finally written to the page being sent to the client as the value of the hidden HTML input control.
A good example of registering a variable for client-side use is in the DotNetNuke Tree control. If you look inside the DotNetNuke.WebControls\DNNTree.vb file, you will see the DnnTree_PreRender method. In Listing 10-5, the part of the DnnTree_PreRender method is shown where it calls the RegisterClientVariable method. This steps through the method as explained earlier and builds the name/value set using dnn_controlid_xml as the name part, and XML data will be written out as its value part. The name/value set will be the value for the __dnnVariable. To avoid confusion, please note that the dnn_controlid represents the ClientID variable that is generated by ASP.NET at runtime.
Listing 10-5: Example of RegisterClientVariable in DNNTree
DotNetNuke.UI.Utilities.ClientAPI.RegisterClientVariable(Me.Page, Me.ClientID & "_xml", Me.TreeNodes.ToXml, True)
Continuing with the example, the HTML input control named __dnnVariable has a value assigned to it. Although not outlined previously, this example registers the proper JavaScript files to be sent to the client before assigning any values to the input control. Listing 10-6 includes the RegisterClientScript method, which determines the files to send to the client side. Pay attention to the part toward the bottom where it calls RegisterStartUpScript. This is part of the System.Web.UI namespace that allows ASP.NET server controls to add client-side script blocks to the page. Looking into this further, you can see the script block being passed in calls the dnn.controls.initTree client-side function. It also adds the parameter of the control id before sending the script block.
Listing 10-6: Registering a Startup Script
Public Sub RegisterClientScript() If IsDownLevel = False Then DotNetNuke.UI.Utilities.ClientAPI.RegisterClientReference(Me.Page, DotNetNuke.UI.Utilities.ClientAPI.ClientNamespaceReferences.dnn_dom) DotNetNuke.UI.Utilities.ClientAPI.RegisterClientReference(Me.Page, DotNetNuke.UI.Utilities.ClientAPI.ClientNamespaceReferences.dnn_xml) If Me.PopulateNodesFromClient Then DotNetNuke.UI.Utilities.ClientAPI. BrowserSupportsFunctionality(Utilities.ClientAPI.ClientFunctionality.XMLHTTP) Then DotNetNuke.UI.Utilities.ClientAPI.RegisterClientReference(Me.Page, DotNetNuke.UI.Utilities.ClientAPI.ClientNamespaceReferences.dnn_xmlhttp) End If If Not ClientAPI.IsClientScriptBlockRegistered(Me.Page, "dnn.controls.dnntree.js") Then ClientAPI.RegisterClientScriptBlock(Me.Page, "dnn.controls.dnntree.js", "<script src="/books/3/435/1/html/2/"" & TreeScriptPath & "dnn.controls.dnntree.js""></script>") End If ClientAPI.RegisterStartUpScript(Page, Me.ClientID & "_startup", "<script>dnn.controls.initTree(dnn.dom.getById('"& Me.ClientID & "')); </script>") 'wrong place End If End Sub
All of the code discussed so far has been rendered on the server side. If you are an ASP.NET developer, everything should have seemed fairly familiar to you because it was all .NET code. Now you are about to enter client-side territory, which may be completely new to you. Because the DotNetNuke Client API took the namespace approach with its client-side script, it should help level the learning curve for those .NET developers who are working with client-side script for the first time.
In the "Starting on the Server Side" section, the DotNetNuke Tree control was used as an example. Before any values were set, the proper client-side namespaces were registered. Registering the client-side namespace sends the necessary JavaScript files and script blocks to the client. This is an important step because after the page is sent to the client, the dnn.controls.initTree function (see Listing 10-7) is called by the client side. dnn.controls.initTree creates a new instance of the DNNTree on the client side by creating a new XML document on the client side. The XML document retrieves its data from the controlid_xml variable created on the server side by calling the dnn.getVar client-side function. This getVar function basically does the opposite of RegisterClientVariable and selects the matching variable on the client side, and then populates the newly created XML document with data.
Listing 10-7: Beginning of the dnn.controls.dnntree.js File
dnn_control.prototype.initTree = function (oCtl) { //oCtl.innerHTML = '';//temp dnn.controls.controls[oCtl.id] = new dnn.controls.DNNTree(oCtl); dnn.controls.controls[oCtl.id].generateTreeHTML(); return dnn.controls.controls[oCtl.id]; } //------- Constructor -------// dnn_control.prototype.DNNTree = function (o) { this.ns = o.id; //stores namespace for tree this.container = o; //stores container //--- Data Properties ---// //this.xml = dnn.getVar(o.id + '_xml'); this.DOM = new dnn.xml.createDocument(); this.DOM.loadXml(dnn.getVar(o.id + '_xml')); //--- Appearance Properties ---// this.css = __dt_getAttr(o, 'css', ''); this.cssChild = __dt_getAttr(o, 'csschild', ''); this.cssHover = __dt_getAttr(o, 'csshover', ''); this.cssSel = __dt_getAttr(o, 'csssel', ''); this.cssIcon = __dt_getAttr(o, 'cssicon', ''); this.sysImgPath = __dt_getAttr(o, 'sysimgpath', ''); this.imageList = __dt_getAttr(o, 'imagelist', '').split(','); this.expandImg = __dt_getAttr(o, 'expimg', ''); this.workImg = __dt_getAttr(o, 'workimg', 'dnnanim.gif'); this.collapseImg = __dt_getAttr(o, 'colimg', ''); this.indentWidth = new Number(__dt_getAttr(o, 'indentw', '10')); if (this.indentWidth == 0) this.indentWidth = 10; this.checkBoxes = __dt_getAttr(o, 'checkboxes', '0') == '1'; this.target = __dt_getAttr(o, 'target', ''); this.defaultJS = __dt_getAttr(o, 'js', ''); this.postBack = __dt_getAttr(o, 'postback', ''); this.callBack = __dt_getAttr(o, 'callback', ''); this.callBackStatFunc = __dt_getAttr(o, 'callbackSF', ''); //if (this.callBackStatFunc != null) // this.callBackStatFunc = eval(this.callBackStatFunc); //obtain width of expand image this.expImgWidth = new Number(__dt_getAttr(o, 'expcolimgw', '12')); this.hoverTreeNode = null; this.selTreeNode=null; this.rootNode = null; if (this.container.tabIndex <= 0) { this.container.tabIndex = 0; dnn.dom.addSafeHandler(this.container, 'onkeydown', this, 'keydownHandler'); dnn.dom.addSafeHandler(this.container, 'onfocus', this, 'focusHandler'); } else { var oTxt = document.createElement('input'); oTxt.type = 'text'; oTxt.style.width = 0; oTxt.style.height = 0; oTxt.style.background = 'transparent'; oTxt.style.border = 0; oTxt.style.positioning = 'absolute'; this.container.parentNode.appendChild(oTxt); dnn.dom.addSafeHandler(oTxt, 'onkeydown', this, 'keydownHandler'); dnn.dom.addSafeHandler(oTxt, 'onfocus', this, 'focusHandler'); } }
After the page is fully loaded and everything has been rendered on the client side, there is the possibility that a user may interact — selecting, expanding, collapsing, and so on — with the items enabled on the client side, like the DotNetNuke Tree. The entire time the user is interacting with the DotNetNuke Tree, the variable used in the example is being manipulated with each change on the client side. Assuming postbacks and callbacks are not enabled for these changes, the variable keeps getting altered until some type of state change is made. An example would be expanding a tree view until the end user sees the node he wants to select. The user then selects the node and clicks the Submit button, sending the manipulated code is sent back to the server side for processing.
So far, the variables have been created on the server side and sent to the client side. When they were on the client side, these variable values were manipulated based on the actions performed by the end user. After the manipulation, the variables are sent back to the server side for processing after some type of event is thrown or a callback is done. (The details of callbacks are discussed later in this chapter.) No matter how the variable was returned, it needs to be processed on the server side. Listing 10-8 shows the function called to retrieve the value of a client-side variable. This function retrieves the value of the variable and returns it so you can do something with the result.
Listing 10-8: Retrieving the Value of a Client-Side Variable
Public Shared Function GetClientVariable(ByVal objPage As Page, ByVal strVar As String) As String Dim strPair As String = GetClientVariableNameValuePair(objPage, strVar) If strPair.IndexOf(COLUMN_DELIMITER) > -1 Then Return Split(strPair, COLUMN_DELIMITER)(1) Else Return "" End If End Function
In the DotNetNuke Tree example, the client side manipulated the __dnnVariable item stored in the HTML input area that was hidden. The submit button click handler is processed and in there, assuming only single selection was permitted, you want to see what tree node the user selected. Similar to the earlier discussion regarding registering the client-side variable using the RegisterClientVariable function, the DotNetNuke Tree control handles the GetClientVariable function for you. It processes the variable, enabling you as a developer to focus on its results in your preferred ASP.NET programming language. At this point, you would write custom logic to handle the changes made on the client side with the DotNetNuke Tree and possibly send those changes to the data store.
That completes the round trip. What happens next depends on what you want your application to do.By now you should start to see that the Client API handles most of the work for you. This type of functionality has been used on the Internet for several years now but never has it been this easy to use in DotNetNuke, especially for those not familiar with client-side programming. Although not necessary in this example because it made use of the DotNetNuke Tree control, a solid understanding of the areas discussed is necessary if you want to create a custom control of your own that handles changes on the client side.