Fundamentals

Components

An Echo user interface is built by assembling Components into hierarchies. A "Component" is any object which extends from the Echo.Component class, such as Echo.Column, Echo.Label, Echo.Button, Echo.TextField, or Echo.WindowPane. Components may have multiple child Components, but only one parent Component. Hierarchies are created and manipulated using the add() and remove() methods of individual Components. The following code example demonstrates the building of a hierarchy by adding two Buttons to a Column:

var buttonColumn = new Echo.Column();
var button1 = new Echo.Button();
button1.set("text", "First Button");
buttonColumn.add(button1);
var button2 = new Echo.Button();
button2.set("text", "Second Button");
button2.set("background", "#00ff00");
buttonColumn.add(button2);

Properties of client-side applications are set and retrieved using the get() and set() methods, rather than individual getters and setters as in server-side implementations. Indexed properties are set using the getIndex() and setIndex() methods.

Properties, Styles, and StyleSheets: An Echo.Component class will typically have properties that define its appearance and behavior. As shown in the above example, the second button's background color property has been set to the color green. In addition to setting properties on individual Components, some Component properties may be set using the concepts of Styles and StyleSheets, which are discussed in later chapters of the tutorial.

Children: Only certain Components, typically those used for layout purposes, can contain other Components. Examples of such containers include Echo.WindowPane, Echo.Column, Echo.Grid, and Echo.ContentPane. Some Components may also specify requirements that they only be added to specific types of parent Components, or that child Components be limited to a specific number and/or specific type. The API documentation for each Component will describe any such issues in detail.

Defining Component Hierarchies Using Object Literals

Hierarchies of client-side components can be defined with a substantially cleaner syntax than previously shown. To do this, we pass the Echo.Component constructor an object.

var buttonColumn = new Echo.Column({
    children: [
        new Echo.Button({
            text: "First Button"
        }),
        new Echo.Button({
            text: "Second Button",
            background: "#00ff00"
        })
    ]
});

All that is being done here is to pass each component constructor an object literal that specifies its initial property values. The "children" property is interpreted specially by the component constructor: it is specified as an array that contains the initial children of the component. You can nest as many constructors as you'd like, creating substantially larger component hierarchies all-at-once if desired. Additional "special" properties are available to assign layout data, style, style name, and register event listeners (these concepts are discussed later in the tutorial).

Note that this form of component definition looks like the component hierarchy. Though there are a few more braces, brackets, and parenthesis involved, it's generally a cleaner means of defining a new component.

LayoutData

The "LayoutData" property of a component is used to describe how it should be rendered by its containing parent component. For example, in the previous code snippet showing two buttons being added to a column, we might use layout data to inform the column as to how the buttons should be aligned. By setting the layoutData property appropriately, we could cause the Column to right justify the first button and left justify the second.

var buttonColumn = new Echo.Column({
    children: [
        new Echo.Button({
            text: "First Button",
            layoutData: {
                insets: 10
            }
        }),
        new Echo.Button({
            text: "Second Button",
            background: "#00ff00"
        })
    ]
});

Events

Components which capture user input may fire events when input occurs. In order to notify an application of events, the Echo.Component object provides the ability to register arbitrary event listeners for specific event types, which will be notified when a matching event type occurs. Others types objects, such as data models, may provide similar capability.

In the case of Echo.Component, addListener() and removeListener() methods are provided. These methods take two parameters, the first indicating the type of the event (as a string) and the second the listener to invoke (as a function). If you wish to invoke a method of an object, create a method wrapper using Core.method() and pass it to the event listener registration and deregistration methods as needed.

WARNING: Be careful to pass the SAME instance returned by Core.method() to register and unregister event listeners. When removing listeners, they will be removed based on reference, not based on equality of their properties.

As an example, Echo.Button components provide the capability to register "action" listeners which will receive events when a button click occurs. The following code snippet shows a button with a registered listener for "action" events. The The listener method will be invoked when the user clicks the button, with the event passed to it as the only parameter. The event listener in this case defines behavior to change the text of the Button in response to the user clicking it.

var button = new Echo.Button({
    text: "Please click me!"
});
button.addListener("action", function(e) {
    e.source.set("text", "Thanks!");
});

Event handlers can also be defined using at component construction time using the special events property.

var button = new Echo.Button({
    text: "Please click me!",
    events: {
        action: function(e) {
            e.source.set("text", "Thanks!");
        }
    }
});

And if you would like to invoke a method of an object as a result of an event being fired, Core.method() may be used as well:

PoliteButton = Core.extend(Echo.Button, {

    $construct: function() {
        Echo.Button.call(this, {
            text: "Please click me!",
            events: {
                action: Core.method(this, this._processClick)
            }
        });
    },
    
    _processClick: function(e) {
        this.set("text", "Thanks!");
    }
});

Event-driven: At this point it's worth revisiting the notion that Echo is an event-driven framework: the dynamics of an Echo application are defined by registering events that cause specific behaviors to occur in response to user actions. The entire control flow of an Echo application is defined using events. While a web developer using a conventional framework might define a hyperlink to navigate the user to a new screen of the user interface, an Echo developer would instead create a Echo.Button with an "action" listener that defined behavior to update the state of the user interface appropriately when it was clicked.