Git Product home page Git Product logo

oz's Introduction

Oz.js

Oz.js is a lightweight†, simple, declarative templating method with built-in events, two-way bindings, and efficient rendering.

var template = Oz('<div oz-scope="person">' +
                    '<input type="text" oz-val="name" /><br>' +
                    '<button oz-evt-click="save">' +
                  '</div>');

var person = {
  firstName: "Trey",
  lastName: "Griffith",
  name: function () {
    return this.firstName + " " + this.lastName;
  }
};

template.on('change:person', function (key, val) {
  person[key] = val;
});

template.on('save', function () {
  // save person somewhere
});

document.body.appendChild(template.render(person));

// renders:
// <div oz-scope="person">
//   <input type="text" oz-val="name" value="Trey Griffith" /><br>
//   <button oz-evt-click="save">
// </div>

person.firstName = "Terry";

template.update(person);

// updates in place to:
// <div oz-scope="person">
//   <input type="text" oz-val="name" value="Terry Griffith" /><br>
//   <button oz-evt-click="save">
// </div>

It is built on a philosophy of:

  1. taking advantage of the DOM instead of string manipulation
  2. making the minimum number of DOM changes to increase rendering speed
  3. using HTML attributes to make templates easily render-able from the server
  4. being completely agnostic to any other part of the application (i.e. modeling, routing, etc)
  5. letting the application never touch the DOM

† Oz.js core is ~150 lines. When all dependencies are included, it is 29 KB minified, and 8 KB gzipped.

Installation

Using component:

$ component install treygriffith/oz

As a standalone (using the scripts in dist/):

<script src="oz.min.js"></script>

To install Oz along with the commonly used tags, see oz-bundle.

Usage

Creating a template

To create a template, simply call the Oz constructor function on a string of HTML text or directly on a DOM node (it will be cloned for rendering)

var template = Oz('<div></div>');

Rendering a template

To render a template, just call the returned Oz instance's render method with the rendering context. This will return a documentFragment that can be appended to the DOM.

var fragment = template.render();
document.body.appendChild(fragment);

There is also a convenience method attached to the Oz constructor to build and render the template in a single call:

Oz.render('<div></div>');

Oz Tags

You define which actions the template should take using HTML attributes that are registered as Oz tags. While Oz core does not have any tags included, There is a bundle that includes the most common tags. See Extending Oz for more information.

Here's a quick example of what a tag looks like:

var context = {
  name: 'Tobi'
};

Oz.render('<span oz-text="name"></span>', context);

Produces:

<span oz-text="name">Tobi</span>

Updating a template

Oz is completely agnostic to whatever you use to model your data. To let Oz know that your data model has updated, simple call the update method with the new data model. Oz smartly re-renders the template, so updating the entire data model is not as expensive as with other templating libraries.

var template = Oz('<div oz-scope="person"><span oz-text="name"></span></div>');
template.render({ person: { name: "Tobi" } });
// outputs <div oz-scope="person"><span oz-text="name">Tobi</span></div>
template.update({ person: { name: "Fred" } });
// updates existing node to <div oz-scope="person"><span oz-text="name">Fred</span></div>

In a practical sense, you don't want to be manually updating your templates. Instead you should hook up the update function as a listener for change events on your data model. For instance, if you were using Backbone to model your data, you might have a set up like this:

var person = new Backbone.Model({
  firstName: "Jeremy",
  lastName: "Ashkenas"
});

var personTemplate = Oz('<span oz-text="firstName"></span><span oz-text="lastName"></span>');

// register our change listener
person.on('change', function (model) {
  personTemplate.update(model.attributes);
});

// render the initial template
document.body.appendChild(personTemplate.render(person.attributes));
// outputs <span oz-text="firstName">Jeremy</span><span oz-text="lastName">Ashkenas</span>

// update one of the attributes
person.set('firstName', 'J');
// template is now <span oz-text="firstName">J</span><span oz-text="lastName">Ashkenas</span>

Events

Part of Oz's philosophy is to let applications push data into the DOM and receive meaningful events from the DOM without ever interacting with the DOM itself. This allows the application logic to be decoupled from the template, which acts as a View.

