These are (probably) not the docs you're looking for.

This documentation pertains to Enyo 1.0 and webOS 3.0, both of which shipped on the HP TouchPad and are no longer being actively developed.

The current version of Enyo is Enyo 2, which supports cross-platform development. For documentation, see the Enyo 2 Developer Guide.

For information on the next generation of webOS, see www.openwebosproject.org.

Enyo Basics - From the Bottom Up

This document and its companion, Enyo Basics - Kinds, Components, and Controls, provide an introduction to the conceptual underpinnings of the Enyo framework. Our assumption is that you, as a current (or potential) Enyo developer, are at least somewhat knowledgeable about Web development technologies, so we'll begin by looking at Enyo in relation to both HTML and JavaScript.

Enyo and HTML (Controls)

One fundamental Enyo object is the Control. Controls work a lot like DOM nodes; in fact, each control usually translates directly to a node.

Here we create a trivial control and render it to the document body:

enyo.create({
  content: "Hello World"
}).renderInto(document.body);

This snippet of code produces the following HTML:

<div id="control">Hello World</div>

A Control object is like a DOM node in that you can attach CSS classes and styles to it, and you can choose what kind of node you want to make. (Note that while HTML uses the term "class" to identify CSS classes, that word is reserved in JavaScript, so the Enyo convention is to use "className" instead.)

enyo.create({
  nodeTag: "span",
  className: "a-css-class",
  style: "color: purple;",
  content: "Hello World"
}).renderInto(document.body);

This yields the following HTML:

<span id="control" class="a-css-class" style="color: purple;">Hello World</span>

We can nest controls just like we nest DOM nodes.

enyo.create({
  components: [
      {content: "I'm in a container"},
      components: [
          {content: "I'm in a container that's in the container."}
      ]},
      {content: "I'm in the first container."}
  ]
}).renderInto(document.body);

(Notice that we are defining controls in a block called "components". The Control kind is derived from the Component kind, so a component is a more general object than a control. Thus the components block may contain both Control objects and non-control objects. For more on components and controls, see "Enyo Basics - Kinds, Components, and Controls".)

Why bother with all this JavaScript when it's just generating HTML? Our goal in showing how closely Enyo controls map to HTML is simply to demystify the framework; once we start building applications, we'll find that working with controls rather than HTML will save us a lot of trouble.

For example, one obvious benefit of using controls is that they can encapsulate complex rendering and behavior:

enyo.create({
  components: [
      // button with custom graphics
      {kind: "Button"},
      // input box with special features like hinting and graphic fx
      {kind: "FancyInput"},
      // one-of-many selector with custom graphics
      {kind: "RadioGroup", components: [
          {label: "Alpha"},
          {label: "Beta"},
          {label: "Gamma"}
      ]}
  ]
}).renderInto(document.body);

The snippet above renders something like this:


Enyo and JavaScript (Kinds)

JavaScript natively supports object templating and inheritance using functions and prototypes. Here's an example of classic JavaScript usage:

// an object constructor
MyObject = function() {
  this.data = [];
};

MyObject.prototype.toString = function() {
  return this.data.join(", ");
};

// another object constructor, built on the first one
MySpecialObject = function() {
  MyObject.apply(this, arguments);
};

MySpecialObject.prototype = new MyObject();

MySpecialObject.prototype.toNumber = function() {
  return this.data.length;
};

// Make an instance
mso = new MySpecialObject();

In keeping with its heavily object-oriented nature, Enyo provides a method for generating constructors (object templates) with a compact syntax. Constructors built this way have some special features, and we call them kinds. The method used to create a kind is enyo.kind. Here's an example of enyo.kind in action:

// a kind
enyo.kind({
  name: "MyKind",
  constructor: function() {
      this.data = [];
  },
  toString: function() {
      return this.data.join(", ");
  }
});

// another kind, built on the first one
enyo.kind({
  name: "MySpecialKind",
  kind: "MyKind",
  toNumber: function() {
      return this.data.length;
  }
});

// Make an instance
msk = new MySpecialKind();

(Note: Why do we use the term "kind"? These constructors aren't exactly types or classes, but rather specializations of Object. A similar idea exists in db8, where a db8 record schema is also called a kind. Moreover, JavaScript uses prototypal inheritance and not class-based inheritance, so to use the word "class" in the Enyo context would cause confusion.)

It's important to remember that enyo.kind isn't magic--it's performing the normal steps for generating a constructor, just keeping the boilerplate hidden.

There are several things to note about this example:

  • The name of the kind is specified inside of the property block. This name will become a global variable that references the kind. Putting the name inside the block gives you an easy way to use namespacing. For example, say you write:

    enyo.kind({name: "Super.Special.Kind"});
    
    

    The namespaces Super and Super.Special will be created for you, and Super.Special.Kind will reference the new constructor.

  • Initialization code is placed in a special method called constructor. This is very similar to the body of the MyObject function in the first example. The main difference is that the constructor method is not called when inheriting from a kind (if you look closely at the first example, you can see that MyObject is called to create the prototype for MySpecialObject, which ends up creating an extraneous data array in the MySpecialObject prototype.)

  • To make a new kind that inherits from an old one, specify the old one's name in the new one's kind property. In the example, MySpecialKind is based on MyKind.

All these kinds may start to sound confusing, but it all boils down to one simple idea: whenever we make something, whether a constructor or an instance, we say what kind it's based on. When creating an instance, for example, we might do this:

enyo.create({kind: "aKind"});

(Note: The input for enyo.create is a JavaScript object that describes the object to create. This kind of input is sometimes called a "property block" or "property bag".)

Similarly, to make a new kind based on an existing kind, we could do this:

enyo.kind({kind: "aKind"});

This consistency makes the syntax easy to remember. It's turtles all the way down.