Total.js — UI library
Tags: totaljs, javascript, html, frontend
Published: 2024-05-06 18:00:20

## Element ui-plugin
Element [ui-plugin](https://docs.totaljs.com/components/40d03002xw50c/) 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**.

```html
<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>
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4phc46r7etg8dx97fl3d.png)

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:

```javascript
{
    "address": {
        "city": "Banská Bystrica",
        "country": "Slovakia"
    },
    "email": "@"
}
```

**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.

```html
<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>
```

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.

```html
<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>
```

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:

```javascript
{
    "city": "Banská Bystrica",
    "address": {
        "country": "Slovakia"
    },
    "email": "@"
}
```

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.

```html
<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>
```

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

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

// model for address
{
    "city": "Banská Bystrica",
    "country": "Slovakia"
}
```

**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](https://docs.totaljs.com/components/40d03002xw50c/#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.

```javascript
PLUGIN('plugin_name', function(exports) {
    // some code
});
```

It is also possible to declare PLUGIN without defining a name when you use [ui-import](https://docs.totaljs.com/components/40d04002gw50c/) or jComponent [j-Importer](https://componentator.com/components/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.

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

If you want to get more information about how jComponent works, please read my [previous blog](https://dev.to/palo/jcomponent-totaljs-1m26) or, for more information about j-Importer in particular, you can do it on the web [componentator.com](https://componentator.com/components/j-importer/).

**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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ro7kwkwa3phwpg50a2s.png)


This code is displayed on the web like this:


![user.html displayed on the web](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k742rk0mfg0epk9yle6g.gif)

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

```css
 .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>
```
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.

```html
<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>
```

This is the HTML code for our form. I chose [j-MiniForm](https://componentator.com/components/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](https://componentator.com/components/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.

```javascript
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');
  };
});
```

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:

```javascript
// PLUGIN('CLASS', function(exports) {
// PLUGIN('~PATH~', function(exports) {
// PLUGIN('userform', function(exports) {
PLUGIN(function(exports) {
  // some code
}
```

**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](https://docs.totaljs.com/components/40d03002xw50c/#f2c19001rt51c)**. You can also access model with **exports.data**, which is an alias for **exports.model**, or with **[exports.form](https://docs.totaljs.com/components/40d03002xw50c/#f2c1a001rt51c)** 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3e5rc3ywvh0oic8vnrm0.gif)


**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()](https://docs.totaljs.com/components/40d03002xw50c/#f2c1b002rt50c)**, which is similar to the **[SET()](https://docs.totaljs.com/components/40d02001ra51c/#504c6001jc51c)** function for PLUGINS. I used this function in both custom functions (_complete_ and _complete_email_) but with little difference.

**Function complete**

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

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:

```javascript
{ 
  "email": "total@avengers.com",
  "address": { 
    "country": "USA",
    "city": "New York"
  }
}
```


![Demonstration of function complete](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dwq8mrp88olkcuknu4fx.gif)

**Function complete_email**

```javascript
exports.set('email', 'avengers@totaljs.com');
```

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:

```javascript
{
  "address": {
    "city": "Banská Bystrica",
    "country": "Slovakia"
  },
  "email": "avengers@totaljs.com"
}
```


![Demonstration of function complete_email](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kr3wte7qxj4d03m3ef9e.gif)

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.