Any Oz tag can trigger a template event based on a lower level DOM event. The oz-evt tag allows you to define a DOM event as a key (e.g. click) and a more meaningful event as a value (e.g. save). The template will emit the more meaningful event every time the lower-level DOM event occurs. An example of how this might be used is shown below in Backbone:

var person = new Backbone.Model({
  firstName: "Jeremy",
  lastName: "Ashkenas"
});

var personTemplate = Oz('<input oz-val="firstName"><br><input oz-val="lastName"><br><button oz-evt-click="save">Save</button>');

// listen for save events
personTemplate.on('save', function () {
  person.save();
});

// render the initial template
document.body.appendChild(personTemplate.render(person.attributes));

Two-way Bindings

Oz has an internal method for tags to notify the template that an attribute has changed. This is one case of an Oz event, and the Oz template notifies any listeners of the change event, as it would for any other event. This is used to bind models to changes in the template. This binding is exemplified by the oz-val tag.

Here again, Oz is totally agnostic to how you model your data, you simply have to listen for change events and set your data in whatever way it prefers. Using Backbone:

var person = new Backbone.Model({
  firstName: "Jeremy",
  lastName: "Ashkenas"
});

var personTemplate = Oz('<input oz-val="firstName"><br><input oz-val="lastName">');

// listen for changes to the template
personTemplate.on('change', function (attr, val) {
  person.set(attr, val);
});

// make changes in our model be reflected in the template for true two-way binding
person.on('change', function (model) {
  personTemplate.update(model.attributes);
});

// render the initial template
document.body.appendChild(personTemplate.render(person.attributes));

Getters

When a property being rendered is a function, Oz calls that function. In that way, there is not really a concept of a "getter" in Oz so much as a function that defines a property. This pattern makes it easier to include different displays of data without mangling your templates. For example, in a previous example I templated a firstName and lastName property separately. I could have just as easily defined a fullName function property like so:

var person = {
  firstName: "Trey",
  lastName: "Griffith",
  fullName: function () {
    return this.firstName + " " + this.lastName
  }
};

var personTemplate = Oz('<span oz-text="fullName"></span>');

personTemplate.render(person); // outputs <span oz-text="fullName">Trey Griffith</span>

Extending Oz

Tags

Oz does not include any tags in its core. There are however, a few tags that were developed for a baseline Oz use case, and are included in the Oz Bundle. Those tags are:

  • oz-attr Bind an attribute value to a property
  • oz-each Render each element for each member of an Array
  • oz-evt Propagate events from the DOM to the template
  • oz-if Boolean show/hide
  • oz-scope Scope child nodes to a property
  • oz-text Render a text node
  • oz-val Add a form value, and get notified when they change

See the above libraries for examples of implementing a tag, but in short:

The new tag should expose a plugin function that, when called with the Oz instance or constructor as the only parameter, will add the tag to the Oz instance or constructor. A tag is added by calling the tag method of the instance or constructor. tag takes 3 parameters:

  • name - the attribute to be used in the DOM (e.g. oz-text, or oz-evt-*)
  • render - function responsible for modifying the node. It can accept 4 arguments:
    • el - DOM Node being rendered (e.g. <div oz-scope="person"></div>)
    • val - the value of the attribute given the context (e.g. { name: "Brian" })
    • scope - the scope of the attribute given the context (e.g. "person" )
    • raw - an Object with the raw arguments
      • ctx - the current context (e.g. { person: { name: "Brian" } })
      • name - the name of the HTML attribute (e.g. oz-scope)
      • prop - the value in the HTML attribute (e.g. "person")
      • scope - the scope tree prior to this attribute (e.g. "")

An example plugin function might look like this:

module.exports = function (Oz) {
  Oz.tag('my-tag', render)
}

The render function is called within the context of the Oz instance, so you have access to a number of important utilities:

  • Oz#get: get the value of a property in a context
  • Oz#scope: get the textual representation of a scope tree with a property added
  • Oz#hide: hide element (display: 'none';)
  • Oz#show: unhide element (display: '';)

See the source for more information, as all the default tags are defined this way in lib/tags.js.

Bindings

TODO

oz's People

Contributors

treygriffith avatar

Watchers

Yannick Albert avatar James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.