DEV Community

Cover image for Total.js — UI library
Pavol
Pavol

Posted on

Total.js — UI library

Element ui-plugin

Element ui-plugin has to be declared in HTML code and can be used for simplifying paths for components, ui-binds or other nested ui-plugins. The plugin can contain attributes path and config.

<ui-plugin path="userform" config="aclass:1;class:form">
  <ui-component name="input" path="?.email" config="type:email;required:1" default="'@'">Email</ui-component>
  <ui-component name="input" path="?.address.country" config="required:1" default="'Slovakia'">Country</ui-component>
  <ui-component name="input" path="?.address.city" config="required:1" default="'Banská Bystrica'">City</ui-component>
</ui-plugin>
Enter fullscreen mode Exit fullscreen mode

Here you can see the declared ui-plugin with path userform and in the configuration are keys aclass (this configuration key will add value from the path as a class to the HTML element) and class. With this configuration setting, classes userform and form will be added to the HTML element of the ui-plugin.

Defined code with ui-plugin displayed on the web

As you can see, all components used in our plugin have question marks in the attribute path. This question mark will be replaced with the path from the plugin. So the final paths will be userform.email, userform.address.country and userform.address.city (userform is stored in the window object). The model for this plugin will look like this:

{
    "address": {
        "city": "Banská Bystrica",
        "country": "Slovakia"
    },
    "email": "@"
}
Enter fullscreen mode Exit fullscreen mode

Nested plugins
Plugins also support nested plugins, so we can simplify our code above with another plugin with a path address and then we can shorten paths in our embedded components.

<ui-plugin path="userform" config="aclass:1;class:form">
  <ui-component name="input" path="?.email" config="type:email;required:1" default="'@'">Email</ui-component>
  <ui-plugin path="address">
    <ui-component name="input" path="?.country" config="required:1" default="'Slovakia'">Country</ui-component>
    <ui-component name="input" path="?.city" config="required:1" default="'Banská Bystrica'">City</ui-component>
  </ui-plugin>
</ui-plugin>
Enter fullscreen mode Exit fullscreen mode

As you can see in the updated code, the attribute path in the nested plugin doesn't have to contain a question mark, it will be automatically complemented with a path from the parent plugin. Model will be the same as for our first code example because with this change paths will remain unchanged. Adding of a ui-plugin is not reflected on the web, so it will also remain the same as in our first code example.

Skip parent plugin
In special cases, you might need to skip the parent plugin in nested plugins. It is possible to do that by adding a number after the question mark. In the example I showed here is no need for such use, but for educational purposes, I will modify this example.

<ui-plugin path="userform" config="aclass:1;class:form">
  <ui-component name="input" path="?.email" config="type:email;required:1" default="'@'">Email</ui-component>
  <ui-plugin path="address">
    <ui-component name="input" path="?.country" config="required:1" default="'Slovakia'">Country</ui-component>
    <ui-component name="input" path="?1.city" config="required:1" default="'Banská Bystrica'">City</ui-component>
  </ui-plugin>
</ui-plugin>
Enter fullscreen mode Exit fullscreen mode

As you can see, I added number 1 to the path for the input component for city after a question mark. Now our model will look like this:

{
    "city": "Banská Bystrica",
    "address": {
        "country": "Slovakia"
    },
    "email": "@"
}
Enter fullscreen mode Exit fullscreen mode

In the previous example, path to the city looked like userform.address.city, but by skipping the parent plugin, it will “jump” over the first parent plugin to the second and now path will be userform.city. You can skip more parent plugins with increasing number added to the path attribute.

Isolated plugins
Another special case can be, that we need to use nested plugins in our HTML structure, but we want this plugin to be isolated from parent plugins. We can do that with the configuration key isolated and value true.

<ui-plugin path="userform" config="aclass:1;class:form">
  <ui-component name="input" path="?.email" config="type:email;required:1" default="'@'">Email</ui-component>
  <ui-plugin path="address" config="isolated:true">
    <ui-component name="input" path="?.country" config="required:1" default="'Slovakia'">Country</ui-component>
    <ui-component name="input" path="?.city" config="required:1" default="'Banská Bystrica'">City</ui-component>
  </ui-plugin>
</ui-plugin>
Enter fullscreen mode Exit fullscreen mode

In this case, the address won't be part of userform, but we will get two separate models.

// model for userform
{
    "email": "@"
}

// model for address
{
    "city": "Banská Bystrica",
    "country": "Slovakia"
}
Enter fullscreen mode Exit fullscreen mode

Anonymous plugins
ui-plugin path attribute must contain a path or a question mark. A question mark can be used for anonymous plugins, which can be used for creating independent reusable web parts, where ? will be replaced with a randomly generated string with 15 characters. For example, this string can look like wcqokaxluzo6rvs and this path will be stored in a window object.

JavaScript PLUGIN

PLUGINs are created for dynamic parts, pages, forms, or windows for single page applications and they can be removed when they aren't used. It is recommended to use PLUGIN names with lowercase.

PLUGIN declaration
PLUGIN name has to be string without any white characters. Inside of the PLUGIN, you can define your custom functions, properties, watchers, or events related to PLUGIN but also independent.

PLUGIN('plugin_name', function(exports) {
    // some code
});
Enter fullscreen mode Exit fullscreen mode

It is also possible to declare PLUGIN without defining a name when you use ui-import or jComponent j-Importer to import parts to your application.

I will show you an example of the usage of PLUGIN without defining name a with the j-Importer component. In this case, I want to import the User form to our application.

<ui-component name="importer" path="common.form" config="if:userform;url:/forms/user.html"></ui-component>
Enter fullscreen mode Exit fullscreen mode

