Open the Contacts application

The Contacts sample application demonstrates the use QuickUI as the user interface framework for a typical web application. This page provides an overview of the Contacts architecture. See the source code repository for complete details.

General notes on tools

The Contacts application shows just one possible set of tools; many other combinations could be used to create the same Contacts application. In addition to jQuery, which all QuickUI applications depend upon, this particular sample Contacts implementation uses the following tools:

General architecture

Like many modern applications, the Contacts sample application carefully separates its representations of the data objects being viewed and manipulated (the "Model") from the user interface doing that viewing and manipulation (the "View").

Backbone applications like Contacts are often described as using a Model-View-Controller architecture. In the case of the Contacts application, the entity sitting between the View and Model behaves more like the Presenter in a Model-View-Presenter arrangement. (See this article on Scaling Isomorphic Javascript Code for a good comparison a good comparison of MVC, MVP, and related architectures). The main difference is that, in the Contacts application, the View is not itself listening to events that indicate changes in the Model; the Presenter listens to those events, then updates the View as appropriate.

Contact data models

The definition for the data Model classes in the Contacts application are quite simple:

ContactsPresenter

The ContactsPresenter class provides most of the wiring between the Contacts collection (described above) and the main ContactsPage user interface class (described below).

Example: When the user presses the New button in the Toolbar user interface control, a custom “newContact” user interface event is raised. This bubbles up to the level of the ContactsPage view, where it is detected by ContactsPresenter. The presenter then tells the Contacts collection model to add a new contact. The Contacts collection will eventually raise an “add” event of its own, which will again be detected by the ContactsPresenter. In response, the presenter tells the ContactsPage view to add the new contact to the list box. Other view and model communication is handled similarly.

Backbone doesn't provide a base "Presenter" base class, but Backbone's own View class makes a reasonably good base class for a presenter:

class window.ContactsPresenter extends Backbone.View

The real View in this application is provided by QuickUI controls like ContactsPage. Deriving our presenter from the View base class does provide some nice features, however: the presenter gets references to the model (the Contacts collection) and to the real View (via the $el reference). In Backbone applications, the $el member points to a jQuery reference. Because QuickUI controls derive from jQuery, we can set $el to directly to a QuickUI control. Another benefit of deriving our presenter from the View class is that we automatically get event binding for events raised by the QuickUI controls which constitute the View.

Some other notes on the presenter:

ContactsPage

The ContactsPage is a QuickUI control that acts as the top-level View in the Model-View-Presenter architecture. In this particular application, the ContactsPage represents the entire end-user visible document in the browser, but the View could just have easily been a control that represented only a portion of the overall DOM tree. Some notes on ContactsPage:

Toolbar

The Toolbar control takes care of populating the simple toolbar with a title and some buttons. The only interesting aspect of the user interface here is that a standard QuickUI Catalog control called PopupButton is used to create a dropdown menu of debug commands in a few lines of declarative Control JSON:

{ control: PopupButton, ref: "buttonDebug", content: "Debug", popup: [
  { control: MenuItem, ref: "menuItemSamples", content: "Reload Sample Contacts" }
  { control: MenuItem, ref: "menuItemEraseAll", content: "Remove All" }
  { control: MenuSeparator }
  { control: MenuItem, ref: "menuAbout", content: "About This Application" }
]}

The Toolbar class also defines the "+" key (and, on Windows, the Insert key) as a keyboard shortcut for the "New" button.

ContactListBox

This control is the user interface element responsible for actually representing the overall collection of contacts. ContactListBox derives from ListBox, which in turn inherits from List, a standard control for representing an array of items as controls. In this case, ContactListBox will represent its items as ContactCard controls (described below). All of this is extremely easy to arrange in declaring the ContactListBox class:

class window.ContactListBox extends ListBox
  
  inherited:
    # Each item in the list should be represented as a ContactCard control.
    itemClass: "ContactCard"
    # The list item (the model) will be passed to a card's contact() property.
    mapFunction: "contact"

To the standard List behavior, ListBox base class adds selection semantics and keyboard navigation:

ContactListBox adds some keyboard support of its own: Enter to edit the selected card, Del to delete the selected card, and Escape to deselect a card.

Card

This class is a simple container that renders as a small business card with shadow effects. Most of this is achieved with styling. One set of style rules are only applied when the “selected” CSS class is applied, which is automatically applied to a card by the list box when a card becomes selected. A card defines styles such that a card’s visual height is normally constrained, but when selected the card will expand to show the card’s entire content.

One aspect of its visual appearance that is managed with a standard control is the use of a Fader to fade out the card’s content in the normal state if the content doesn’t completely fit.

ContactCard

This control is the primary representation of a contact in the user interface. It subclasses Card (above) in order to gain that class’ visual presentation.

class window.ContactCard extends Card
  
  inherited:
    content:
      # The Mode control shows just one child (mode) at a time.
      control: Mode, ref: "mode", content: [
        { control: "DetailsReader", ref: "modeRead" }
        { control: "DetailsEditor", ref: "modeEdit" }
      ]

The Card and ContactCard classes could easily be combined into a single class, but factoring this into two classes lets each class focus on a more limited set of responsibilities. It would also make it easier to adapt the Card class for other purposes.

ContactDetails

The read and edit user interface for a contact expose different controls, but the two states do share some things:

To implement these shared aspects, the ContactDetails class exists to serve as a base class for the read state, DetailsReader, and the edit state, DetailsEditor.

The ContactDetails class expects to be subclassed to provide a complete implementation; it’s similar to an abstract class in many programming languages. Specifically, the various contact properties for name(), address(), etc., store their values in controls — but it’s up to a subclass to actually decide what type of element or control will be used. This allows the read state to represent the name() property with a simple div, while the edit state represents the same name() property with a ValidatingTextBox.

DetailsReader

This subclass of ContactDetails (above) is used to represent the contact in the read state (when the card is unselected, or selected but not yet in editing mode). It defines elements expected by ContactDetails that show the various contact properties in read only elements or controls. E.g., the postal address() property is shown in a PostalAddress control, which adds a link to Google Maps.

Because this handles the read state, the only interactivity defined by DetailsReader is tracking whether the user has clicked a specific read-only field. If the user clicks the name field, for instance, the control will raise an fieldClick event. ContactCard uses this event to place the keyboard focus in the corresponding editable field in DetailsEditor.

DetailsEditor

The other subclass of ContactDetails, DetailsEditor, is used to edit the information for a specific contact. This control entails the most domain- specific interactivity (i.e., UI details which wouldn’t be found in any other application), and hence has the longest source code of any of the controls in the application. (Even then, it’s not particularly long.)

FieldLauncher

In the card’s read-only states, two of the card fields, email address and postal address, include little buttons next to them that use the field’s content to launch a new window: a new email message or Google Maps, respectively. This simple but shared behavior is encapsulated in the control class FieldLauncher. This is actually a good example of a situation where, in an old-school JavaScript application, a developer might have simply copy-and-pasted code rather than going through the work of factoring out the common behavior into a separate component. In QuickUI, creating and using a component is easy enough that the “activation energy” required to trigger refactoring is extremely low.

Observations

The Contacts sample application achieves a reasonably interesting visual appearance and degree of interactivity in a fairly small amount of code.