📜 ⬆️ ⬇️

jsForms

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';


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/>


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 .


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 .



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 .



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 .



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:

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 .


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:

You can test here .

P.S.


The following components are currently implemented:

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.

Source: https://habr.com/ru/post/45361/


All Articles