jComponent / 05. Component
Updated: 13. June 2018
Author: Peter Širka

05. Component

Professional Support Chat with contributors

This page describes the whole implementation of reusable component. Learn from existing components.

Quick navigation:

Definition

The code below describes a simple component definition with default configuration:

COMPONENT('datetime', 'interval:1000;format:dd.MM.yyyy', function(self, config) {

    // This is the component scope
    // self == this
    // config == this.config

    var interval;

    self.readonly();
    self.blind();

    self.configure = function(name, value, init) {
        switch (name) {
            case 'interval':
                interval && clearInterval(interval);
                interval = setInterval(self.tick, value);
                break;
           case 'format':
                !init && self.tick();
                break;
        }
    };

    self.tick = function() {
        self.html(new Date().format(config.format));
    };

});

+v14.0.0 Declaration with a specific version:

// v1
COMPONENT('datetime@1', 'interval:1500;format:dd.MM.yyyy', function(self, config) {
    // ...
});

// v2
COMPONENT('datetime@2', 'interval:500;format:dd.MM.yyyy', function(self, config) {
    // ...
});

+v14.0.0 Declaration with multiple versions:

// v1
COMPONENT('datetime, datetime@1, datetime@2', 'interval:1000;format:dd.MM.yyyy', function(self, config) {
    // ...
});

Properties

Property: self._id

The property contains an internal identificator for this component.

  • readonly
  • returns String

Property: self.caller

The property contains a reference to caller/parent of some method executed via .exec()

  • readonly
  • returns Component
  • default undefined

Property: self.config

The property contains the whole configuration.

  • readonly
  • returns Object

Property: self.element

  • readonly
  • returns jQuery Element

Property: self.global

The property contains shared temporary object for all instances of this component.

  • returns Object

Property: self.id

The property contains an identifactor for this component. jComponent binds data-jc-id="ID" attribute to this property automatically. Otherwise self.id is same as self._id.

  • readonly
  • returns String

Property: self.name

The property contains a name of this component.

  • readonly
  • returns String

Property: self.path

The property contains the absolute path for data-binding. This value can be changed via self.setPath().

  • readonly
  • returns String

Property: self.pathscope

The property contains a path from parent e.g. <div data-jc-scope="" element - if exists.

  • readonly
  • returns String

Property: self.removed

The property indicates whether the component has been removed.

  • readonly
  • returns Boolean

Property: self.scope

The property returns a scope data object. Read more in Scopes section.

  • readonly
  • returns Object

Property: self.siblings

The property indicates whether the element contains multiple declaration of components in the one element, e.g. <div data-jc="binder,exec">

  • readonly
  • returns Boolean

Property: self.template

The property can contain url address to a template or jQuery selector which it starts with ., # or [. Template will be stored in this property before is executed self.make(). Default value is binded according to data-jc-template="DEFAULT_VALUE" attribute.

Examples:

  • URL address: self.template = '/templates/products.html'
  • selector: self.template = '#mytemplate'
  • or <div data-jc="YOUR_COMPONENT" data-jc-template="/templates/products.html">

Property: self.trim

The property can disable auto trim string values. It works only with inputs with data-jc-bind="" attribute.

  • returns Boolean
  • default true

Property: self.type

The property contains a value from data-jc-type="" attribute. This type describes a data-type for parser or formatter.

  • returns String

Property: self.usage

The property contains the last date of usage in milliseconds. The property has declared a method .convert().

{ init: 0, manually: 0, input: 0, default: 0, custom: 0, valid: 0, dirty: 0 }

console.log(self.usage.convert('seconds'));
// Output will be in seconds    : { init: 0, ... }

console.log(self.usage.convert('minutes'));
// Output will be in minutes: { init: 0, ... }

console.log(self.usage.convert('hours'));
// Output will be in hours: { init: 0, ... }
  • returns Object

Delegates

Delegates are executed on some jComponent actions. Some delegates have to return a value.

Delegate self.configure()

optional The delegate handles a configuration of the component. It's executed after is component initialized and after self.make(), or if self.reconfigure() is executed manually. Each key is processed independently.

