Chapter 3. Components, Controls, and Other Objects

In Chapter 2, I introduced kinds and inheritance. It should come as no surprise that Enyo makes good use of those features by providing a rich hierarchy of kinds you can use and build upon in your apps. In this chapter, I’ll focus on two important kinds that Enyo provides: controls and components. I’ll also touch on some of the other kinds that you’ll need to flesh out your app.

Components

Components introduce one of the most-used features of Enyo apps: the ability to create kinds composed of other kinds. This ability to compose new components from other components is one of the key features that encapsulation allows. Most kinds you’ll use, including the base app kind, will be based upon Component or one of its descendants.

Composition

Composition is a powerful feature that lets us focus on breaking down the functionality of our app into discrete pieces and then combine those pieces together into a unified app. We used this feature in Chapter 1 when we built a traffic light out of three individual lights. Each descendant of Component has a components block that takes an array of component definitions.

We are not restricted to composing within new kinds. Many of the components that Enyo supplies were designed as containers for other components. We’ll cover many of the kinds that are designed to hold other components in Chapter 4. Some kinds, such as Button, weren’t intended to contain other components.

Component Functions

Components introduce create() and destroy() methods to asisst with the component’s lifecycle. These functions can be overridden by kinds that derive from Component to provide extra functionality, such as allocating and deallocating resources. We previously used the create() function to initialize published properties on our Light kind. We can use this feature to create a simple heartbeat object:

enyo.kind({
    name: "Heartbeat",
    events: {
        onBeat: ""
    },
    create: function() {
        this.inherited(arguments);
        this.timer = window.setInterval(enyo.bind(this, "beat"), 1000);
    },
    destroy: function() {
        if(this.timer !== undefined) {
            window.clearInterval(this.timer);
        }
        this.inherited(arguments);
    },
    beat: function() {
        this.doBeat({});
    }
});

Tip

Try it out: jsFiddle.

We used the destroy() function to ensure that we cleaned up the timer we allocated in the create() function. You may also notice that we introduced a new function: enyo.bind(). In all our previous event handlers, Enyo handles making sure the context of the event handlers is set correctly. We’ll need to take care of that ourselves when subscribing directly to non-Enyo events. For more information on binding and why it’s necessary, please see this article on Binding Scope in JavaScript.

Dynamic Components

Up to this point we’ve always created components when a kind is being instantiated. It is also possible to create and destroy components dynamically. Components have a number of functions for interacting with their owned components. You can use createComponent() to create an individual component or create a number of components at once using createComponents(). A component can be removed from an instance of a component by calling its destroy() method. It is also possible to destroy all owned components by calling destroyComponents(). The following example shows creating a component dynamically:

enyo.kind({
    name: "DynamicSample",
    components: [
        { kind: "Button", content: "Click", ontap: "tapped" }
    ],
    tapped: function(inSender, inEvent) {
        this.createComponent({ content: "A new component" });
        this.render();
        return(true);
    }
});

new DynamicSample().renderInto(document.body);

Tip

Try it out: jsFiddle.

New controls are not rendered until requested. Call the render() function on a control to ensure that it and its children are rendered to the DOM.

Controls

Control, a descendant of Component, is the kind responsible for providing the user interface to your apps. A large part of what makes an app an app is the user interface. The Enyo core provides wrappers around the most basic type of controls found natively in browsers. The Onyx library expands upon those basic controls and provides the more specialized elements expected in modern apps.

Controls are important because they map to DOM nodes. They introduce a number of properties and methods that will be important for our apps. By default, controls render into a div element. You can override this behavior by specifying the tag property when defining the control (e.g., tag: "span").

Core Controls

The core visual controls in Enyo are wrappers around the basic elements you can create directly with HTML. Of course, because they’re Enyo controls, they’ll have properties and events defined that make them easy to use within your apps. The core controls include: Button, Checkbox, Image, Input, RichText, Select, and TextArea.

The following code sample creates a simple app with several controls:

