Toolkit

Getting Started

A small introduction

The Toolkit javascript framework allows you to define your HTML in a JSON structure. The structure can contain the normal html attributes, javascript events, or any custom properties and events. The framework also includes support for inheritance. When initializing, the JSON structure is processed and HTML elements are generated.

The name Toolkit is choosen because the ability to use inheritance opens op abilities to easily define, use, reuse and extend components. Toolkit comes with several built in components (Sortable Tables, Tree, Forms and more). Reusability and Structure are the main focus points for Toolkit.

Include the Toolkit script

Get started by adding this script to your HTML page

<script src="https://toolkit.comgenie.com/minified/toolkit.js"></script>

Define an entry point

<script>
    // This is your application template. This will be used to generate HTML elements.
    var application =  {
        innerHTML: "Hello World!"
    };

    // Wait till all resources are loaded    
    window.addEventListener("load", function() {
        // Initialize and append the application template above to the current body element.
        document.body.appendChild(TK.Initialize(application));
    });
</script>

The application template of the code above will create a DIV with the text 'Hello World!'. For all code examples, this is the only part of the code above we are going to modify.

{
    innerHTML: "Hello World!"
}

Defining the type

Use the special property name _ to say what HTML element should be created. By default a DIV element will be created.

{
    _: "button",
    innerHTML: "I am a button",
    onclick: function() {
        alert('You pressed me, great job!');
    }
}

You can also reference another template in the _ property as demonstrated below. This will inherit all properties and allows you to override them.

{
    _: {
        _: "button",
        innerHTML: "I am a button",
        onclick: function() {
            alert('You pressed me, great job!');
        }
    },
    innerHTML: "I am ALSO a button"
}
var buttonTemplate = {
    _: "button",
    innerHTML: "I am a button",
    onclick: function() {
        alert('You pressed me, great job!');
    }
};

var application = {
    _: buttonTemplate,
    innerHTML: "I am ALSO a button"
};

The Init event

After transforming the application template to HTML elements, the framework will call all the Init functions defined (including those overriden).

{
    _: "button",
    innerHTML: "Button Button",
    Init: function() {
        this.innerHTML = "Changed";
    }
}

Children

Using the Elements property, child elements can be defined with their own name. These children and their parents can be reached using the .Elements, .Parent and .Sibling properties.

{   
    innerHTML: "Parent",
    Elements: {
        FirstChild: {
            innerHTML: "I am child one",
            onclick: function() {
                alert(this.Sibling.SecondChild.innerHTML);
            }
        },
        SecondChild: {
            innerHTML: "I am child two",
            onclick: function() {
                this.Parent.Elements.FirstChild.innerHTML = "I am changed by child two";

            }
        }
    },
    Init: function() {
        this.Elements.FirstChild.innerHTML = "I am changed by my parent element.";
    }
}

Helpers: Creating childs

Adding a suffix with the HTML element name, will create the child element as that HTML type, removing the need to use the _ property.
If only the innerHTML needs to be changed, the value can also be just a "string". You can also include variables with the $propertyName$ syntax. $SearchFilter:propertyName$ can be used to search for a specific element (Same functionality as the Near method).
The prefix TextButton will create a button element, and uses the name as text for the button.
If a function is given instead of a sub property, it is automatically added as onclick code.

{   
    innerHTML: "Parent",
    SomeField: 123,
    Elements: {
        FirstChildH3: {
            innerHTML: "I am child one"
        },
        SecondChildH4: "I am child two, $Parent.SomeField$, $FirstChildH3:innerHTML$",
        TestTextButton: function() {
            this.innerHTML = "Hi";
        }
    }
}

You can also use the .HTML property for the same functionality. Note that this will remove the child Elements from the DOM.

{
    PersonName: "Mr. Anderson",
    HTML: "Hello $PersonName$",
    Elements: {
        SomeButton: "I am not visible unless referenced as explained below"
    },
    onclick: function() {
        // Changing is also possible
        this.SetHTML("Bye $PersonName$"); 
    }
}

Using the $propertyName$ syntax, you can also reference to a (child) Element. This will insert that element into that location. Note that this does come with some performance penalty.

{
    HTML: "You can press the $Elements.TestButton$, but it does $Elements.ResultSpan$!",
    Elements: {
        TestButton: {
            innerHTML: "button",
            onclick: function() {
                this.Parent.Elements.ResultSpan.innerHTML = "something";
            }
        },
        ResultSpan: "nothing"
    }
}