self.configure = function(key, value, init, prev) {

    // @key {String}
    // @value {String/Number/Boolean/Object}
    // @init {Boolean} Is initialization?
    // @prev {String/Number/Boolean/Object} A previous value

    switch (key) {
        case 'required':
            self.tclass('required', value);
            break;
        case 'icon':
            self.find('.fa').rclass2('fa-').aclass('fa fa-' + value);
            break;
    }
};

Delegate self.destroy()

optional The delegate is executed when the component is removing.

self.destroy = function() {
    // Clean up
};

Delegate self.getter()

optional + internal The delegate has an internal implementation and it receives a value from HTML controls like input, select and textarea automatically. It's binded with data-jc-bind attribute on the target control. Its implementation is a bit complicated.

self.getter = function(value) {
    // @value {String} A value from HTML control
};

Delegate self.getter2()

optional This delegate has a similar functionality as self.getter() but you can use it for your own needs without changing self.getter().

self.getter2 = function(value) {
    // @value {Object} A processed value
};

Delegate self.init()

optional This delegate is executed the only onetime for all same components and jComponent evaluates it if the component is used. If you want to share some data between all instances then you can use self.global property.

self.init = function() {
    // It's executed the only one time for all instances of same components
};

Delegate self.knockknock()

optional This delegate is executed each 60 seconds by jComponent automatically.

self.knockknock = function(counter) {
    // @counter {Number}
};

Delegate self.make()

optional This delegate is executed when the component is creating. If you fill self.template property then the template argument will contain obtained data.

self.make = function(template) {
    // @template {String} Optional, it contains processed template data
};

Delegate self.prerender()

optional This delegate can update processed template before is executed self.make(). It works with filled self.template property only.

self.prerender = function(template) {
    // @template {String}
    // IMPORTANT: it needs to return a modified value
    return template.replace(/\n/g, '<br />');
};

Delegate self.released()

optional This delegate is executed when the component parent component performs release method. For example: j-Form component - when the form is closed the component executes released(true) for all nested components and when the form is going to visible the component executes released(false) for all nested components.

self.released = function(is) {

    // @is {Boolean} true = the component is released by parent component, false = isn't released

    if (is) {
        // here you can clean-up DOM because e.g. component is hidden
    } else {
        // here you can restore previous state because control is visible
    }

};

Delegate self.setter()

optional The delegate has an internal implementation and it sets a value to HTML controls like input, select and textarea automatically. It's binded with data-jc-bind attribute on the target control. Its implementation is a bit complicated. This delegate is executed when data on the data-jc-path are changed.

// Custom implementation:
self.setter = function(value, path, type) {

    // @value {Object}
    // @path {String}
    // @type {Number} 0: init, 1: manually, 2: by input, 3: default 

    // Apply formatters
    value = self.formatter(value);

    // Render the value:
    self.html(value == null ? 'NULL' : value.toString());
};

Delegate self.setter2()

optional This delegate has a similar functionality as self.setter() but you can use it for your own needs without changing self.setter().

self.setter2 = function(value, path, type) {

    // @value {Object}
    // @path {String}
    // @type {Number} 0: init, 1: manually, 2: by input, 3: default 

};

Delegate self.state()

optional This delegate can change CSS when the value is invalid or something else.

self.state = function(type, what) {

    // type === 0 : init
    // type === 1 : manually
    // type === 2 : by input
    // type === 3 : by default

    // what  === 1 : valid
    // what  === 2 : dirty
    // what  === 3 : reset
    // what  === 4 : update
    // what  === 5 : set
    // what  === 6 : notify

    self.tclass('error', type === 0 ? false : self.isInvalid());
};

Delegate self.validate()

optional This delegate validates a value. It's executed by jComponent automatically when the value is modified. This delegate has to return a Boolean value.

self.validate = function(value, isInitialValue) {

    if (isInitialValue)
        return true;

    return value.length > 0;
};

Methods

Method: self.aclass()

This method is alias for self.element.addClass() and it adds CSS class into the element classes.

self.aclass(cls, [delay]);
// @cls {String}
// @delay {Number} Optional, in milliseconds
// returns {Component}

Method: self.append()

This method is alias for self.element.append() and it appends a new content into the element. A value can be HTML.

self.append(value);
// @value {String} or {Array String}
// returns {Component}

