An advanced form control with evently/couchapp

Finally wrapping it up. My objective was to create an invoice-like screen where it would basically consist of two parts: above part for general details like name, address and phone number; the below part for an area to list orders along with prices. No need to mention i am using couchapp and evently to get this done, meaning application works directly on couchdb.

The above part is a simple form so it was easy to map it to a json document and save to couchdb. Couchapp provides an easier way to do this but i have done it myself because of the reasons i explained earlier. The area of order items on the other hand, has proved to be challenging.

First of all, what i didn’t mention before is the way that these get stored in couchdb, or how my documents are structured. The form you see in screenshot actually represents a participant in plevsy+ops. It contains personal and contact details of the participant, his/her orders and the actual accommodation/transfer details. All of this information is handled in one document so an example participant is like:

{
  "person":"p",
  "title":"Dr",
  "vip":false,
  "firstname":"Erdem",
  "lastname":"Agaoglu",
  "note":"not",
  "orders":{
    "acc":{
      "Sheraton Paket":{
        "Double":{
          "Geç":{
            "price":1650,
            "addbed":1,
            "child":0
          }
        }
      }
    },
    "reg":{
      "Uzman":{
        "On-site":{
          "price":320
        }
      }
    }
  }
}

The first attribute of person does not carry any business value but useful for views. It defines that this document contains information about a participant. Next attributes are straight-forward detail values which are simple mostly-string fields.

The orders attribute is where it gets interesting. While it may seem confusing at first sight, it is carefully crafted to reflect real-world usage scenarios as well as to provide efficient storage. Both advantages aside it is the product of the requirement of extreme flexibility. Conferences change and some PCOs do not use the terminology other might use, so it is not possible to put constraints on the data to represent a order. So we use the data itself as the structure by using a tree, hence, for an accommodation order where it is simpler to write something like

{
  "package": "Sheraton",
  "occupancy": "Double",
  "registration": "Late",
  "price": 1650,
  "addbed": 1,
  "child": 0
}

we write

{
  "Sheraton":{
    "Double":{
      "Late":{
        "price":1650,
        "addbed":1,
        "child":0
      }
    }
  }
}

That way we don’t repeat the attribute identifiers “package”, “occupancy” and “registration” and since we read this information recursively, we are able to introduce any kind of categorization into any level. Think adding a modifier to your invoice item with a pen. On the real-world-usage-scenario-consistency stuff, did you see any of these attribute identifiers on any invoices?

Back to the problem, which is mapping the form to this json document and back. I’ll list the core and side requirements of this form to make the amount of required functionality clear.

  1. User should be able to add as many items as she desires/requires to the orders area. Similarly she should be able to remove any items.
  2. Sub-types should be dynamic, as in selecting a super-type will bring its sub-types. For example Sheraton may provide occupancy options like Single and Double, while Hilton provides Single, Double and Triple. These occupancy options should appear as user selects Sheraton or Hilton.
  3. User should see base prices for the order, so the thing should calculate the Late price of a Double Sheraton room with one additional bed and no child, as user selects them.
  4. Value carrier inputs (price, addbed, # of children) should be named like ‘orders.acc.Sheraton.Double.Late.price’ so that i can use a deep-json converter and create my document structure.
  5. I should be able to re-construct the whole widget from the saved document in order to show edit screens.
  6. Registration, accommodation or transfer orders should expose their prices and sponsors so that the area could show sums. Or in metaphorical words, all order types should extend some abstract order type that exposes a common price field.
  7. I should be able to do all of this functions in one evently widget. (because)

Most of these requirements, i explained in previous posts but i’ll go over them again.

Couchapp directory structure shows the root of the evently widget orders. _init does not do anything hugely important, install loads the widgets from a saved document, total calculates and shows total prices. spawn creates an order item widget using mustache.

<div>
  <div class="field">
    <div class="controls">
      <a href="#" class="remove" title="Sil"></a>
    </div>
    <div class="input">
      <input type="text" class="value price"/>
      <div class="module {{module}}" moduleid="{{moduleid}}">
      </div>
    </div>
  </div>
</div>

As you can see this template does not contain anything about types nor any order-item specific control. This is the metaphorical root class. It defines price which is common among order-items but not how registrations or accomodations manage this price. The outer div may seem superfluous but it is the workaround for the evently problem where you cannot attach event listeners to elements added using render:prepend/append [mentioned in the previous post]. You still cannot attach any listeners to outer div but you’re free to do so on div.field using selectors.

As in this example i have attached some mouse events, a destroy and a reset event. price event lets other controls to set this widget’s price so this completes the common behaviour.

Back to the template, the inheritance-like structure is hidden with the {{module}} class in div.module. This template is rendered with {{module}} values such as reg, acc or tra and the selectors div.reg, div.acc or div.tra in _init event of div.field applies order-item specific behaviour.

As you can guess they all have another mustache system to define specific controls. I’ll show only reg since the other ones are not much different.

The mustache defines inner structure like

<select class="name">
  {{#defs}}
  <option value="{{name}}">{{name}}</option>
  {{/defs}}
</select>
<select class="type">
</select>

meaning that part gets inserted into the previous template block. Here is your template inheritance in mustache! In reverse that is. Not the real deal like django templates or facelets.

Selectors attach behaviors to change events of the select controls so that the first one is able to populate the second one and second one is able to set the base price. In readable words, user selects ‘Assistant’ in first control to get ‘Early’ and ‘Late’ in second control, then user selects ‘Late’ to get the base price of Late registration for Assistants. The after function controls initial states of those controls. If the widget is for a new order item, they will have default values whereas for a previously saved order item, they will show saved values.

BTW i needed to patch evently to get it working in the way i want it to work. Evently somehow accumulates event arguments down the _init chain. So while your arguments for the spawn event is like [event, arg1, arg2] an _init connected to this gets [event, parentevent, arg1, arg2] and the _init connected to this gets [event, parentevent, grandparentevent, arg1, arg2]. I wanted to have [event, arg1, arg2] no matter how deep my _init was.

Complete evently widget looks like that.

I can agree that looks complicated but bear in mind that this is not an input type="text" anymore. This is a fully-functioning multi-typed multi-item-editing form control. And i am sure it will be more than complicated without evently.

For the curious, i have pushed a demo here. The interface is in Turkish but i think anyone can navigate by randomly clicking around. Since this is just a demo, i have to advice using some late version Chrome. And on the performance issues, i know i have a long way optimizing it.

As a conclusion, putting business-specific things aside i got 3 gotchas trying to build an advanced widget:

  1. Evently will not let you attach events to the appended/prepended elements. You may workaround this by wrapping it with a div.
  2. You can let your mustache templates to define some css classes while rendering, then you can use this classes in selectors to alter behavior of the widget.
  3. Evently accumulates event handler arguments down the _init chain, so don’t try to read your parameters on the second argument. (I will submit the patch to github)

< PREV

Notes

  1. doomythefroomy reblogged this from agaoglu
  2. agaoglu posted this