Skip to content

Elation Components Example

James Baicoianu edited this page Feb 17, 2014 · 8 revisions

For this example, we'll make a quick list app which lets the user add and remove items from any number of lists (shopping list, action item list, assassination hit list, etc). We start by defining a simple component which will act as a controller for the page. We'll fill in the details as we go.

The following components will be used in this example:

elation container localindexed
ui button
input
label
list
panel

templates/quicklist.tpl:

<div data-elation-component="quicklist.main"></div>

scripts/quicklist.js:

elation.component.add("quicklist.main", function() {
  // initialization
  this.init = function() { 
    this.initData(); 
    this.initHTML(); 
    this.initEvents(); 
  }
  this.initData = function() {}
  this.initHTML = function() {}
  this.initEvents = function() {}

  // app logic
  this.getListItemContainer = function(listname) {}
  this.setActiveList = function(listname) {}

  // event handlers
  this.handleListAdd = function(ev) {}
  this.handleListItemAdd = function(ev) {}
  this.handleListSelect = function(ev) {}
  this.handleListItemSelect = function(ev) {}
});

Let's fill in these functions. First let's look at the data this app will need. We have two main entities we're dealing with here, lists and listitems. A user can have many lists, and each list can have many listitems. Let's define those:

quicklist.model

// FIXME - this doesn't actually work end-to-end, since the model config isn't shared with the frontend
{
  "classes": {
    "list": {
      "listname"    : {"type": "string", pk: true},
      "itemcount"   : {"type": "integer"},
      "created_time": {"type": "timestamp"},
      "updated_time": {"type": "timestamp"}
    },
    "listitem": {
      "listname":     {"type": "string", pk: true}
      "itemname":     {"type": "string", pk: true}
      "created_time": {"type": "timestamp"},
      "updated_time": {"type": "timestamp"}
    }
  }
}

Now we'll set up some collections to contain these items. For now we'll do localStorage-backed lists using elation.container.localindexed, and can look into adding server sync later. We'll store the list of all lists as quicklist.lists and each individual list gets stored as quicklist.lists.{$listname}

scripts/quicklist.js:

  /**
   * Initialize the data containers involved in making our app go
   */
  this.initData = function() {
    // First create a single container to hold a list of all lists
    this.listcontainer = elation.container.localindexed({
        index: "listname",
        storagekey: "quicklist.lists"
      });

    // Then allocate an object to hold multiple ListItem containers, one for each list
    // These ListItem containers will be allocated as needed by this.getListItemContainer(listname)
    this.listitemcontainers = {};
  }
  /**
   * Return the container associated with the named listitem, creating it if it doesn't exist yet
   * @param name string listname
   */
  this.getListItemContainer = function(listname) {
    if (!this.listitemcontainers[listname]) {
      this.listitemcontainers[listname] = elation.container.localindexed({
        index: "itemname",
        storagekey: "quicklist.lists." + listname
      });
    }
    return this.listitemcontainers[listname];
  }

Next, let's set up the HTML layout. In this case, we'll generate everything in JavaScript, but depending on the needs of your app you could generate the HTML and component structure with server-side templates if needed. We're going for a two-column layout, with a list and an input inside of each one. Here's how the client-side implementation looks:

scripts/quicklist.js:

  /**
   * Create HTML layout and components
   */
  this.initHTML = function() {
    // Layout panels
    this.panel_main = elation.ui.panel({
        orientation: 'horizontal',
        classname: 'quicklist',
        append: this
      });
    this.panel_sidebar = this.panel_main.add(elation.ui.panel({
        orientation: 'vertical',
        classname: 'quicklist_sidebar'
      }));
    this.panel_content = this.panel_main.add(elation.ui.panel({
        orientation: 'vertical',
        classname: 'quicklist_content'
      }));

    // UI elements
    this.listlabel = elation.ui.label({
        label: "Lists",
        containertag: 'h2',
        append: this.panel_sidebar
      });
    this.listinput = elation.ui.input({
        placeholder: "Load List",
        autofocus: true,
        append: this.panel_sidebar
      });
    this.listview = elation.ui.list({
        itemcontainer: this.listcontainer,
        append: this.panel_sidebar,
        attrs: { label: "listname" }
      });
    this.listitemlabel = elation.ui.label({
        label: "List Items",
        hidden: true,
        containertag: 'h2',
        append: this.panel_content
      });
    this.listiteminput = elation.ui.input({
        placeholder: "Add Item",
        hidden: true,
        append: this.panel_content
      });
    this.listitemview = elation.ui.list({
        attrs: { label: "itemname" },
        hidden: true,
        append: this.panel_content
      });
  }

Finally, we'll attach some events to the components we've created, and define their event handlers

  /**
   * Attach event handlers to the components we've created above
   */
  this.initEvents = function() {
    elation.events.add(this.listinput, "ui_input_accept", elation.bind(this, this.handleListAdd));
    elation.events.add(this.listview, "ui_list_select", elation.bind(this, this.handleListSelect));
    elation.events.add(this.listiteminput, "ui_input_accept", elation.bind(this, this.handleListItemAdd));
    elation.events.add(this.listitemview, "ui_list_select", elation.bind(this, this.handleListItemSelect));
  }
  /**
   * Set the specified list as active
   * @param listname string Name of list to set as active
   */
  this.setActiveList = function(listname) {
    var list = this.listcontainer.get(listname)
    if (list) {
      this.activelist = list;
      this.listitemview.setItemContainer(this.getListItemContainer(listname));

      this.listitemlabel.setlabel(list.listname);
      this.listitemlabel.show();
      this.listitemview.show();
      this.listiteminput.show();
      this.listiteminput.clear(true);
    }
  }
  // Event handlers
  this.handleListAdd = function(ev) {
    var newvalue = this.listinput.value;
    if (newvalue != "") {
      this.listinput.clear();
      var newlist = new elation.quicklist.list(newvalue);
      this.listcontainer.add(newlist, 0);
      this.setActiveList(newvalue);
    }
  }
  this.handleListItemAdd = function(ev) {
    var newvalue = this.listiteminput.value;
    if (newvalue != "") {
      var container = this.getListItemContainer(this.activelist.listname);
      container.add(new elation.quicklist.listitem(newvalue), 0);
    }
    this.listiteminput.clear(true);
  }
  this.handleListSelect = function(ev) {
    this.setActiveList(ev.data.listname);
  }
  this.handleListItemSelect = function(ev) {
    var container = this.getListItemContainer(this.activelist.listname);
    container.remove(ev.data);
  }
Clone this wiki locally