Method: self.attr()

This method is alias for self.element.attr(name, [value]) and it can get/set a value into the element attribute.

self.attr(name, [value]);
// @name {String}
// @value {String} Optional
// returns {Component}

Method: self.attrd()

This method is alias for self.element.attr(name, [value]) and it can get/set a value into the element attribute with data- prefix for name of attribute.

self.attrd(name, [value]);
// @name {String}
// @value {String} Optional
// returns {Component}

self.attrd('name', 'value');
// sets a value to the "data-name" attribute

Method: self.bindchanges()

+v14.2.0 This method binds changes only. If setter will get the same value as a previous value then skips binding.

self.bindchanges();
// returns {Component}

Method: self.bindexact()

+v14.1.2 This method sets binding of values when the the modification path is same as the component path or the path is part of parent path.

self.bindexact();
// returns {Component}

Method: self.bindvisible()

+v13.0.0 This method sets binding of values only when the component is visible.

self.bindvisible([delay]);
// @delay {Number} A delay for binding value, default: DEF.delaybinder (200)
// returns {Component}

Method: self.blind()

This method sets the component as blind. Component will be skipped when jComponent performs data-binding. If your component won't work with data-binding then this option can increase a performance of your web app.

self.blind();
// returns {Component}

Method: self.change()

This method can perform a change state. It changes dirty state and contacts all components which listen on path.

self.change([is]);
// @is {Boolean} Optional
// returns {Component}

// Is a change?
console.log(self.change());
// Output: false

// Perform a change
self.change(true);

console.log(self.change());
// Output: true

Method: self.classes()

This method can add or remove CSS classes.

self.classes(value);
// @value {String}
// returns {Component}

self.classes('+selected +hover -disabled -fa-home +fa-check-circle');
// +class -> is for adding
// -class -> is for removing

Method: self.closest()

This method is alias for self.element.closest(selector), it searchs selector in all parent elements.

self.closest(selector);
// @selector {String}
// returns {jQuery}

Method: self.compile()

This method compiles new and uncompiled jComponents.

self.compile([container]);
// @container {jQuery Element} Optional, default: current element
// returns {jQuery}

Method: self.css()

This method is alias for self.element.css(name, [value]), it can set CSS or get.

self.css(name, [value]);
// @name {String}
// @value {String} Optional
// returns {Component} or {String}

// Writing:
self.css('margin', '5px');
self.css('width', '100%');

// Reading:
console.log(self.css('height'));

Method: self.datasource()

This method can watch some additional data-source. Each next declaration of self.datasource() cancels previous declaration.

self.datasource(path, callback, [init]);
// @name {String}
// @callback {Function(path, value, type)}
// @init {Boolean} Optional, invokes the callback with a value (default: true)
// returns {Component}
// +v12.0.0

self.datasource('products.manufacturers', function(path, value, type) {
    // @path {String}
    // @value {Object}
    // @type {Number} 0: init, 1: manually, 2: by input, 3: default 
});

Method: self.default()

This method can set a default value for the current component path.

self.default([reset]);
// @reset {Boolean} Optional, it resets "dirty" and "valid" states (default: true)
// returns {Component}

Method: self.emit()

This method emits an event within jComponent. Is alias for EMIT() method.

self.emit(name, [a], [b], [..n]);
// @name {String} Event name
// @a {Object} Optional, additional argument
// @b {Object} Optional, additional argument
// @..n {Object} Optional, additional argument
// returns {Component}

Method: self.empty()

This method is alias for self.element.empty(), it removes the whole content of element.

self.empty();
// returns {Component}

Method: self.evaluate()

This method is alias for self.element.empty(), it removes the whole content of element.

self.evaluate([value], expression, [is_value]);
// @value {Object} Optional, can be object if "is_value" is "true"
// @expression {String} A condition.
// @is_value {Boolean} Optional, default: false
// returns {Boolean}

// With reading a value according to the component "data-jc-path"
var isTeenager = self.evaluate('value >= 10 && value <= 20');

// With a real value
var isTeenager = self.evaluate(15, 'value >= 10 && value <= 20', true);

Method: self.event()

This method registers a new event for the element. Is alias for self.element.on() method.

