module-js

An extendable class for Web Elements, modules and components

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
module-js
7.0.16 years ago11 years agoMinified + gzip package size for module-js in KB

Readme

Build Status npm version
Module JS
This library allows you standardize each module-like Element throughout your single-page web application. A module can essentially be anything, but it usually represents an independent component (like a modal, or a carousel for example).
The idea behind this library is to provide a common interface between all the modules you may want to use on your site. Which all means less code, less work, and less things to unit test.

Installation

You can install as an npm package if using a build system like Browserify.
npm install module-js --save-dev

NOTE: Node v7 is also recommended if using this package as an NPM module.

Usage

Each module that you have on your site/app will generally be tied to an HTML/DOM element on the page, which you pass to the Module class to create an instance. Then you'll have access to all the helpful methods below. For our example, we'll use an element that represents our modal container (assuming there is already an element in the DOM with the id of "modal-container").
let modalContainer = document.getElementById('modal-container');
let modalModule = new Module(modalContainer, {
   styles: 'http://path/to/modal/styles.css',
   template: 'http://path/to/modal/template.hbs',
   data: 'http://path/to/modal/api/data',
   loadedClass: 'modal-loaded'
});
Once your instance is created, you can call the built-in methods on it. For instance, calling load() on the Modal instance (demonstrated below) will load module along with its template, styles, and even fetch the module's data using the options we've specified above. Once loaded, it will also add the loadedClass specified to the module's modalContainer element.
// load the module
modalInstance.load();

NOTE: since a handlebars file was used as the template option above, calling load will inject the data JSON response from the data` url specified into the handlebars file.

Extending

Out of the box, the Module class will provide a few helpful methods. But the real power comes when you begin to have your components extend it with their own custom implementations. From there, you can override the default abstract methods to add logic that is custom to each specific module. Here is an example of how to create a Modal class that extends the module class and has custom loading functionality.
class MyCustomModule extends Module {
 load () {
     // do some custom stuff before load
     return super.load().then(() => {
         // do some custom stuff after load
     });
 }
}

As you can see above, you can overridethe load method of the parent Module class and call it whenever you want. We recommend that you call the Module class's method via super when overriding any of the methods to ensure you get all internal functionality.

Options

When instantiating a module, you can pass it options:
let modalContainer = document.getElementById('modal-container');
let modalModule = new Module(modalContainer, {
    styles: 'http://path/to/modal/styles.css',
    template: 'http://path/to/modal/template.hbs',
    data: 'http://path/to/modal/api/data',
    loadedClass: 'modal-loaded',
    activeClass: 'modal-shown',
    disabledClass: 'modal-disabled',
    errorClass: 'modal-has-error'
});

| Option | Type | Description | |--------|--------|--------| | styles| Array/String | Array of stylesheet urls to be loaded (or single url string) | template| String|HTMLElement|HTMLTemplateElement | The string url (.html or .hbs files), element, or template element to be injected into the el | data| Object/String | The url to the data or the data object that should be loaded into the module's template | requestOptions| Object | The set of request options that will be passed to the fetch call when fetching data | loadedClass| String| The CSS class that will be applied to the module element when it is loaded | activeClass| String | The CSS class that will be applied to the module element when it is shown | disabledClass| String | The CSS class that will be applied to the module element when it is disabled | errorClass| String | The CSS class that will be applied to the module when error occurs | onLoad| Function | A function that fires when module is loaded | onShow| Function | A function that fires when module is shown | onHide| Function | A function that fires when module is hidden | onEnable| Function | A function that fires when module is enabled | onDisable| Function | A function that fires when module is disabled

Methods

Your module will get some useful methods whenever an instance of it is created.

load()

Your module instance will have a .load() method when it is instantiated. When called, it will load all templates, styles, and data passed into the options above, apply your module's loadedClass and, when done, will call your onLoad callback option (if specified).
You can also do some custom load handling tasks. Below you can see how a Carousel class can be created that loads some assets asynchronously when the load() method is called.
```javascript class Carousel extends Module {
load () {
    // code that loads carousel images asynchronously here
    // and return a promise when done
    return super.load();
}
}; let carousel = new Carousel();
// trigger carousel load carousel.load();
### waitForTransition()