enyo.kind({
    name: "ControlSample",
    components: [
        { kind: "Button", content: "Click", ontap: "tapped" },
        { tag: "br"},
        { kind: "Checkbox", checked: true, onchange: "changed" },
        { tag: "br"},
        { kind: "Input", placeholder: "Enter something", onchange: "changed" },
        { tag: "br"},
        { kind: "RichText", value: "<i>Italics</i>", onchange: "changed" }
    ],
    tapped: function(inSender, inEvent) {
        // React to taps
    },
    changed: function(inSender, inEvent) {
        // React to changes
    }
});

new ControlSample().renderInto(document.body);
Core control sample

Tip

Try it out: jsFiddle.

You will note that the controls themselves are unstyled, appearing with the browser’s default style. We’ll see how the Onyx versions of these controls compare next. You may also note that some controls use the content property to set the content of the control. The exceptions to this rule are the text field controls: Input, TextArea, and RichText. These controls use the value property to get and set the text content.

Note

By default, most Enyo controls escape any HTML in their content or value properties. This is to prevent the inadvertent injection of JavaScript from unsafe sources. If you want to use HTML in the contents, set the allowHtml property to true. By default, RichText allows HTML content.

Onyx Controls

The Onyx library (an optional piece of Enyo) includes professionally designed widgets. These controls expand upon the basic set available in the Enyo core. The Onyx controls that correspond to the core controls use the same interface as those core controls:

enyo.kind({
    name: "ControlSample",
    components: [
        { kind: "onyx.Button", content: "Click", ontap: "tapped" },
        { tag: "br"},
        { kind: "onyx.Checkbox", checked: true, onchange: "changed" },
        { tag: "br"},
        { kind: "onyx.InputDecorator", components: [
            { kind: "onyx.Input", placeholder: "Enter something",
              onchange: "changed" }
        ]},
        { tag: "br"},
        { kind: "onyx.InputDecorator", components: [
            { kind: "onyx.RichText", value: "<i>Italics</i>",
              onchange: "changed" }
        ]}
    ],
    tapped: function(inSender, inEvent) {
        // React to taps
    },
    changed: function(inSender, inEvent) {
        // React to changes
    }
});
Onyx control sample

Tip

Try it out: jsFiddle.

As you can see, the Onyx widgets are much more pleasing to look at. With Onyx, we wrapped the text input controls in an InputDecorator. This is a control that allows for additional styling and should be used for all Onyx input controls.

The Onyx library also provides a number of new controls, including Drawer, ProgressBar, TabPanel, and TimePicker, among others. Here’s a sample of some of the new Onyx controls that show off their important properties and events:

enyo.kind({
    name: "OnyxSample",
    components: [
        { kind: "onyx.Toolbar", components: [
            { content: "Toolbar" },
            { kind: "onyx.Button", content: "Toolbar Button" }
        ]},
        { content: "Radio Group" },
        { kind: "onyx.RadioGroup", onActivate: "activated", components: [
            { content: "One", active: true },
            { content: "Two" },
            { content: "Three" }
        ]},
        { content: "Groupbox" },
        { kind: "onyx.Groupbox", components: [
            { kind: "onyx.GroupboxHeader", content: "Groupbox Header" },
            { content: "Groupbox item" }
        ]},
        { content: "ProgressBar" },
        { kind: "onyx.ProgressBar", progress: 25 }
    ],
    activated: function(inSender, inEvent) {
        // React to radio button activation change
    }
});

new OnyxSample().renderInto(document.body);
New Onyx controls sample

Tip

Try it out: jsFiddle.

Functions and Properties

Controls have a number of methods and properties that focus on their special role of interacting with the DOM. These methods include rendered(), hasNode(), and a number of methods for manipulating the DOM.

The first, rendered(), is a method that can be overridden to perform processing that only takes place when the DOM node associated with the control is available. By default, controls are not rendered into the DOM until they are required. In our samples, we have used the renderInto function to tell Enyo to create the nodes associated with the kinds in our examples. As always, be sure to call the inherited() function within rendered().

The second important method is hasNode(). This function allows us to test whether the DOM node for the control exists and to retrieve it, if available. hasNode() will return null if no node is available. This function is most useful when you are creating new controls that will need to manipulate the DOM or for when you want to wrap a widget from another UI library.

The following example shows a naive way to implement a scalable vector graphic (SVG) container object. The only purpose is to show off the rendered() and hasNode() functions:

