npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

plastiq

v1.33.0

Published

A fast, feature rich and simple framework for building dynamic browser applications.

Downloads

100

Readme

plastiq npm version npm Build Status

A fast, feature rich and simple framework for building dynamic browser applications.

  1. There is one model for the whole page, this model is stateful, object-oriented and free of framework elements.
  2. There is a render function that renders the model into HTML every time the model changes, but only applies the differences from the last render to the DOM.

Of course, the model can be as simple or sophisticated as you need, and you can refactor the render function into render functions or polymorphic render methods, or whatever works best. It's just javascript, you know what you're doing.

Plastiq is influenced by Facebook's React and uses virtual-dom for the DOM patching.

Sponsored by Browserstack.

An Example

in JSX, using babel

/** @jsx plastiq.jsx */
var plastiq = require('plastiq');

class App {
  render() {
    return <div>
      <label>what's your name?</label>
      <input type="text" binding={[this, 'name']} />
      <div>hi {this.name}</div>
    </div>;
  }
}

plastiq.append(document.body, new App());

in JS

var plastiq = require('plastiq');
var h = plastiq.html;

function render(model) {
  return h('div',
    h('label', "what's your name?"), ' ',
    h('input', {type: 'text', binding: [model, 'name']}),
    h('div', 'hi ', model.name)
  );
}

plastiq.append(document.body, render, {name: ''});

Try it on requirebin.

install

npm

npm install plastiq

Use either with browserify:

var plastiq = require('plastiq');

Or from HTML, first create a symlink:

ln -s node_modules/plastiq/plastiq.js public/plastiq.js

Then

<script src="plastiq.js"></script>

cdn

Plastiq on CDNJS:

<script src="https://cdnjs.cloudflare.com/ajax/libs/plastiq/<version>/plastiq.min.js"></script>

size

  • plastiq.js: 69K
  • plastiq.min.js: 28K
  • plastiq.min.js.gz: 9.1K

browser support

  • IE 9, 10, 11
  • Edge
  • Safari
  • Safari iOS
  • Firefox
  • Chrome

Other browsers are likely to work but aren't routinely tested.

sister projects

JSX