Adding and Removing

The following functions are added to all elements: Add / Remove / Clear. These can be used to manipulate the structure.

{
    Elements: {
        AddButton: {
            innerHTML: "Add",
            onclick: function() {

                // Add a child to my parent element
                // (Optionally, you can give the new child a name with the second parameter)
                this.Parent.Add({
                    _: "button",
                    innerHTML: "I am a child, Click to remove me",
                    onclick: function() {
                        // Remove this element
                        this.Remove();
                    },
                    Destroy: function() {
                        // Optionally, you can also do something when this element gets removed.
                    }
                });
                
            }
        },
        ClearButton: {
            innerHTML: "Clear",
            onclick: function() {
                // Remove all the childs from the parent element, including me
                this.Parent.Clear();
            }
        }
    }
}

Self

Use the .Self property to directly point to the main object which is added using .Add(), or the entry point initialized using TK.Initialize(). This can be useful to directly navigate to the main properties of a template without having to use a .Parent.Parent.Parent.. chain. If needed, an object can set _Self to true to make itself the .Self object.

{   
    Test: 123,
    Elements: {
        Container: {
            innerHTML: "Press the button",
            Elements: {
                List: {
                    _: "ul",
                    Elements: {
                        Item: {
                            _: "li",
                            Elements: {
                                ExampleTextButton: function() {
                                    // this.Self is available everywhere
                                    // And points to the top object manually Added
                                    alert(this.Self.Test);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Tags

By filling the _Tag property with a name. The element will be made available for all child elements using $ + tag name (Child -> Parent with tag). If the _Tag property is set to the value 'true', the name of the element used to add the element is used.

{
    _Tag: "Example",
    Elements: {
        Text: "Tag example",
        ExampleButton: {
            innerText: "Click me to change the element with the _Tag set to 'Example'",
            onclick: function() {
                this.$Example.style.borderTop = "5px solid #f00";
            }
        },        
    }
}

It is also possible to reach a child element from the parent using the child's element tag, this is done using $$ + tagname (Parent -> Child with tag).

Other methods

The Near function can be used to find the nearest child element with a specific name. Note that this method comes with a small performance penalty.

{   
    Elements: {
        ResultText: {
            innerHTML: "Press the buttons"
        },
        FirstButton: {
            innerHTML: "First Button",
            onclick: function() {
                this.Near("ResultText").innerHTML = "Changed by the first button";
            }
        },
        SecondButton: {
            innerHTML: "Second Button",
            onclick: function() {
                this.Near("ResultText").innerHTML = "Changed by the second button";
            }
        }
    }
}

Styling

A css class is added automatically to all children: .Element-ChildName
Adding your own css class is of course possible with the default javascript property className.

{
    innerHTML: "Styling",
    Elements: {
        ExampleStyle: "This is an example"
    }
}
.Element-ExampleStyle {
  color: #f00;
}

It is also possible to set a style in code:

{
    style: {
        backgroundColor: "green"
    },
    innerHTML: "Styling using code",
    onclick: function() {
        this.style.backgroundColor = "blue";
    }
}

Reference a property on runtime

In some cases it is needed to reference to a property on runtime (When the element is added to the page), instead of during parsing of the javascript. This can be done by using the TK.P("propertyName") function. It is also possible to use TK.P(function) in case the value needs to be retrieved on runtime using a custom javascript function.

{
    Template: {
        Text: TK.P("TextInput:value"),
        Number: TK.P(function(a) {
            return a.Parent.Elements.ToArray().length;
        }),
        Init: function() {
            this.innerHTML = this.Text + ", Number: " + this.Number;
        }
    },
    Elements: {
        TextInput: {},
        AddTextButton: {
            onclick: function() {
                this.Parent.Elements.TextContainer.Add(this.Parent.Template);
            }
        },
        TextContainer: {

        }
    }
}

Reference a property on runtime and observe changes

You can also add an reference to another property, and observe any changes. Note that this will make your current property read-only. Using the FieldUpdate function you can react on the changes.

{
    Elements: {
        AgeLabel: "Enter your age: ",
        AgeInput: {        
            SelectedValue: 0,
            onchange: function() {
                this.SelectedValue = this.value;
            }
        },
        Status: {
            Age: TK.P("AgeInput:SelectedValue", true),
            FieldUpdate: function() {
                this.innerHTML = "You entered " + this.Age;
            }
        }
    }
}