Good evening, after writing the previous post, probably more than three weeks have passed, since then my direction has changed a bit, but MVC is a good pattern, but now for js it is still too cumbersome. We strive to choose more transparent and subtle solutions that would allow to see how everything works, which is why I like jQuery, it introduces the very minimum that allows you to abstract from the browser type, but it does not remove the feeling that you write exactly on js. That's why when trying to implement the Document-View pattern on js, I wanted to make it as subtle and imperceptible as possible, and it seems to me that I managed to achieve the same light sensations that it was all the same js, it just expanded a little.
As in WinForms, in jsForms everything is built on components. So let's get started.
Component
The component consists of two parts:
Visual part
The visual part is a template that contains HTML markup, sets properties and events.
The template is a js file having the following form:
jsForms.Templates.ComponentType='HTML';
- ComponentType - component type
- [HTML] - HTML code enclosed in quotes
Important - in HTML code, use the characters "<" and ">" to frame the tags, in other places use the "& lt;" and "& gt;" respectively.
The HTML tag must be very strict, that is, all open tags must be closed and the order followed.
The template should have only one root html tag.
Functional part
The functional part is also the js file, which contains the “class”, as well as the declaration of the properties and events of the component.
The functional part is:
jsForms.Components.ComponentType=<br/>
{<br/>[JavaScript]<br/>};<br/>
- ComponentType - component type
- [Javascript] - in general classic js
Using jsForms
Let us examine all the examples.
For example, the Template File for a button will look like this:
jsForms.Templates.Button=' < div class ="button" > '+<br> ' < div class ="button-background" > '+<br> ' < div class ="button-common button-w" /> '+<br> ' < div class ="button-common button-e" /> '+<br> ' </ div > '+<br> ' < span name ="text" class ="button-text" /> '+<br>' </ div > '; <br><br> * This source code was highlighted with Source Code Highlighter .
Component
To use other components in the template, use the tag: component
In this case, a component whose template contains other components will be called a container.
The component tag has a view
< component [ name ='ComponenName' ][ id ='IdValue' ][ class ='CSSClass' ] > [Settings] </ component > <br><br> * This source code was highlighted with Source Code Highlighter .
- ComponentType - component type
- ComponenName - the name of the component, an optional parameter, will be described below.
- id - sets the same as for html tag, id property
- class - sets as for the html tag, the class property
- Setting - a set of tags, which will be discussed later
Let us consider an example of a dialog box that contains some text and two buttons.
jsForms.Templates.InsertCSSDialog=' < div > '+<br> ' < div > Test text </ div > '+<br> ' < component type ="Button" /> '+<br> ' < component type ="Button" /> '+<br> ' </ component > '+<br>' </ div > '; <br><br> * This source code was highlighted with Source Code Highlighter .
But the question arises how to set the names of the buttons?
To set the properties of the coponent is the properties tag.
Properties
The properties tag contains all the specified properties of the component, the enumeration is using the key tag
The key tag has a view
< key name ="PropertyName" [ value ="PropertyValue" ][ type ="object|string|int|float..." ][ disable ][ direct ][ const ] > [PropertyValue] </ key > <br> <br> * This source code was highlighted with Source Code Highlighter .
- PropertyName - property name, required parameter
- PropertyValue - value, must be specified either in the value attribute, or inside the key tag, the attribute is priority
- type - value type, optional parameter
- disable - attribute indicates that the property will not be set
- direct - the attribute indicates that the property is set to bypass the set property; it is now ignored.
- const - attribute is reserved for future multi-language support.
So we can set the “Text” to our buttons.
jsForms.Templates.InsertCSSDialog=' < div > '+<br> ' < div > Test text </ div > '+<br> ' < component type ="Button" > '+<br> ' < properties > '+<br> ' < key name ="Text" value ="Ok" /> '+<br> ' </ properties > '+<br> ' </ component > '+<br> ' < component type ="Button" > '+<br> ' < properties > '+<br> ' < key name ="Text" value ="Cancel" /> '+<br> ' </ properties > '+<br> ' </ component > '+<br>' </ div > '; <br><br> * This source code was highlighted with Source Code Highlighter .
Now we will pick up events for the buttons, for this we will use the events tag
Events
The events tag contains all the specified component events, the enumeration is using the key tag.
< key name ="EventName" value ="EventCallback" /> <br><br> * This source code was highlighted with Source Code Highlighter .
- EventName - event name, required parameter
- EventCallback is the name of the method from the container "class" component.
Let's make changes to the functional part of our component by adding two methods there: bOk_Click, bCancel_Click
jsForms.Components.InsertCSSDialog=<br>{<br> // <br> // <br> // <br> bOk_Click: function ()<br> {<br> alert( 'OK' );<br> },<br> bCancel_Click: function ()<br> {<br> alert( 'Cancel' );<br> }<br> // <br> // <br> // <br>}; <br><br> * This source code was highlighted with Source Code Highlighter .
And change our template
jsForms.Templates.InsertCSSDialog=' < div > '+<br> ' < div > Test text </ div > '+<br> ' < component type ="Button" > '+<br> ' < properties > '+<br> ' < key name ="Text" value ="Ok" /> '+<br> ' </ properties > '+<br> ' < events >< key name ="onClick" value ="bOk_Click" /></ events > '+<br> ' </ component > '+<br> ' < component type ="Button" > '+<br> ' < properties > '+<br> ' < key name ="Text" value ="Cancel" /> '+<br> ' </ properties > '+<br> ' < events >< key name ="onClick" value ="bCancel_Click" /></ events > '+<br> ' </ component > '+<br>' </ div > '; <br><br> * This source code was highlighted with Source Code Highlighter .
')
Templates have another important property for us, they allow us to give each html tag and component names,
which will be available in the "class". In order to set the name you need to set the name attribute; for all components that have
this attribute is not set, the name of the form "_CX" is automatically generated when compiling, where X is a number,
why it will need to be understood later. How to use the names in the component code will be described below.
The template allows you to make investments of some components in the other. For example, let's create a MultiPanel component.
Template:
jsForms.Templates.MultiPanel=' < div > '+<br> ' < div class ="MPanelTop" name ="top" /> '+<br> ' < div class ="MPanelCenter" name ="content" /> '+<br> ' < div class ="MPanelBottom" name ="bottom" /> '+<br>' </ div > '; <br><br> * This source code was highlighted with Source Code Highlighter .
"Class":
jsForms.Components.MultiPanel={};
Content
Now we will change our dialog box - we will place the keys in the lower part of the panel, the text in the upper part, we will also add an input to the central one.
jsForms.Templates.InsertCSSDialog=' < div > '+<br> ' < component type ="MultiPanel" > '+<br> ' < content into ="top" > '+<br> ' < div > Please enter css class name </ div > '+<br> ' </ content > '+<br> ' < content > '+<br> ' < input name ="iStyleClassName" /> '+<br> ' </ content > '+<br> ' < content into ="bottom" > '+<br> ' < component type ="Button" > '+<br> ' < properties > '+<br> ' < key name ="Text" value ="Ok" /> '+<br> ' </ properties > '+<br> ' </ component > '+<br> ' < component type ="Button" > '+<br> ' < properties > '+<br> ' < key name ="Text" value ="Cancel" /> '+<br> ' </ properties > '+<br> ' </ component > '+<br> ' </ content > '+<br>' </ div > '; <br><br> * This source code was highlighted with Source Code Highlighter .
As you can see from the example, using the content tag, we can place one component inside another.
The content tag has a view
< content [ into ="ComponentName" ] > [HTML|Component] </ content > <br><br> * This source code was highlighted with Source Code Highlighter .
- into - indicates to which control the content will be added, the default is “content”
- [HTML | Component] - any html code + components.
Compiling

The picture from the previous post, the essence remains the same, but the parsing mechanism itself has changed.
At the first request of a component, the engine will check if the component has been compiled, if not, it will compile. To do this, it will parse the template and make a js function based on it.
When the function is called, the DOM model wrapped in jQuery will be created, and the basic functionality and functionality of the “class” of the component will be added to this object. The base functionality is the one defined by the Component component. All tags marked with the name attribute will be in the Components property. .
That is, if you compile:
jsForms.Templates.InsertCSSDialog=' < div id ="test" > '+<br> ' < component type ="MultiPanel" > '+<br> ' < content into ="top" > '+<br> ' < div name ="SimpleText" > Please enter css class name </ div > '+<br> ' < div > Unnamed div </ div > '+<br> ' </ content > '+<br> ' < content > '+<br> ' < input name ="iStyleClassName" /> '+<br> ' </ content > '+<br> ' < content into ="bottom" > '+<br> ' < component type ="Button" > '+<br> ' < properties > '+<br> ' < key name ="Text" value ="Ok" /> '+<br> ' </ properties > '+<br> ' </ component > '+<br> ' < component type ="Button" name ="bCancel" > '+<br> ' < properties > '+<br> ' < key name ="Text" value ="Cancel" /> '+<br> ' </ properties > '+<br> ' </ component > '+<br> ' </ content > '+<br>' </ div > '; <br><br> * This source code was highlighted with Source Code Highlighter .
At the output we get an object containing:
- Properties
- _Type - component type
- _Name - tag name set in the name attribute or generated automatically.
- P or Properties - the default is an empty object designed to store the properties of a particular object.
- E or Events - the default is an empty object designed to store events of a specific object.
- C or Components - an object containing all the tags and components wrapped in jQuery, marked with the name attribute in our case
C = {- Simpletext
- iStyleClassName
- bCancel
}
- JQuery object methods
- Component methods
- Init - initialization of the component, called by the user, after adding the component to the DOM
- _GetProperty (name) - get property
- _SetProperty (name, value) - set the property
- _SetEvent (name, obj, func) - set the event, you need to specify the object (obj) and the function it contains (func)
- _GetEvent (name) - get the structure {obj, func}
- _ExecEvent (name, arguments) - to execute the event, the argument must be one, but not mandatory.
- The methods of the component itself.
- bOk_Click () - user function
- bCancel_Click () - user function
- etc.
You should also know that the DOM component object itself contains a reference to the object and stores it in the _OBJ property, i.e.
document.getElementById ('test') ._ OBJ - will be equal to a specific object of the InsertCSSDialog class.
For a simpler entry, it is better to use jsForms.GetObject (element), and to search upwards for a specific component type
jsForms.GetObjectByType (element, foundType)
The implementation of the functional part
We now turn specifically to the writing of the functional part.
I recommend using the “chains” approach, which is very well implemented in jQuery; for this, at the end of each method, return a pointer to yourself [return this; ].
Also use specifically allocated space for data, that is, this.P. <PropertyName> for storing properties.
Give your methods and properties names with a capital letter - this will save you from overlapping jQuery methods, but still keep in mind the fact that the basic object is jQuery and its plugins, sometimes the names of the methods in plugins start with a capital letter, which can be patted you nerves)
When the "overload" of the Init method, insert at the end a call to the base class [return jsForms.Components.Component.Init.call (this);], it is necessary to initialize the nested components.
Remember that in this.C property you have jQuery content wrapping over tags and other components of the template, manipulate them.
Let's add validation to our dialog box:
jsForms.Components.InsertCSSDialog=<br>{ // <br> bOk_Click: function ()<br> {<br> var text = this .C.iStyleClassName.val();<br> if (text==undefined || $.trim(text)== '' )<br> {<br> alert( 'Please enter css class name' );<br> return ;<br> }<br> alert( 'OK, class="' +text+ '");<br> },<br> bCancel_Click:function()<br> {<br> alert(' Cancel');<br> },<br> // <br>}; <br><br> * This source code was highlighted with Source Code Highlighter .
If we want to add a property, event or container to our component, we need to declare it, for this it is easiest to call the jsForms.CreateCompileInfoByObject (objDeclaration) function, which takes a specially composed object as a parameter.
objDeclaration={<br>{Name:<ComponentType>,<br> Properties:{ // Property list <br> <PropertyName1>:{ Type:<PropertyType><,Values:{ object }>, Access:<PropertyAccess>,Description:<String>},<br> <PropertyName2>:{ Type:<PropertyType>,Access:<PropertyAccess>},<br> etc<br> }<br> Events:{<br> <EventName1>:{Description:<String>},<br> <EventName2>:{Description:<String>},<br> <EventName3>:{Description:<String>},<br> etc},<br> Contents:<br> {<br> <ContentName1>:{Description:<String>},<br> <ContentName2>:{Description:<String>},<br> <ContentName3>:{Description:<String>},<br> etc}<br> }<br>}; <br><br> * This source code was highlighted with Source Code Highlighter .
- Description - description, optional.
- ContentName is the name of the container, in the template there should be a tag with the name attribute like that of the container.
- PropertyName - the name of the property, the register is important.
- PropertyType - the input data type of the property; if Object is selected, the input value will be executed via eval (...) and passed to the property
- PropertyAccess - access, if access is set to 'Direct', then the entry will be made via the _SetProperty method bypassing the property set-ra, otherwise the Set <PropertyName> method will be called and the value passed to it, the setter method must be defined.
PropertyType in ('Boolean', 'String', 'Object', 'Int', 'Float', 'Values', <undefined>) <undefined> - is default
PropertyAccess in ('Direct', 'Set', 'Get', 'GetSet') 'GetSet' - is default
Let's take a look at the key as an example of how to declare properties and events.
jsForms.Components.Button = {<br> Init: function ()<br> {<br> // <br> // Init, <br> // <br> return this .SetEnabled( true );<br> },<br> eventClick : function (e)<br> {<br> if (e.data) { //e.data <br> if (e.data.P.Enabled) {<br> e.data._ExecEvent( 'onClick' );<br> }<br> }<br> },<br> SetEnabled: function (flag)<br> {<br> if ( this .P.Enabled != flag) {<br> this .P.Enabled = flag== true ;<br> this .attr( 'state' , flag ? 'enabled' : 'disabled' );<br> if (flag) {<br> this .unbind( 'click' ).bind( 'click' , this , this .eventClick );<br> }<br> else {<br> this .unbind( 'click' );<br> }<br> }<br> return this ;<br> },<br> GetEnabled: function ()<br> {<br> return this .P.Enabled;<br> },<br> GetText: function ()<br> {<br> return this .P.text;<br> },<br> SetText: function (newText)<br> {<br> if ( this .P.text != newText) {<br> this .P.text = newText;<br> this .C.text[0].innerHTML = newText;<br> }<br> return this ;<br> }<br>};<br> // ----------------------------------------------- <br> // Compilation INFO <br> // ----------------------------------------------- <br>jsForms.CreateCompileInfoByObject(<br>{<br> Name: 'Button' ,<br> Properties: {<br> Enabled: {<br> Type: 'Boolean' ,<br> Access: 'GETSET' <br> },<br> Text: {}<br> },<br> Events: {<br> onClick: {}<br> }<br>});<br> <br> * This source code was highlighted with Source Code Highlighter .
Insert components into the page
Let's now turn to the consideration of the process of using components on the page.
To create a component, you must call the function jsForms.CreateComponent (compType)
var button = jsForms.CreateComponent( 'Button' );<br> // "" , DOM<br/> <br>button.hide().appendTo(...);<br> // DOM , Init() <br/> <br>button.Init().show();<br> // , .<br/> <br> var button = jsForms.CreateComponent( 'Button' ).hide().appendTo(...).Init().show(); <br><br> * This source code was highlighted with Source Code Highlighter .
Performance
To measure the performance, 2000 simple objects were created, each test was repeated 10 times.
Test environment:
WinXP Sp2, FireFox 3.0.4 + FireBug, CPU Intel Pentium Dual E2160@1.80GHz, 2Gb Mem

As can be seen from the graphs, the results float, presumably this is due to the garbage collector, which occasionally slows down js.
Detailed statistics:
- jQuery (Template)
Rt: 10 R: 2000, T: 1440 ms [min: 1315, max: 2333 ms] [min: 0.6575 avr: 0.7198, max: 1.1665 ms] delta = 1018 - Manual DOM building
Rt: 10 R: 2000, T: 340 ms [min: 330, max: 375 ms] [min: 0.1650 avr: 0.1701, max: 0.1875 ms] delta = 45 - jsForms
Rt: 10 R: 2000, T: 442 ms [min: 434, max: 457 ms] [min: 0.2170 avr: 0.2210, max: 0.2285 ms] delta = 23
You can test
here .
P.S.
The following components are currently implemented:
- Button - "thick" key.
- ButtonDiv is a divi key, very light.
- Listbox
- Treeview
- TabControl
- Progressbar
- trifle
A living example of the work of this approach is the site,
component editor.The whole site and the technology itself are available under GPL3, you can download it from the
offsite .
The editor is in development, so I will welcome suggestions and criticism.
Technology tested on: FF2 +, IE7, Safari, Chrome
Who has IE6 at hand, sign off, thanks in advance.
Also thanks to Habr, since many ideas were underlined from its pages.
Thank.