self.event(name, [selector], callback);
// @name {String} Event name
// @selector {String} Optional, custom selector
// @callback {Function}
// returns {Component}

self.event('keydown', 'input', function(e) {
    console.log(e.which);
});

Method: self.exec()

The method executes a method according to the path in all nested components. It wont't throw any exception if the method not exist.

self.exec(name, [a], [b], [..n]);
// @name {String} A method name
// @a {Object} Optional, additional argument
// @b {Object} Optional, additional argument
// @..n {Object} Optional, additional argumentň
// returns {Component}

self.exec('refresh');
// Executes in all nested components --> component.refresh();

Method: self.extend()

The method extends a current value by adding/rewrite new fields with new values.

self.extend([path], value);
// @path {String} Optional, default current path
// @value {Object}
// returns {Component}

self.extend({ age: 35, alias: 'Peter' });

Method: self.find()

This method finds elements according to the selector. Is alias for self.element.find() method.

self.find(selector);
// @selector {String}
// returns {jQuery}

self.find('input').prop('disabled', true);

Method: self.formatter()

This method can register a new formatter for this component or can format a value. The method uses all registered global formatters MAIN.formatter() too for formatting values.

// For registering formatter:
self.formatter(fn);
// @fn {Function(path, value, type} Function needs to return a value
// returns {Component}

self.formatter(function(path, value, type) {
    // @path {String}
    // @value {Object}
    // @type {String} According to the "data-jc-type" attribute or "self.type"
    return typeof(value) === 'string' ? value.toUpperCase() : value;
});

// You can register more fomatters in a row
self.formatter(function(path, value, type) {
    return typeof(value) === 'string' ? value.split('').reverse().join('') : value;
});

// For formatting:
self.formatter(value);
// @value {Object}
// returns {Object}

console.log(self.formatter('Peter'));
// Output: RETEP

Method: self.get()

This method can get a value according to the data-jc-path or path.

self.get([path]);
// @path {String} Optional, default current component path
// returns {Object}

console.log(self.get());
// Gets a value: "window[component.path]"

console.log(self.get('users.grid.items'));
// Gets a value: "window.users.grid.items"

Method: self.hclass()

This method is alias for self.element.hasClass(), it determines class in the element.

self.hclass(cls);
// @cls {String}
// returns {Boolean}

Method: self.html()

This method is alias for self.element.html(), it can set/get a content of the element.

self.html(value);
// @value {String} or {String Array}
// returns {String} or {jQuery Element}

Method: self.import()

This method can import some content from external source to this element.

self.import(url, [callback], [insert], [preparator]);
// @url {String}
// @callback {Function} Optional
// @insert {Boolean} Optional, default: true
// @preparator {Function(content)} Optional, it needs to return modified content
// returns {Component}

self.import('/templates/products.html');

Method: self.inc()

The method increments a Number according to the component.path or path.

self.inc([path], value);
// @path {String} Optional, default current path.
// @value {Number}
// returns {Component}

self.inc(5);

Method: self.invalid()

The method sets the state of this component to invalid and it contacts all components listen on the path.

self.invalid();
// returns {Component}

Method: self.isInvalid()

The method checks a state whether the component is invalid or valid.

self.isInvalid();
// returns {Boolean}

Method: self.main()

The method returns a parent component instance if exists (otherwise: null).

self.main();
// returns {Component}

Method: self.nested()

The method returns all nested components.

self.nested();
// returns {Array Component}

Method: self.noDirty()

The method disables dirty state.

self.noDirty();
// returns {Component}

Method: self.noScope()

The method disables scopes according to the data-jc-scope attribute.

self.noScope();
// returns {Component}

Method: self.notify()

The method notifies a setter in all components according to the component.path.

self.notify();
// returns {Component}

Method: self.noValidate()

The method disables validation.

self.noValidate();
// returns {Component}

Method: self.parent()

The method is alias for self.element.parent(), it gets a parent element.

self.parent([selector]);
// @selector {String} Optional, jQuery selector
// returns {jQuery}

Method: self.parser()

This method can register a new parser for this component or can parse a value. The method uses all registered global parsers MAIN.parser() too for parsing values.