enyo.kind({
    name: "Svg",
    published: {
        svg: ""
    },
    rendered: function() {
        this.inherited(arguments);
        this.svgChanged();
        // Can only call when we have a node
    },
    svgChanged: function() {
        var node = this.hasNode();
        if(node !== null) {
            node.innerHTML = '<embed src="' + this.svg + '"
                type="image/svg+xml" />';
        }
    }
});

new Svg({
    svg: "http://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg"
}).renderInto(document.body);

Tip

Try it out: jsFiddle.

Other Important Objects

Not all functionality in an app is provided by visible elements. For many apps, there is processing that must be done in the background. Enyo provides a number of objects that handle such processing. These objects include Animator, Ajax, and JsonpRequest.

Animator is a component that provides for simple animations by sending periodic events over a specified duration. Each event sends a value that iterates over a range during the animation time. The following example shows how you could use Animator to change the width of a div:

enyo.kind({
    name: "Expando",
    components: [
        { name: "Expander", content: "Presto",
          style:
          "width: 100px; background-color: lightblue; text-align: center;" },
        { name: "Animator", kind: "Animator", duration: 1500, startValue: 100,
          endValue: 300, onStep: "expand", onEnd: "done" },
        { kind: "Button", content: "Start", ontap: "startAnimator" },
    ],
    startAnimator: function() {
        this.$.Expander.setContent("Presto");
        this.$.Animator.play();
    },
    expand: function(inSender, inEvent) {
        this.$.Expander.applyStyle("width", Math.floor(inSender.value) + "px");
    },
    done: function() {
        this.$.Expander.setContent("Change-O");
    }
});

new Expando().renderInto(document.body);

Tip

Try it out: jsFiddle.

Ajax and JsonpRequest are both objects that facilitate performing web requests. It is worth noting that they are objects and not components. Because they are not components they cannot be included in the components block of a kind definition. We can write a simple example to show how to fetch some data from a web service:

enyo.kind({
    name: "AjaxSample",
    components: [
        { kind: "Button", content: "Fetch Repositories", ontap: "fetch" },
        { name: "repos", content: "Not loaded...", allowHtml: true }
    ],
    fetch: function() {
        var ajax = new enyo.Ajax({
            url: "https://api.github.com/users/enyojs/repos"
        });
        ajax.go();
        ajax.response(this, "gotResponse");
    },
    gotResponse: function(inSender, inResponse) {
        var output = "";
        for(i = 0; i < inResponse.length; i++) {
            output += inResponse[i].name + "<br />";
        }
        this.$.repos.setContent(output);
    }
});

new AjaxSample().renderInto(document.body);

Tip

Try it out: jsFiddle.

In this sample we used the GitHub API to fetch the list of repositories owned by enyojs, the user account that owns the Enyo repository. In the button’s tap handler we created an Ajax object, populated it with the appropriate API url, and set the callback function for a successful response. We could have passed additional parameters for the service when we called the go() function. In general, we would have trapped error responses by calling ajax.error() with a context and error handling function.

Note

The Ajax object performs its request asynchronously so the call to go() does not actually cause the request to start. The request is not initiated until after the fetch() function returns.

A general discussion of when and how to use Ajax and JSON-P are outside the scope of this book.

Warning

By default, Enyo adds a random query string onto Ajax requests to prevent aggressive browser caching. This can interfere with some web services. To disable this feature, add cacheBust: false to the Ajax configuration object.

Community Gallery

The Enyo developers decided to keep the core of Enyo very simple. The additional libraries supplied with Enyo are also similarly focused. No framework can provide all the possible components that users will need. Fortunately, all the features of Enyo that we’ve discussed up to this point mean that it’s very easy to create reusable components. The developers have created a community gallery to make it easy to find and share these reusable components. The gallery includes a variety of components that can be easily dropped in to your apps.

The Enyo Community Gallery

Hopefully you will feel motivated to create new components and share them with the community.

Summary

In this chapter, we explored components and the visual controls that Enyo developers use to make beautiful apps. We explored the various widgets that Onyx has to offer and learned a bit about using them. We also covered some non-visual objects Enyo provides. We’ll take Enyo to the next level by exploring how to arrange these controls in the next chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.20.68