Previously, I blogged on the new template engine that will be included as part of version 04.00.00 of the Repository module. Now, I would like to talk about the new token architecture.
Background:
In the current version of the Repository module and all previous versions, I designed the tokens to look and work like the DotNetNuke skinning tokens, square bracketed token names intermingled in with static text. Token settings were stored in matching .XML files establishing settings and values, then at runtime the engine would process each token, lookup and apply any settings in the .XML file and replace the token in the generated markup.
The new tokens:
While the source/xml file pairing works, from a developer and maintenance perspective it always seemed like too much work :) In thinking about designing the new tokens for 04.00.00, I thought about html markup and asp markup in general and decided to take the same approach to 'modifying' the token via attributes ... in line as part of the token markup.
So, where in the past you would have something like this...
in the source (html) file...
[TITLE]
in the matching settings (xml) file...
<Object>
<Token>[TITLE]</Token>
<Settings>
<Setting>
<Name>CssClass</Name>
<Value>header</Value>
</Setting>
</Settings>
</Object>
all just to set the style to use when rendering a data element. Now, in version 04.00.00, you will be able to specify your tokens in your template source like this...
[$TITLE cssclass="header"]
Much simpler. No separate .XML file to worry about, no file processing at run time to load and parse the .XML document, and it just feels familiar.
Some other examples
[INPUT type="TextEditor" datafield="TextEditor" width="600" height="350" required="true"]
[INPUT type="Button" text="View Details..." cssclass="normal" onclick="ShowView('details')"]
[$CreatedDate dateformat="ddd MM/dd/yyyy @ hh:mm"]
Behind the scenes:
If you remember from my previous blog, you create a template object, then load the source either by passing a string or a text file that contains the template source. When you load the source the template engine parses the text and identifies each token within your template. It caches the tokens for performance reasons, and then when you call the template's Render method or ToString method, the engine processes the template.
As each token is processed, the engine will look at the attributes which were specified in the markup to modify the content being generated to replace the token.
Here's an example of the logic for applying attributes...
[INPUT type="RadioButton" id="rbFavoriteSport" datafield="FavSport"
datasource="Sports"]
The above token instructs the template engine to inject a radio button control in place of the token. The id of the radio button should be "rbFavoriteSport", when the data is saved, the selected value will be stored in the "FavSport" column and the choices will be populated from the Sports Table.
The template engine has a function to create a dynamic radio button control and inject it into the stream. The first thing the function does is create the radio button list control, then it needs to modify the control by setting various attributes on the control before adding it to the control collection.
You do not have to specify EVERY possible attribute, so the engine creates a NameValueCollection and initializes it with all of the possible attributes for that control type along with default values for each, then it iterates through the attributes set in the markup and updates the NameValueCollection replacing the default values with any user specified values.
' ---------------------------------------------------
' create the control
' ---------------------------------------------------
Dim rb As New RadioButtonList()
' ---------------------------------------------------
' setup an attribute name value collection with default values
' ---------------------------------------------------
Dim nvc As New NameValueCollection()
nvc.Add("id", "")
nvc.Add("name", "")
nvc.Add("class", "normal")
nvc.Add("repeatcolumns", "1")
nvc.Add("repeatdirection", "horizontal")
nvc.Add("datasource", "")
nvc.Add("datafield", "")
nvc.Add("selectedvalue", "")
' ---------------------------------------------------
' parse the token parameters and update the attribute collection
' ---------------------------------------------------
MergeAttributes(token, nvc)
So, you can see in the above code, that there are 8 possible attributes that you can set for a radio button list. In our example token we are specifiying values for the name, datafield and items attributes, meaning that the default values will be used for the other 5. The MergeAttributes() method merges the user specified values with the default values resulting in a NameValueCollection that contains all of the settings we need to modify the control as requested.
After the user specified settings have been merged, then the engine will modify the dynamic control. For example, in this case, setting the RepeatColumn property of the radio button list control is done as follows...
rb.RepeatColumns = Integer.Parse(nvc("repeatcolumns"))
So, if the user does not specify a repeat columns setting on their token markup, the default value of "1" will be used, but if the user wanted 2 columns, then they just need to modify their token markup to look like this...
[INPUT type="RadioButton" id="rbFavoriteSport" datafield="FavSport"
datasource="Sports" repeatcolumns="2"]
and when the radio button list was generated, their radio button list would have 2 columns.
The end result is a much simpler method of applying attributes to your tokens which not only is easier to write, but easier to read ( you only have to look in one place ) and provides better performance as it avoids reading and parsing a separate xml file.