// For registering parser:
self.parser(fn);
// @fn {Function(path, value, type} Function needs to return a value
// returns {Component}

self.parser(function(path, value, type) {
    return typeof(value) === 'string' ? value.parseDate() : value;
});

// For parsing:
self.parser(value);
// @value {Object}
// returns {Object}

console.log(self.parser('2017-11-06'));
// Output: Mon Nov 06 2017 00:00:00 GMT+0100 (CET)

Method: self.push()

The method pushs a new item into the Array according to the component.path or path.

self.push([path], value);
// @path {String} Optional, default current path.
// @value {Object}
// returns {Component}

self.push('Node.js');
self.push(['Node.js', 'Total.js']);

Method: self.rclass()

The method is alias for self.element.removeClass(), it removes a class from the current element.

self.rclass(cls, [delay]);
// @cls {String} Class names
// @delay {Number} Optional, default: 0
// returns {jQuery}

Method: self.rclass2()

The method removes classes according to the text to search.

self.rclass2(search);
// @search {String} or {RegExp}
// returns {Component}

self.rclass2('fa-');

Method: self.readonly()

The method enables readonly mode for the component. It disables dirty and valid states, getter, setter, parsers and formatters. This option can improve performance.

self.readonly();
// returns {Component}

Method: self.reconfigure()

The method reconfigures the component. Internally it executes self.configure delegate. Read more about configuration.

self.reconfigure(value);
// @value {String} or {Object}
// returns {Object}

self.reconfigure('maxlength:50;required:false');

// is same as:

self.reconfigure({ maxlength: 50, required: false });

Method: self.refresh()

The method executes the component.setter with a refreshed value according to the component.path.

self.refresh([updatePath]);
// @updatePath {Boolean} Optional, "true" notifies all components on the path (default: false)
// returns {Component}

Method: self.release()

The method performs release state for all nested components.

self.release(is);
// @is {Boolean} "true": release state, "false" restore state
// returns {Component}

// E.g. component is hidden:
self.release(true);

// E.g. component will be visible:
self.release(false);

Method: self.remove()

The method removes this component from the DOM and executes destroy delegate.

self.remove();
// returns {Component}

Method: self.replace()

The method replaces the current element for a new.

self.replace(el, [remove]);
// @el {jQuery} a new element
// @remove {Boolean} Optional, removes older element (default: false)
// returns {Component}

Method: self.reset()

The method resets dirty and valid state.

self.reset();
// returns {Component}

Method: self.rewrite()

The method rewrites a value according to the component.path and it won't notify all listeners.

self.rewrite([path], value);
// @path {String} Optional, default: current component.path
// @value {Object}
// returns {Component}

Method: self.set()

The method sets a value according to the component.path.

self.set([path], value);
// @path {String} Optional, default: current component.path
// @value {Object}
// returns {Component}

Method: self.setPath()

The method rewrites component.path.

self.setPath(path);
// @path {String} New path
// returns {Component}

Method: self.singleton()

The method creates a single instance of the component. So if the component will be declared multiple times then jComponent creates the only one instance and another declarations will be skipped.

self.singleton();
// returns {Component}

Method: self.skip()

The method skips future calling of component.setter. It's incremental.

self.skip([path]);
// @path {String} Optional, absolute path (default: "data-jc-path")
// returns {Component}

// Skips a next calling of setter
self.skip();

// or with multiple paths:
self.skip('users.form.firstname, users.form.lastname');

// returns {Component}

Method: self.tclass()

The method toggles class, it's alias for self.element.toggleClass().

self.tclass(cls, [add]);
// @cls {String} A class name
// @add {Boolean} Optional, true: adds class, false: remove class, undefined: toggles class
// returns {Component}

Method: self.text()

This method is alias for self.element.text(), it can set/get a content of the element.

self.text([value]);
// @value {String}
// returns {String} or {jQuery Element}

Method: self.unwatch()

The method unregisters a monitoring of value according to the path argument.

self.unwatch(path, [handler]);
// @path {String}
// @handler {Function} Optional
// returns {Component}

Method: self.used()

The method sets the last used time, time will be stored in self.usage.custom.

self.used();
// returns {Component}

Method: self.validate2()

+v13.0.4 The method performs validation with refreshing state of component.