If you want to get more information about how jComponent works, please read my previous blog or, for more information about j-Importer in particular, you can do it on the web componentator.com.

PLUGIN example
j-Importer will import the entire html file user.html (defined in the code above). This component can contain styles, html and also script.

Complete content of the file user.html

This code is displayed on the web like this:

user.html displayed on the web

Now we’ll break down each part of the code in sequence.

 .CLASS .buttons { display: flex; }
 .CLASS .custom { height: 40px; padding: 0 20px; color: #fff; background-color: #83c83c; cursor: pointer; font-family: Arial; line-height: 38px; font-size: 14px; border-radius: var(--radius); }</style>
Enter fullscreen mode Exit fullscreen mode

In styles, you can see that I used class .CLASS, word CLASS is in this case reserved word, which will be replaced with userform (same for ~PATH~) in this example and it will be obtained from the j-Importer component declaration. This helps create custom styles for your form because you can use this as a class for a parent element and thus modify styles only for a specific form.

<ui-component name="miniform" path="common.form" config="if:userform;title:User;width:400;reload:?/reload;submit:?/submit;icon:ti ti-user" class="hidden CLASS" plugin="~PATH~">
  <div class="padding">
    <div class="m">
      <ui-component name="input" path="?.email" config="type:email;required:1" default="'@'">Email</ui-component>
    </div>
    <ui-plugin path="address">
      <div class="m">
        <ui-component name="input" path="?.country" config="required:1" default="'Slovakia'">Country</ui-component>
      </div>
      <div class="m">
        <ui-component name="input" path="?.city" config="required:1" default="'Banská Bystrica'">City</ui-component>
      </div>
    </ui-plugin>
    <div class="buttons">
      <div class="exec custom mr5" data-exec="?/complete">Complete</div>
      <div class="exec custom" data-exec="?/complete_email">Complete email</div>
    </div>
  </div>
  <nav>
    <ui-component name="validate" path="?">
      <button name="submit">Submit</button>
      <button name="cancel">Cancel</button>
    </ui-component>
  </nav>
</ui-component>
Enter fullscreen mode Exit fullscreen mode

This is the HTML code for our form. I chose j-MiniForm and you can see, that I used both words CLASS and ~PATH~ here for example usage, but you can choose just one of them.

The part I would like to point out in this code are two buttons with class exec (there is used component j-Exec), and attribute data-exec. It refers to a function defined in PLUGIN. As you can see name of a function is in form ?/complete, a question mark will be replaced in this case with userform and therefore it will invoke a function from a particular PLUGIN.

PLUGIN(function(exports) {
  exports.submit = function() {
    var model = exports.model;
    console.log('Model -->', model);
  };

  exports.complete = function() {
    var obj = { email: 'total@avengers.com', address: { country: 'USA', city: 'New York' }};
    exports.set(obj);
  };

  exports.complete_email = function() {
    exports.set('email', 'avengers@totaljs.com');
  };
});
Enter fullscreen mode Exit fullscreen mode

Here you can see, that I used PLUGIN without declaring the name. In this case, the name will be automatically complemented by j-Importer and method ADAPT(). There are a few options for how this will be achieved, a name can be taken from the configuration of j-Importer from if or path keys or it can be found as the first plugin name from HTML code (HTML code will be transformed to string and value from first found plugin name will be used).

All these declarations will have the same result in our example:

// PLUGIN('CLASS', function(exports) {
// PLUGIN('~PATH~', function(exports) {
// PLUGIN('userform', function(exports) {
PLUGIN(function(exports) {
  // some code
}
Enter fullscreen mode Exit fullscreen mode

exports.model
You can define functions in the plugin as part of the exports object. I defined some custom functions to explain it more. The first function is for submitting our form and it will be invoked by the button Submit, because we set it in the config of our j-MiniForm. In function submit I wanted to show you how to access the model. The complete model, which is saved on the path userform is accessible in the exports.model. You can also access model with exports.data, which is an alias for exports.model, or with exports.form but this will also reset the state of components, which is used in forms. I added console.log for demonstration.

Demonstration of exports.submit function

exports.set()
Also as you can see, there are two green buttons at the bottom of our form. I added them there to to explain you another functionality you can use in PLUGINS and that is exports.set(), which is similar to the SET() function for PLUGINS. I used this function in both custom functions (complete and complete_email) but with little difference.

Function complete

var obj = { email: 'total@avengers.com', address: { country: 'USA', city: 'New York' }};
exports.set(obj);
Enter fullscreen mode Exit fullscreen mode

When you invoke exports.set() with only one argument (in this case object obj), this object will be set as value to our path — userform (because we are in PLUGIN userform). So after invoking function the complete our model at window.userform will be:

{ 
  "email": "total@avengers.com",
  "address": { 
    "country": "USA",
    "city": "New York"
  }
}
Enter fullscreen mode Exit fullscreen mode

Demonstration of function complete

Function complete_email

exports.set('email', 'avengers@totaljs.com');
Enter fullscreen mode Exit fullscreen mode

In this case, you can see two arguments in function exports.set(). The first argument is a path, where we want to set a new value. In this example path, where we will set a new value is window.userform.email (because we are in PLUGIN userform), and the rest of the model will remain the same. So after invoking function complete_email our model at window.userform will be:

{
  "address": {
    "city": "Banská Bystrica",
    "country": "Slovakia"
  },
  "email": "avengers@totaljs.com"
}
Enter fullscreen mode Exit fullscreen mode

Demonstration of function complete_email

These functions can be used only in PLUGINS, there are also equivalents in global functions, that can be also used in PLUGINS to influence paths outside of a particular PLUGIN.

Top comments (0)