Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/paranoid-software/plugster

jQuery wrapper for those who still consider it as their preferred JavaScript library.
https://github.com/paranoid-software/plugster

es6 event-driven javascript jquery modules wrapper

Last synced: about 1 month ago
JSON representation

jQuery wrapper for those who still consider it as their preferred JavaScript library.

Awesome Lists containing this project

README

        

# Plugster (._.)

Plugster is a wrapper for those who still consider jQuery as their preferred JavaScript library.

The wrapper has the following goals in mind:

1. Prevent indiscriminate use of jQuery-like selection statements.
2. Enable separation of views and controllers into separate files.
3. Enable the use of events as the main communication mechanism between controllers (Plugsters).

A plugster is a standalone view controller, which can access in a clean way to all the HTML elements previously declared as "outlets".

## Elements of a plugster

### The view

It can be any HTML element inside a page (even the body or the head section), but it is commonly a `

` element; and you can have as many "views" as you need inside the same HTML page.

The wrapper recognize a view by the existence of a **data-controller-name** attribute which must contain the plugster name.

```html


My New Web Application



Static label







Static label:





```

In the event of having nested views, the inner boundaries define the field of action for every controller, so an outlet X defined inside the plugster A wich is a child of plugster B can not be accessed by the plugster B.

### The outlets (._.)

We define an outlet as an HTML element which form part of a plugster by beign inside it. They have a **data-outlet-id** attribute which holds its corresponding unique ID at the plugster context.

Every outlet defined in a view can be referenced and accessed by its corresponding controller as a jQuery component without using the $ selector directly, which helps to mantain the code with a little less effort.

### The controller

It is an instance of class extended from the Plugster Base Class. It must implement an initialization method to prepare all the outlets and dependencies in order to work properly.

## List type outlet

Sometimes we need to render complex lists using all kind of HTML elements, in that cases we can use a special outlet with a **data-child-templates** property set to an array of at least one HTML template for every item of the list. In the next example we specified two templates, the first one will be rendered for normal items inside the list, while the second one will be rendered for "deleted" items; in that way we can separate behavior from design and manage to render every item on the list accordingly to its current state.

```html





```

A template is an HTML independent file in which we can design a complex item using other HTML elements (child outlets), which then will be referenced form within the controller using standard programming. The following examples include two independant HTML files used as templates for a list type outlet.

```html


:

```

```html


:


Undelete



```

### Using the child outlets whitin the controller

```javascript
class MyPlugster extends Plugster {

invalidateRatesList(forCurrency) {
let self = this;
self._.selectedCurrencyLabel.text(forCurrency);
self.exchangeRatesSvcs.getLatest(forCurrency).then(function (response) {
self._.ratesList.clear();
Object.keys(response['rates']).map(function (key) {
let rate = response['rates'][key];
let itemAsJson = {};
itemAsJson[key] = rate;
// Here we use the first template specifying its array index 0,
// but we can choose which one to use based for example in the item state,
// using something like ...List.buildListItem(rate.state == 'deleted'? 1 : 0, key ....
let itemOutlets = self._.ratesList.buildListItem(0, key, itemAsJson, {
currencyCodeLabel: {},
valueLabel: {}
});
if (!itemOutlets) return null;
itemOutlets.root.click(function () {
let key = this.dataset['key'];
console.log([key, self._.ratesList.getData(key)]);
});
itemOutlets.currencyCodeLabel.text(key);
itemOutlets.valueLabel.text(rate);
});
});
}

}
```

## Plugster Boilerplate

```javascript
import {Plugster} from '../../libs/plugster/plugster.js';

class WorkingPlugster extends Plugster {

constructor(outlets) {
super(outlets);
}

afterInit() {
// This is our entry point to the plugser,
// here we can start coding the Plugster behavior
// and using the declared outlets.

let self = this;
self._.someOutlet // ....
}

someEvent(data, callback) {
this.registerEventSignature(this.someEvent.name, data, callback);
}

}

let workingPlugster = await new WorkingPlugster({
someOutlet: {}
}).init();

Plugster.plug(workingPlugster);

export {workingPlugster as WorkingPlugster};
```

## Plugster Sample