self.validate2();
// returns {Boolean}

Method: self.watch()

The method registers a monitoring of value according to the path argument.

self.watch(path, handler, [init]);
// @path {String}
// @handler {Function}
// @init {Boolean} Optional, it evaluates path now (default: false)
// returns {Component}

self.watch('products.items', function(path, value, type) {
    // @path {String}
    // @value {Object}
    // @type {Number} 0: init, 1: manually, 2: by input, 3: default
});

Usage in HTML

Each component needs to be initialized in HTML directly:

Component declaration:

<div data-jc="COMPONENT_NAME"></div>
<span data-jc="COMPONENT_NAME"></span>
<whatever data-jc="COMPONENT_NAME"></whatever>

Component declaration with data-binding:

<div data-jc="COMPONENT_NAME" data-jc-path="path.to.model"></div>
  • jComponent converts data-jc-path to the path in the window scope. It converts it to e.g. window.path.to.model in the background
  • so the component will watch a value on the path.to.model path

Component declaration with data-binding and configuration:

<div data-jc="COMPONENT_NAME" data-jc-path="path.to.model" data-jc-config="required:true;maxlength:30"></div>

Much simpler HTML declaration in +v14.2.1:

  • separator can be __ or ___ or _____ and can contain spaces between values
  • developer can choose declaration between new and older
<div data-jc="component __ path __ config __ default value"></div>

Examples:

<div data-jc="textbox___form.name___required:true;maxlength:30">NEW</div>
<div data-jc="textbox" data-jc-path="form.name" data-jc-config="required:true;maxlength:30">OLD</div>

<div data-jc="textbox___form.name___required:true;maxlength:30___'Peter'">NEW</div>
<div data-jc="textbox" data-jc-path="form.name" data-jc-config="required:true;maxlength:30" data-jc-value="'Peter'">OLD</div>

<div data-jc="textbox __ form.name __ required:true;maxlength:30 __ 'Peter'">NEW</div>
<div data-jc="textbox" data-jc-path="form.name" data-jc-config="required:true;maxlength:30" data-jc-value="'Peter'">OLD</div>
  • jComponent follows HTML 5 standards

All attributes for components

<element data-jc="" />
<!--
    Required. This attribute needs to contain a component name. The value can be
    empty and then jComponent renders a raw content according to
    the "data-jc-path".

    IMPORTANT: "data-jc" can contain multiple components like this:
    <body data-jc="binder,exec,avatar">

    +v14.0.0 supports versioning
    <div data-jc="datetime@1">
    <div data-jc="textbox@2">Textbox v2</div>
    <div data-jc="textbox">Textbox</div>

    +v14.0.0 supports multiple versions declaration
    IMPORTANT: jComponent tries to load a comopnent version in order (other versions are skipped)
    <div data-jc="datetime|datetime@1|datetime@2">

-->

<element data-jc-path="" />
<!--
    Optional. The attribute can contain a binding path for two-way
    binding values between the component and model.
-->

<element data-jc-type="" />
<!--
    Optional. The attribute can contain an output data-type for
    the component. Internally jComponent supports "date" and "number".
-->

<element data-jc-id="" />
<!--
    Optional. This attribute is a custom component indetificator.
    According to the ID you can find the specific component. E.g.:
    FIND('#id').remove();
-->

<element data-jc-class="" />
<!--
    Optional. This attribute can contain a CSS class which is toggled
    when the component is ready.
-->

<element data-jc-import="" />
<!--
    Optional. This attribute has to contain a valid URL address and
    the element needs to contain "data-jc" attribute. jComponent
    downloads the content of the component and evaluates it.
    The content is downloaded the only one time and the component
    can be used multiple times.

    E.g.:

    // First component
    <div data-jc="editor" data-jc-import="/editor.html"></div>

    // Second component
    <p data-jc="editor" data-jc-import="/editor.html"></p>

    // etc..
-->

<element data-jc-init="" />
<!--
    Optional. The attribute can contain a path to the function. This function
    will be executed when the component is initialized. E.g.

    <... data-jc-init="init_textbox"></div>

    function init_textbox(component) {

    }
-->