JSX is well supported, and can perform faster than using non-JSX plastiq.html() as JSX is slightly less flexible expression of VDOM, (you can't specify selectors as you can using plastiq.html().)

Insert the following lines at the top of your .jsx file:

/** @jsx plastiq.jsx */
var plastiq = require('plastiq');

Features

Rendering the View

The render function takes a model object and returns a virtual DOM fragment. The render function should not modify the model, just return the view. It should not be relied upon to manipulate any state, this is because it can be called very frequently during user interaction, or very rarely if ever if the browser tab is not in focus.

function render(model) {
  return h('span', 'hi ', model.name);
}

Use Selectors

Use tagname, with any number of .class and #id.

h('div.class#id', 'hi ', model.name);

Spaces are taken to be small hierarchies of HTML elements, this will produce <pre><code>...</code></pre>:

h('pre code', 'hi ', model.name);

Add HTML Attributes

h('span', { style: { color: 'red' } }, 'name: ', model.name);

virtual-dom uses JavaScript names for HTML attributes like className, htmlFor and tabIndex. Plastiq supports these, but also allows regular HTML names so you can use class, for and tabindex. These are much more familiar to people and you don't have to learn anything new.

Non-standard HTML attribtes can be placed in the attributes key:

h('span', {attributes: {'my-html-attribute': 'stuff'}}, 'name: ', model.name);

Keys

Plastiq (or rather virtual-dom) is not clever enough to be able to compare lists of elements. For example, say you render the following:

h('ul',
  h('li', 'one'),
  h('li', 'two'),
  h('li', 'three')
)

And then, followed by:

h('ul',
  h('li', 'zero'),
  h('li', 'one'),
  h('li', 'two'),
  h('li', 'three')
)

The lists will be compared like this, and lots of work will be done to change the DOM:

<li>one</li>   => <li>zero</li>  (change)
<li>two</li>   => <li>one</li>   (change)
<li>three</li> => <li>two</li>   (change)
                  <li>three</li> (new)

If we put a unique key (String or Number) into the attributes, then we can avoid all that extra work, and just insert the <li>zero</li>.

h('ul',
  h('li', {key: 'one'}, 'one'),
  h('li', {key: 'two'}, 'two'),
  h('li', {key: 'three'}, 'three'))

And:

h('ul',
  h('li', {key: 'zero'}, 'zero'),
  h('li', {key: 'one'}, 'one'),
  h('li', {key: 'two'}, 'two'),
  h('li', {key: 'three'}, 'three'))

It will be compared like this, much faster:

                  <li>zero</li>  (new)
<li>one</li>   => <li>one</li>
<li>two</li>   => <li>two</li>
<li>three</li> => <li>three</li>

Its not all about performance, there are other things that can be affected by this too, including CSS transitions when CSS classes or style is changed.

Raw HTML

Insert raw unescaped HTML. Be careful! Make sure there's no chance of script injection.

function render(model) {
  return h.rawHtml('div',
    {style: { color: 'red' } },
    'some dangerous <script>doTerribleThings()</script> HTML');
}

This can be useful for rendering HTML entities too. For example, to put &nbsp; in a table cell use h.rawHtml('td', '&nbsp;').

Classes

  • an string, e.g. 'item selected'.
  • an array - the classes will be all the items space delimited, e.g. ['item', 'selected'].
  • an object - the classes will be all the keys with truthy values, space delimited, e.g. {item: true, selected: item.selected}.
h('span', { class: { selected: model.selected } }, 'name: ', model.name);

Data Attributes

h('div', {'data-stuff': 'something'});

or

h('div', {dataset: {stuff: 'something'}});

Responding to Events

Pass a function to any on* event handler.

When the event handler has completed the view is automatically re-rendered.

If you return a promise, then the view is also re-rendered when the promise resolves.

function render(model) {
  return h('div',
    h('ul',
      model.people.map(function (person) {
        return h('li', person.name);
      })
    ),
    h('button', {
      onclick: function () {
        model.people.push({name: 'Person ' + (model.people.length + 1)});
      }
    }, 'Add Person')
  );
}

plastiq.append(document.body, render, { people: [] });

Try it on requirebin

Window Events

You can attach event handlers to window, such as window.onscroll and window.onresize. Return a h.window() from your render function passing an object containing the event handlers to attach. When the window vdom is shown, the event handlers are added to window, when the window vdom is not shown, the event handlers are removed from window.

E.g. to add an onresize handler:

function render() {
  return h('div',
    'width = ' + window.innerWidth + ', height = ' + window.innerHeight,
    h.window({ onresize: function () {console.log('resizing');} })
  );
}

Try it on requirebin

Binding the Inputs

This applies to textarea and input types text, url, date, email, color, range, checkbox, number, and a few more obscure ones. Most of them.

The binding attribute can be used to bind an input to a model field. You can pass either an array [model, 'fieldName'], or an object {get: function () { ... }, set: function (value) { ... }}.

function render(model) {
  return h('div',
    h('label', "what's your name?"), ' ',
    h('input', {type: 'text', binding: [model, 'name']}),
    h('div', 'hi ' + model.name)
  );
}

plastiq.append(document.body, render, { name: '' });

Try it on requirebin.

Radio Buttons

Bind the model to each radio button. The buttons can be bound to complex (non-string) values.

var blue = { name: 'blue' };

function render(model) {
  return h('div',
    h('input.red', {
      type: 'radio',
      name: 'colour',
      binding: [model, 'colour'],
      value: 'red'
    }),
    h('input.blue', {
      type: 'radio',
      name: 'colour',
      binding: [model, 'colour'],
      value: blue
    }),
    ' ',
    h('code', JSON.stringify(model.colour))
  );
}

plastiq.append(document.body, render, { colour: blue });

Try it on requirebin.

Select Dropdowns

Bind the model onto the select element. The options can have complex (non-string) values.

var blue = { name: 'blue' };

function render(model) {
  return h('div',
    h('select',
      {binding: [model, 'colour']},
      h('option', {value: 'red'}, 'red'),
      h('option', {value: blue}, 'blue')
    ),
    ' ',
    h('code', JSON.stringify(model.colour))
  );
}

plastiq.append(document.body, render, { colour: blue });

Try it on requirebin.

File Inputs

The file input is much like any other binding, except that only the binding's set method ever called, never the get method - the file input can only be set by a user selecting a file.

function render(model) {
  return h('div',
    h('input',
      {
        type: 'file',
        binding: {
          set: function (file) {
            return new Promise(function (result) {
              var reader = new FileReader();
              reader.readAsText(file);

              reader.onloadend = function () {
                model.filename = file.name;
                model.contents = reader.result;
                result();
              };
            });
          }
        }
      }
    ),
    h('h1', model.filename),
    h('pre', h('code', model.contents))
  );
}

plastiq.append(document.body, render, {
  filename: '(no file selected)',
  contents: ''
});

Try it on requirebin.

Storing temporary state to prevent immediate model binding

When the user input represents an invalid model value, you may not want to bind that value on to the model immediately. For example, when converting the input value to the model type would discard the user's temporary input value. In these cases, you can use a custom transformer, like this:

var mustBeAnInteger = {
  view: function(model) {
    // convert the model value into the user input value
    return model.toString();
  },
  model: function(view) {
    // convert the user input value into the model value, or throw an error
    if (!/^\d+$/.test(view)) {
      throw new Error('Must be an integer');
    } else {
      return Number(view);
    }
  }
}
function render(model) {
  return h('div',
    h('input', {binding: [model, 'age', mustBeAnInteger]})
  );
}

plastiq stores the temporary state in an object called _plastiqMeta on your model object, until the converter's model(view) function stops throwing errors.

Components

Components can be used to track the life-time of some HTML. This is usually helpful if you want to install jQuery plugins.

The plastiq.html.component() allows you to respond to when the HTML is added, updated and removed.

function render(model) {
  return h('div',
    model.show
      ? h.component(
          {
            onbeforeadd: function () {
              // you can store state in `this`, and it will
              // be present in subsequent event handlers
              // in fact, the `this` is the same object
              // for each event handler, across all view refreshes
              this.someProperty = 'some value';
            },

            onadd: function (element) {
              // element is the <div>component contents</div>
              // you may want to add jQuery plugins here
              console.log('added: ', this.someProperty);
            },

            onupdate: function (element) {
              console.log('updated: ', this.someProperty);
            },

            onremove: function (element) {
              console.log('removed: ', this.someProperty);
            }
          },
          h('div', 'component contents')
        )
      : undefined,
    h('div',
      h('label',
        'show component ',
        h('input', {type: 'checkbox', binding: [model, 'show']})
      )
    ),
    h('div',
      h('button', {onclick: function () {}}, 'refresh')
    )
  );
}

plastiq.append(document.body, render, {});

Try it on requirebin.

Components can also be used to render just parts of the page, usually for performance reasons. By returning a component or an array of components from an event handler, only those components will be rendered.

function render(model) {
  var component = h.component(function () {
    return h('div', 'component counter: ', model.counter);
  });
  
  return h('div',
    component,
    h('div', 'page counter: ', model.counter),
    h('div',
      h('button', {
        onclick: function () {
          model.counter++;
          return component;
        }
      }, 'refresh component')
    ),
    h('div',
      h('button', {
        onclick: function () {
          model.counter++;
        }
      }, 'refresh page')
    )
  );
}

plastiq.append(document.body, render, {counter: 0});

Try it on requirebin.

Controllers?

You won't find controllers or components in plastiq like you would in React's React.createClass() and AngularJS's angular.directive() and angular.controller(). This sounds like an omission, but in reality they're simply not needed. Plastiq works with render functions and model objects, the two primary building blocks of JavaScript, so it's refreshingly easy to structure and refactor your application.

Render functions contain event handlers, which act as controllers in other frameworks. Event handlers can either handle events inline, or delegate to methods on the model.

In the example below we have a render function and a renderPerson function. The renderPerson acts as a reusable component for rendering and handling interaction for each person.

function render(model) {
  return h('div',
    h('h3', 'People'),
    h('ol',
      model.people.map(function (person) {
        return renderPerson(model, person);
      })
    ),
    h('button',
      {
        onclick: function () { model.addPerson(); }
      },
      'add')
  );
}

function renderPerson(model, person) {
  return h('li',
    h('input', {type: 'text', binding: [person, 'name']}),
    h('button',
      {
        onclick: function () { model.deletePerson(person); }
      },
      'delete')
  )
}

plastiq.append(document.body, render, {
  people: [
    {name: 'Åke'},
    {name: 'آمر'},
    {name: '正'}
  ],

  addPerson: function () {
    this.people.push({name: "somebody"});
  },

  deletePerson: function (person) {
    var i = this.people.indexOf(person);

    if (i >= 0) {
      this.people.splice(i, 1);
    }
  }
});

Try it on requirebin.

The model too can contain render methods so you can take advantage of polymorphism. This might be useful, for example, if you want to render a list of different types of widgets. Each object in the list would have its own render function, rendering different HTML depending on the object.

Here we render different types of animal, each with it's own user interface. Each animal object has a render method to render it's own HTML and event handlers.

function render(model) {
  return h('div',
    h('ul',
      model.animals.map(function (animal) {
        return h('li', animal.render(model));
      })
    ),
    h('h1', { style: { color: 'red' } }, model.sound? model.sound + '!': '')
  );
}

plastiq.append(document.body, render, {
  animals: [
    {
      name: 'Harry',
      render: function (model) {
        return [
          h('h3', 'Dog ' + this.name),
          h('button',
            {
              onclick: function () {
                return model.makeSound('woof');
              }
            },
            'bark'
          )
        ];
      }
    },
    {
      name: 'Bobo',
      render: function (model) {
        return [
          h('h3', 'Lion ' + this.name),
          h('button',
            {
              onclick: function () {
                return model.makeSound('roar');
              }
            },
            'roar'
          )
        ];
      }
    }
  ],
  makeSound: function (sound) {
    var self = this;
    return function (render) {
      self.sound = sound;
      render();

      setTimeout(function () {
        delete self.sound;
        render();
      }, 300);
    }
  }
});

Try it on requirebin.

Not Refreshing

By default the view will refresh after an event handler has run, however you can return plastiq.html.norefresh from an event handler to prevent this.

Refreshing the view from the model

Sometimes you want to refresh the view at an arbitrary point, not just after a UI event. For this plastiq views can subscribe to events produced by the model.

Refreshing After a Promise

If load() returns a promise, then you can pass it to refreshAfter and have the page refresh when the promise is complete.

plastiq.html.refreshAfter(load());

If the promise returns a component, or an array of components, then those only components will be refreshed.

Refresh Function

You can refresh the view at any time by getting a refresh function. You can get this function from plastiq.html.refresh, and call it after the view has rendered, i.e. after an AJAX response.

Note that just calling plastiq.html.refresh() will not work, please assign it to the model, or a local variable, then call it.

var refresh = plastiq.html.refresh;

// later

refresh();

This is because plastiq.html.refresh is only set during a render cycle. To call it, make sure you assign it to your model, or a local variable so you can call it later.

function render(model) {
  model.refresh = h.refresh;

  return h('div',
    h('h1', 'my favourite color is'),
    model.color
      ? h('h2', model.color)
      : model.loading
        ? h('h2', 'loading...')
        : h('button', {
            onclick: function () {
              setTimeout(function () {
                model.color = 'red';
                model.refresh();
              }, 1000);
            }
          }, '?')
  );
}

plastiq.append(document.body, render, {});
var refresh = plastiq.html.refresh;
refresh([component]);
  • refresh() refreshes the whole UI for the attachment. (Other attached UIs aren't refreshed.)
  • refresh(component) just refreshes the component. See components.

Refreshify

Sometimes you have an event handler in another framework (e.g. jQuery) that modifies the model. You want to refresh the page after that event handler has executed. You can use plastiq.html.refreshify(handler) to return a new handler that refreshes the page after your event handler has run.

var refreshHandler = h.refreshify(handler, [options]);
  • handler - a function that handles some event, can return a promise.
  • options.refresh - one of these values:
    • true - (the default) refreshHandler will refresh on return, and on promise fulfil if it returns a promise.
    • false - refreshHandler will be just handler so no refresh will happen if you call it.
    • 'promise' - refreshHandler will only refresh if it returns a promise and only after the promise is fulfilled.
  • options.component - only refresh this component

Binding

You can customise how bindings refresh the page by using plastiq.html.binding().

var binding = plastiq.html.binding(binding, options);
  • binding - an array [model, 'property'], or a binding object {get(), set(value)}.
  • options - options that are passed directly to refreshify.

Performance

Plastiq is usually very fast. It's based on virtual-dom which has excellent performance, several times faster than React. See these benchmarks. However, if you have very large and interactive pages there are several strategies you can employ to speed things up.

  • Consider only rendering a part of the page on certain events. For this, you can use a component for the portion of the page you want to refresh, then return a component or an array of components from the event handler.
  • Consider using key attributes for large dynamic lists of elements. Key attributes allow the diffing engine to spot differences inside lists of elements in some cases massively reducing the amount of DOM changes between renders.
  • For form inputs with bindings, especially text inputs that can refresh the page on each keypress, consider using plastiq.html.binding() to not refresh, or only refresh a component.
  • Consider using a component with a cacheKey, to have finer control over when the component re-renders. You can reduce the total render time by not rendering portions of the page that don't change very often. When the cacheKey is changes from one render to the next, the component will be re-rendered. When it doesn't change, the component won't be re-rendered.
  • Consider explicitly rendering a component when the model changes. You can set the cacheKey to something that never changes, such as just true, then use plastiq.html.refresh(component) to render it.

Server-side Rendering

You can render plastiq components on the server-side using vdom-to-html:

var h = require('plastiq').html;
var vdomToHtml = require('vdom-to-html');

var vdom = h('html',
  h('head',
    h('link', {rel: 'stylesheet', href: '/style.css'})
  ),
  h('body',
    h('h1', 'plastiq!')
  )
);

var html = vdomToHtml(vdom);
console.log(html);

Rendering will work with event handlers, components, etc.

The result, however, is plain static HTML without any event handlers or jQuery plugins attached. If you want to render plastiq views on the server and have them fully interactive in the browser, you'll need to re-attach the client-side plastiq view with the server-side rendered view. These rough steps should guide you:

  1. Have the server render the HTML from a model with plastiq.
  2. Have the server embed the model in the page, placing it into a global variable inside a <script> tag.
  3. Have the client-side JS load that model and re-render the vdom without event handlers, this should then be identical to the vdom produced by the server.
  4. Have the client-side JS render the same model, but this time with event handlers.
  5. Compare the two DOMs, without and with event handlers, producing the changes necessary to insert the event handlers into the existing DOM.
  6. Have hope.

Common Errors

Outside Render Cycle

You cannot create virtual-dom event handlers outside a render function

This usually happens when you try to create virtual dom outside of a render function, which is ok, but if you try to add event handlers (onclick etc, or otherwise have attributes set to functions) then you'll see this error. This is because outside of the render cycle, there's no way for the event handlers to know which attachment to refresh - you could have several on a page at once.

Another cause of this error is if you have more than one instance of the plastiq module loaded. This can occur if you have an NPM listing like this:

[email protected] /Users/bob/dev/my-app
├── [email protected]
├── [email protected]
│ ├── [email protected]

With my-plastiq-component depending on another plastiq. Better to have my-plastiq-component have a peerDependency on plastiq, allowing it to use the plastiq under my-app.

Refresh Outside Render Cycle

Please assign plastiq.html.refresh during a render cycle if you want to use it in event handlers

This can occur if you use plastiq.html.refresh, or h.refresh outside of a render cycle, for example, in an event handler or after a setTimeout. This is easily fixed, take a look at Refresh Function.

API

Rendering the Virtual DOM

var vdomFragment = plastiq.html(selector, [attributes], children, ...);
  • vdomFragment - a virtual DOM fragment. This will be compared with the previous virtual DOM fragment, and the differences applied to the real DOM.
  • selector - (almost) any selector, containing element names, classes and ids: tag.class#id, or small hierarchies pre code.
  • attributes - (optional) the attributes of the HTML element, may contain style, event handlers, etc.
  • children - any number of children, which can be arrays of children, strings, or other vdomFragments.

The binding Attribute

Form input elements can be passed a binding attribute, which is expected to be either:

  • An array with two items, the first being the model and second the field name, the third being an optional function called on the input when set, for example to convert a string into a number use Number.

    [object, 'fieldName', convert]
  • object - an object

  • fieldName - the name of a field on object

  • convert (optional) - a function called on the value when setting the model, i.e. model.field = convert(value).

  • An object with two methods, get and set, to get and set the new value, respectively.

    {
      get: function () {
        return model.property;
      },
      set: function (value) {
        model.property = value;
      }
    }

Event Handler on* Attributes

Event handlers follow the same semantics as normal HTML event handlers. They have the same names, e.g. onclick, onchange, onmousedown etc. They are passed an Event object as the first argument.

When event handlers complete, the entire page's virtual DOM is re-rendered. Of course only the differences will by applied to the real DOM.

Promises

If the event handler returns a Promise, then the view is re-rendered after the promise is fulfilled or rejected.

Raw HTML

Careful of script injection attacks! Make sure the HTML is trusted or free of <script> tags.

var vdomFragment = plastiq.html.rawHtml(selector, [attributes], html);
  • selector - (almost) any selector, containing element names, classes and ids. E.g. tag.class#id
  • attributes - (optional) the attributes of the HTML element, may contain style, event handlers, etc.
  • html - the element's inner HTML.

Components

var component = plastiq.html.component([eventHandlers], vdomFragment | renderFunction);
  • eventHandlers - object containing:
    • function onbeforeadd() - invoked before the component is rendered for the first time, before renderFunction. This is a good place to setup state for the component.

    • function onadd(element) - invoked after the component has been rendered for the first time, the element being the top-most DOM element in the component.

    • function onupdate(element) - invoked after the component has been re-rendered, element being the top-most DOM element in the component.

    • function onremove(element) - invoked after the component has been removed from the DOM, element being the top-most DOM element in the component.

    • function on(eventType, handler) - invoked during rendering to wrap any event handlers. This may be useful to add global error handlers, always return the component for rendering performance, or just generally spy on events.

      on: function (event, handler) {
        if (event == 'click') {
          return function () {
            console.log('firing a click event handler');
            return handler.apply(this, arguments);
          }
        } else {
          return handler;
        }
      }
    • detached - a boolean indicating that the DOM element is moved during the onadd event. Defaults to false. Some jQuery components, especially dialogs, move the DOM element to another part of the DOM to aid in styling. If this is the case, use detached: true and plastiq will still be able to track it.

    • cacheKey - if truthy, the component will only update if it's different from the previous rendering of the component. If falsey, then the component will re-render normally with everything else.

    • any other fields you want to access from the handlers.

      The event handlers are all invoked with the same this, within the lifetime of the component. This means you can store state between events.

  • vdomFragment - the vdom fragment to render as the component.
  • renderFunction - a function that returns a vdom fragment of the component. This allows the component to be returned from event handlers to be refreshed independently from the rest of the page.
  • component - a component which can be returned from any render function. With the renderFunction argument, this can be returned from an event handler to refresh just this component.

Attaching to the DOM

var attachment = plastiq.append(element, render, model, [options]);
var attachment = plastiq.append(element, modelWithRender, [options]);

var attachment = plastiq.replace(element, render, model, [options]);
var attachment = plastiq.replace(element, modelWithRender, [options]);
  • attachment - the instance of the plastiq attachment, see below.
  • element - any HTML element.
    • in the case of plastiq.append the view is added as a child via element.appendChild(view)
    • in the case of plastiq.replace the view replaces element via element.parentNode.replaceChild(view, element)
  • render - the render function, called as render(model), is called initially, then after each event handler. The model is passed as the first argument.
  • modelWithRender - a model with a .render() method. This will be called as model.render() initially, and after each event handler.
  • model - the model.
  • options
    • requestRender - function that is passed a function that should be called when the rendering should take place. This is used to batch several render requests into one at the right time.

      For example, immediately:

      function requestRender(render) {
        render();
      }

      Or on the next tick:

      function requestRender(render) {
        setTimeout(render, 0);
      }

      Or on the next animation frame:

      function requestRender(render) {
        requestAnimationFrame(render);
      }

      The default is requestAnimationFrame, falling back to setTimeout.

      For testing with karma you should pass setTimeout because requestAnimationFrame is usually not called if the browser is out of focus for too long.

Detach

attachment.detach();

Detaches the rendering engine from the DOM. Note that this doesn't remove the DOM, just prevents any plastiq rendering events from modifying the DOM.

Remove

attachment.remove();

Destroys the DOM, running any onremove handlers found in components. This will remove the DOM element.

Development

To get started:

git clone https://github.com/featurist/plastiq.git
cd plastiq
npm install

Some of the scripts below assume that you have node_modules/.bin in your $PATH.

Building

plastiq.js and plastiq.min.js can be build using npm run build, these can then be used in a <script src="..."></script> tag.

Automated Testing

Plastiq is almost 100% tested with karma and mocha. If you're planning to submit a pull request, we kindly ask that you write a test that demonstrates that it works. Without tests, we can't guarantee that your feature will continue to work as we refactor the codebase.

Tests can be found in test/browser/plastiqSpec.js.

You can run karma by running karma start, it will watch your files and rerun the tests every time they change.

Server-side tests can be found in test/server. You can use mocha test/server to run these.

To run all tests use npm test.

Manual Testing

As much as automated testing is amazing, it can never really replace manual or exploratory testing. You may want to experiment with an API or see how plastiq performs in a real project, while making changes to plastiq as you go.

To do this, first go to the plastiq directory and run npm link. This will make plastiq available to other projects.

Then inside your other project run npm link plastiq. When your project has require('plastiq') it will be referring to your local version.

You can then use browserify: browserify myapp.js > myappbundle.js or watchify: watchify myapp.js -dvo myappbundle.js, or amok: amok --compiler babel --browser chrome myapp.js, or beefy: beefy myapp.js. browserify-middleware is worth a look too.

Alternatively, if you just want to compile plastiq.js and reference it in a <script src="..."></script>, you can by running npm run prepublish in the plastiq directory.