```javascript
import {Plugster} from '../../libs/plugster/plugster.js';

class MyFirstPlugster extends Plugster {

constructor(outlets) {
super(outlets);
};

afterInit() {

let self = this;

self._.someDropDownOutlet.on('change', function () {
self.notifyValueSelection(this.value);
});

self._.someDropDownOutlet.append(new Option(1, 'Argentina'));
self._.someDropDownOutlet.append(new Option(2, 'Colombia'));
self._.someDropDownOutlet.append(new Option(3, 'Ecuador'));
self._.someDropDownOutlet.append(new Option(4, 'Perú'));
self._.someDropDownOutlet.append(new Option(5, 'Usa'));

}

notifyValueSelection(value) {
this.dispatchEvent(this.valueChanged.name, {value: value})
}

valueChanged(data, callback) {
this.registerEventSignature(this.valueChanged.name, data, callback);
}

}

let myFirstPlugster = await new MyFirstPlugster({
someDropDownOutlet: {}
}).init();

Plugster.plug(myFirstPlugster);

export {myFirstPlugster as MyFirstPlugster};
```

### Events declarations and dispatching

One of the main goals when using Plugster is to enable the adoption of events as the preferred communication mechanism between HTML views or widgets.

Plugster exposes various mechanisms to enable this type of communication, one of them is to implement two methods in the Plugster which desires to expose an event listener and dispatcher:

- **registerEventSignature**, using this method we can add a signature for a defined event, for example:

```javascript
class MyPlugster extends Plugster {

valueChanged(data, callback) {
this.registerEventSignature(this.valueChanged.name, data, callback);
}

}
```

- **dispatchEvent**, using this method we can trigger an event from within a Plugster, for example:

```javascript
class MyPlugster extends Plugster {

notifyValueSelection(value) {
this.dispatchEvent(this.valueChanged.name, {value: value})
}

}
```

Finally, in order to respond to the registered event the simplest way is to invoke the Plugster event registration method passing a callback as follows:

```javascript
import {MyPlugster} from './my-plugster.js';

MyPlugster.valueChanged({}, function (e) {
console.log(e.args.value);
// Do something else with the received data
});
```

#### HTML based events subscription

This is definitely the most clean and powerfull way of using events to communicate between Plugsters. Lets say we have plugsters A, B and C, and we need to communicate some value change on plugster A into B and C; we can do it easily, following the next steps:

- Expose an event registration method on the *source* Plugster A.

```javascript
class MyPlugsterA extends Plugster {

valueChanged(data, callback) {
this.registerEventSignature(this.valueChanged.name, data, callback);
}

}
```

- Declare a listener method on the **target** Plugsters B and C.

```javascript
class MyPlugsterB extends Plugster {

handleValueChange(data) {
console.log(data);
// Do something else with the recived data
}

}

class MyPlugsterC extends Plugster {

handleValueChange(data) {
console.log(data);
// Do something else with the recived data
}

}
```

- Declare the listener at view level on the HTML markup of every interested plugster (in this case plugsters B and C), using the attribute ```data-on-sourceplugstername-sourceeventname=targetListenerMethod```.

```html






```

- That's it !!, every time Plugster A dispatch an event using ```this.dispatchEvent(this.valueChanged.name, {someProperty: someValue})``` both target plugsters will recevice the data dispatched on its listeners.

## Repository Content

In this repository we have 2 versions for the wrapper; the main version is written using ES6 standard and it is located at the "es6" folder. But we also publish a version based on the "Revealing Module Pattern" in case we need to work in a legacy project based on that pattern.

```lang-none
plugster
└───dist
└───src
└───tests
│ .babelrc
│ .gitignore
│ LICENSE
│ package-lock.json
│ package.json
│ readme.md
│ rollup.config.js
```

## CDN thanks to jsdelivr

[https://cdn.jsdelivr.net/gh/paranoid-software/[email protected]/dist/plugster.min.js](https://cdn.jsdelivr.net/gh/paranoid-software/[email protected]/dist/plugster.min.js)

## Library Dependencies and Development Dependencies

The library depends only on jQuery, at the moment we are using jQuery 3.6.0, but we believe it will work with older versions also.

For the development we depend on:

- jest, for testing environment.
- babel, for ES6 module testing.
- rollup & terser, for ES6 module distribution file generation.

## Samples

The samples repository is located at [https://github.com/paranoid-software/plugster-samples](https://github.com/paranoid-software/plugster-samples)

There you can play with one small sample which runs on Flask (python). The sample try to demonstrate the communication betwwen 3 plugsters using a public Currency Rate API.

### Real world sample

We have a static blog hosted at GitHub.io, it was created using Plugster, and it is a more complete demo of the library; it is located at [https://paranoid-software.github.io](https://paranoid-software.github.io), and the code it's available at the GitHub repository located at [https://github.com/paranoid-software/paranoid-software.github.io](https://github.com/paranoid-software/paranoid-software.github.io)