<element data-jc-template="" />
<!--
    Optional. The attribute can contain a valid URL address to the
    component template. jComponent automatically downloads the content
    and sends it into the `self.make(template)` delegate.
    IMPORTANT: if the value starts with `.` or `#` or contains `[` then
    jComponent uses a DOM selector and the template will be the
    content of HTML selector.
-->

<element data-jc-noscope="true" />
<!--
    Optional. This attribute can disable scope.
-->

<element data-jc-value="" />
<!--
    Optional. This attribute can contain a default value for the "data-jc-path".
    IMPORTANT: the value is evaluated as JavaScript.

    Value: {Object}
    <element data-jc="a-component-string" data-jc-value="{ name: 'jComponent', tags: ['node.js', 'jComponent', 'total.js'] }">
    ...
    </element>

    Value: {String}
    <element data-jc="a-component-string" data-jc-value="'String value'">
    ...
    </element>

    Value: {Number}
    <element data-jc="a-component-number" data-jc-value="10">
    ...
    </element>

    Value: {Boolean}
    <element data-jc="a-component-boolean" data-jc-value="false">
    ...
    </element>

    Value: {Date}
    <element data-jc="a-component-date" data-jc-value="new Date()">
    ...
    </element>
-->

<element data-jc-released="true" />
<!--
    Optional. Can set default "release" state.
-->

<element data-jc-url="/form.html" />
<element data-jc-url="/formcached.html" data-jc-cache="5 minutes" />
<!--
    Optional. Downloads HTML content into this element.
    Can be defined without "data-jc" attribute.
-->

Configuration

The configuration of components is a very important part of reusable components. Configuration is stored in data-jc-config="key1:value1;key2:value2" attribute.

Good to know:

  • Number is parsed as Number, e.g. key:123 or key:123.45
  • Boolean is parsed as Boolean, e.g. key:false or key:true
  • Environment values are parsed automatically, e.g. key:[key_in_env]
  • if your value contains a : colon you can encode it via backslash \:
  • you don't have to encode absolute URL addresses, e.g. key:https://www.google.sk will work without encoding
  • IMPORTANT: configuration can be binded with path.to.variable like this: data-jc-config="=path.to.variable"

Each component needs to implement the delegate below:

// This delegate is executed for each key from configuration
self.configure = function(key, value, init, prev) {

    // @key {String}
    // @value {String/Number/Boolean/Object}
    // @init {Boolean} Is initialization?
    // @prev {String/Number/Boolean/Object} A previous value

    switch (key) {
        case 'required':
            self.tclass('required', value);
            break;
        case 'icon':
            self.find('.fa').rclass2('fa-').aclass('fa fa-' + value);
            break;
    }

};

Processed component can be reconfigured via:

  • self.reconfigure('key3:value3;key4:value') or
  • self.reconfigure({ key3: 'value3', key4: 'value4' })
  • Quick tip: SETTER('#mytextboxid', 'reconfigure', 'required:false')

Reconfiguration doesn't cancel previous configuration, but it extends it.

Data Binding

Data Binding performs two way Data Binding between a component and a model.

  • data-jc-path="path.in.window.scope" creates a listener on the path and the component will react on its change
  • delegate component.setter processes data-binding on the component side
  • these global methods perform a change in the model and notify all components SET(), PUSH(), INC(), EXTEND() and UPDATE()
<div data-jc="textbox" data-jc-path="myform.name"></div>
<div data-jc="json" data-jc-path="myform"></div>
<div data-jc="textbox" data-jc-path="user.name"></div>

<script>
    SET('myform.name', 'Peter');
    // This method assigns a value "Peter" according to the path "window.myform.name"
    // "window.myform.name" will contain "Peter"
    // Components: textbox + json will be notified because they listen on the modified path
    // Component textbox with "user.name" won't be notified because it's listening on another path

    SET('user', { name: 'Peter' });
    // This method assigns a value {Object} according to the path "window.user"
    // "window.user" will contain the {Object}
    // Only component textbox with "user.name" will contain a modified value
</script>

+v14.2.0 supports inline helpers:

<div data-jc="textbox" data-jc-path="myform.name --> value.toUpperCase()"></div>
<div data-jc="" data-jc-path="product.price --> (value, path) => value.format(2)"></div>