Let's say you have the problem (that many of us have) where you need to detect the completion of your module element's
 [CSS transition](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Using_CSS_transitions)
in javascript before your code can continue. Given the following CSS...


```css
.animate {
    transition-property: background-color;
    transition-duration: 100ms;
    transition-timing-function: ease-out;
}

You can call the waitForTransition method to wait until your Module finishes its transition before doing other things in your javascript code. Like so:
var modal = new Module(document.getElementByTagName('div')[0]);
modal.classList.add('animate'); // start transition
modal.waitForTransition().then(() => {
    // 100 milliseconds later...
    console.log('transition complete!');
});

show()

The show() method can be called when you want to set your module to its "active" state. It adds the css activeClass to your Module element. If you have css that transitions your element when the activeClass is applied, you can utilize the returned promise to wait until after the element has transitioned.
let myModule = new Module(document.getElementById('my-module'));
myModule.show().then(function () {
    // module has finished animating into its active state!
})

hide()

The hide() method can be called when you want to set your module to its "inactive" state, which essentially just removes the activeClass. If there any css transitions that take place when the activeClass is removed, you can use the promise it returns to wait until the animation completes.
let myModule = new Module(document.getElementById('my-module'));
myModule.hide().then(function () {
    // module has finished animating into its inactive state!
})

fetchData()

A method that fetches the data for the Module using the API url supplied as the data option.
let mod = new Module(document.getElementById('my-module'), {
  data: 'path/to/mydata'
});
mod.fetchData().then(function (data) {
    // data has been fetched
})

getStyles()

Loads the css stylesheets supplied via the styles option.
let mod = new Module(document.getElementById('my-module'), {
  styles: ['path/to/my/style.css', 'path/to/my/second/style.css']
});
mod.getStyles().then(function () {
    // styles have been loaded into the head of the current document
})

getTemplate()

Loads the template supplied via the template option. Supports .html and Handlebars (.hbs) files.
let mod = new Module(document.getElementById('my-module'), {
  template: 'path/to/template.hbs'
});
// you can optionally pass data to be injected into the template
let data = {my: 'value'};

mod.getTemplate(data).then(function (html) {
    // the data has been injected in returned html string
    this.el.textContent = html;
})

getClosestAncestorElementByClassName(cssClass, target)

Traverses upwards from the target until finding an ancestor element with the cssClass provided. The target is optional and, if not passed, the Module instance el is used.

disable()

The disable() method can be called to add the css disabledClass to your Module element and set your module to a state in which it can no longer be interacted with.
let myModule = new Module(document.getElementById('my-module'));
myModule.disable();

enable()

The enable() method does the opposite of the disable() method above, removing the disabledClass from your Module element.
```javascript let myModule = new Module(document.getElementById('my-module')); myModule.enable(); ```

error()

The error() method can be called when you want to manually trigger an error in your module, which adds the errorClass to your Module element.
NOTE: the error() method is called automatically when the load() call on your module fails.
let myModule = new Module(document.getElementById('my-module'));
myModule.error();
```

## Properties

### loadStatus

A property that you can query in order to find out where in the loading process a module is.
This will return a string representing one of three states: `notLoaded`, `loading`, `loaded`.


## Sub-Modules

Each module instance provides a `subModules` object to which you can attach additional modules that have extended the
Module class. So when `load()` or `destroy()` is called on the parent module, the same methods will be executed for all
of the `subModules` you've specified, saving you from having to call methods on all of the `subModules` directly. Take
a look at the example below.

```javascript
class MyCustomModule extends Module {
   constructor () {
       // calling super gives you access to the subModules object
       super();
       this.subModules.myModuleId = new Module();
       this.subModules.myModuleId2 = new Module();
       this.subModules.myModuleId3 = new Module();

   }
};
let customMod = new MyCustomModule();

// destroy parent module, which will
// call destroy on all submodules
customMod.destroy();