My name is Ismael Chang Ghalimi. I fight against software illiteracy. I am a stoic, and this blog is my agora.

View meta-model

Following the MVC pattern, we’ve properly separated our record view’s View from its Model, and we’re linking the two through bindings from the View and through control flow statements from the Controller. But we went a step further by also defining a meta-model for the view itself.

The justification for it is the following: the record view’s job is to display a form for the record of an object, plus additional information such as workflow status and changes timeline. For the form part, fields can be organized across multiple sections, which themselves are displayed through multiple tabs. While our current record view only supports the canonical generation of a form based on the model of the record’s object, a future version will let users create their own custom forms for any object.

In such a context, we need a way to define the structure of a form, by nesting references to fields within sections and sections within tabs. Then, we need to generate a canonical form structure for objects that do not have any custom form, or dynamically lookup a pre-defined form structure. Finally, we need to merge this form structure with the record’s data, the object’s model, and the definition of all datatypes used by fields of the object (a field’s datatype defines its widget).

This is pretty much what we did yesterday. At first, I was expecting this project to take a few days. We would have to develop a form meta-model from scratch, then implement a fairly sophisticated template (the View) to merge data and meta-data about four different entities (form, record, object, datatype). Instead, we were done in about three hours, and all it took fit within less than 60 lines of code:

  • HTML template (less than 40 lines of code, widgets excluded)
  • Canonical form generator (less than 20 lines of code)
  • Canonical form meta-model (simple JSON dynamically populated by the form generator)

How come? AngularJS works. Plain and simple…

Adding classes to elements within an iterator

AngularJS offers support for powerful expressions within templates. Unfortunately, you cannot write a control flow statement in an expression (conditionals, loops, or throw). According to the documentation, “the reason behind this is core to the angular philosophy that application logic should be in controllers, not in the view. If you need a conditional, loop, or to throw from a view expression, delegate to a JavaScript method instead.”

This is a bit of a shame, especially when trying to add a class to specific elements within an iterator (loop, using ng-repeat), either the first one, the last one, or any element in the middle. This is especially frustrating since AngularJS conveniently provides special properties for it ($index, $first, $middle, $last). Separation of concerns should be a philosophy, not a religion, and many control flow statements really belong to the view, not to the controller.

In order to work around this problem, we just created the following helper function:

$scope.isOn = function(elementPosition, className) {
  return elementPosition ? className : null;
}

We added this function to our controller and invoked it from our templates like that:

class="{{isOn($first, 'active')}}"

This will add the active class to the first element within a list. Of course, the very same function can be used with $middle and $last as well. We implemented this helper function in order to activate the first tab of our record view now that we’re dynamically generating the record view’s structure based on a JSON definition (more on this later).

UPDATE: We just learned that ternary operators are supported in expressions. This was documented in this stackoverflow answer and we tested it within our code. It removes the need for the helper function we implemented, and makes the code a lot more readable:

ng:class="{true:'active', false:''}[$first]"

Font Awesome

This morning, I’ve been working on adding support for icons in our new record view. Unfortunately, I quickly realized that Fontello (used for icon fonts) was suddenly interfering with Twitter Bootstrap’s icons, since both were using CSS classes with the .icon- prefix. I briefly considered modifying one of the two libraries, but decided against it for obvious maintenance reasons. While looking online for a solution, I stumbled upon Font Awesome, which did an awesome job (pun intended) of packaging an icon font for use with Twitter Bootstrap.

After I had it integrated within our framework, all I had to do was to change the icon references for a few datatypes, and I was up and running. I then applied some Twitter Bootstrap colors (@linkColor and @linkColorHover) and added a Twitter Bootstrap Tooltip to make it a bit more dynamic (when you mouse over an icon, its color gets darker and a tooltip is displayed).

It looks… awesome!

AngularUI and Bootstrap integration

Now that we’ve decided to go with AngularJS for our new user interface framework, we can start re-implementing our record view and query builder. For this purpose, we will need a few widgets, which we will get from AngularUI and Bootstrap.

On that front, I am pleased to report that things are looking pretty good. After a couple hours of work, I implemented field documentation using Bootstrap Tooltips and field masks using AngularUI Mask, which itself is based on jQuery Masked Input.

For the former, all I had to do was to activate tooltips with this jQuery statement:

$("[rel=tooltip]").tooltip();

And for the latter, I added the following line to my controller:

angular.module('sutoiku', ['ui']);

Step by step, I am re-implementing all the features of the record view, and while I’m still finding my way around all the capabilities offered by AngularJS, my first impression is that the code I’m writing is a lot smaller, and a lot more readable. It will be interesting to see how much smaller it ends up being compared to the previous version. As a reminder, here are the features offered by the record view:

  • User-defined tabs
  • User-defined sections
  • User-configured columns
  • User-ordered fields
  • User-extensible input widgets (Check Box, Text Box, Text Area, etc.)
  • Field labels (displaying the name of the field)
  • Field placeholders (displaying some sample content)
  • Field data masks (for datatypes like phone number)
  • Field documentation (in a popup window)
  • Field highlighting (highlighting of the field being edited)
  • Editable fields (uneditable fields are disabled)
  • Hidden fields (invisible to the user but still present in the form’s data)
  • Informative fields (disabled and displayed on a separate tab)
  • Required fields (with asterisk next to the field’s label)
  • Calculated fields (with real-time calculation, if we can implement it)
  • Conditional fields (for which editable, hidden, and required are set by rules)
  • Error messages (for validation errors and missing required fields)
  • Prefixes and suffixes (for currency amounts, percentages, and quantities)
  • Field styles (for customizing the way fields display data)
  • Field icons (for displaying field details)
  • Field details (for displaying details of target records referenced in relations)
  • Dynamic lookups (for relationship fields)
  • Formatters (for displaying data)
  • Parsers (for gathering data)
  • Validators (for validation)
  • Buttons (save and cancel)

Tabs

Refactoring our CSS stylesheet allowed us to refine the styling of our tabs by reusing the styling of our buttons. This makes the look and feel of our record view more consistent, and it reduces the amount of code we have to maintain. Here is the final result, cropped to fit on this blog.

PS: Yes, I know, we need to properly style this ugly checkbox…

CSS refactoring completed

Our CSS refactoring is done. It certainly was a lot harder than I had expected it to be, but it was worth the effort. The record view now scales gracefully with window dimensions and font sizes, while the CSS code is cleaner than it’s ever been, all thanks to the magic of jQuery and LESS.

Here is what it looks like with a 14px font size and 280px input width:

And here is the 18px/350px version:

And if that was not obvious already, the currency symbol stays where it should as you type:

Now, I just wish all web forms were like that…

I love LESS

I just discovered that LESS provides a full set of color functions, so I decided to try them out on our field detail icons (the pin and arrow shown on the following screenshot). I wanted to see if I could use these functions to dynamically change an icon’s color when the user mouses over it.

 

All it took are two lines of CSS code:

.suIcon:hover {color: darken(@blue, 20%);}
.suIconInvalid:hover {color: darken(@red, 20%);}

I love LESS, more and more.

CSS refactoring

Our CSS refactoring is almost done, but it took a lot longer than expected. A couple hours quickly turned into a couple days, as we discovered more and more areas that needed attention. Essentially, when you want a complex piece of user interace to gracefully scale with the default size of fonts, every single CSS rule needs careful crafting. Thanks to LESS, we could do that cleanly by using variables, mixins, parametric mixins, and operations. Ultimately, we managed to:

  • Reset CSS styles
  • Externalize all colors through variables
  • Externalize all fonts through variables
  • Parametrize all dimensions
  • Replace all static font sizes (using px) by dynamic ones (using em)
  • Make all complex rules (like border-radius) browser independant
  • Reduce the repetition of rules to the absolute minimum
  • Include copyright notices for all embedded fonts
  • Make CSS styles visible to jQuery

The last item deserves some explanation: in some cases, jQuery needs to know the style of some elements, and how this style materializes itself on the web browser. For example, in order to properly implement prefixes and suffixes in text boxes, jQuery needs to know the actual width (in pixel) of the monospaced font (Inconsolata in our case) used to display the quantity symbol of numerical values like Currency Amount, Percent, or Quantity.

Because this width depends on the default font size (which can be changed by the user) and the styles of parent elements, it cannot be known in advance. Instead, it has to be detected by jQuery, and for that to happen, a sample character needs to be displayed while the page is loading, measured, then removed before the page is actually displayed to the end-user.

Painful but necessary…

CSS reset

As we were cleaning up our CSS stylesheet, we realized that we had forgotten to reset CSS styles. We quickly filled that gap by including Eric Meyer’s Reset CSS, which instantly fixed quite a few nagging issues. We can now focus our efforts on fine tuning everything.

CSS acid test

The record view’s CSS stylesheet is now over 500 lines, which means that it’s about time we test its robustness. One way to do that is to change the default width for field inputs and the default size for fonts. This acid test will quickly show us all the shortcuts that have been taken. And that’s precisely what happened this morning when we tried it.

Outcome? We scored perfectly on the field input width front, but setting the default font size to an unusually large value highlighted quite a few problems. So, I’ll spend a couple hours fixing them before things get out of control.

Prefixes and suffixes

Take a look at these three screenshots:

They show three examples of use for quantity symbols:

  • Currency amount
  • Percentage amount
  • Quantity of measure

Most importantly, they show that we can use quantity symbols either as prefixes or suffixes. And believe me, when the quantity symbol is on the left of the field’s value for a right-aligned field value (Currency in the above screenshot), keeping it there isn’t exactly trivial, especially if you want its position to change dynamically while the user is updating the field’s value. But with a healthy dose of CSS and jQuery, you can pull this trick and make it look really good.

For the time being, we only support right-aligned fields, but adding support for left alignment is relatively straightforward. When we do, this will give us support for all four combinations that were listed yesterday:

  • Left text align and quantity symbol on the left
  • Left text align and quantity symbol on the right
  • Right text align and quantity symbol on the left
  • Right test align and quantity symbol on the right

This record view is really starting to take shape…

PS: If you try to replicate this trick, make sure to use a monospaced font

Quantities and formatters

Take a look at these two screenshots:

They don’t look like much, but they’re actually demonstrating two very cool features: first, the ability to define quantities for fields (Currency, Percent, or Unit of Measure); second, the ability to format values for display on the record view (80% is actually stored as 0.8 in the database).

Now that this is working, we will need to support four variations on the same theme:

  • Left text align and quantity symbol on the left
  • Left text align and quantity symbol on the right
  • Right text align and quantity symbol on the left
  • Right test align and quantity symbol on the right

Status update on record view

We’ve been working on the new record view for about two weeks. Here is what has been done:

  • User-extensible input widgets (Check Box, Text Box, Text Area, etc.)
  • Field labels (displaying the name of the field)
  • Field placeholders (displaying some sample content)
  • Field data masks (for datatypes like phone number)
  • Field documentation (for one field at a time or all fields at once)
  • Field highlighting (highlighting of the field being edited)
  • Editable fields (uneditable fields are disabled)
  • Hidden fields (invisible to the user but still present in the form’s data)
  • Informative fields (disabled and displayed on a separate tab)
  • Required fields (with asterisk next to the field’s label)
  • Error messages (for validation errors and missing required fields)
  • Prefixes and suffixes (for currency amounts, percentages, and quantities)
  • Field styles (for customizing the way fields display data)
  • Field icons (for displaying field details)
  • Field details (for displaying details of target records referenced in relations)
  • Formatters (for displaying data)
  • Validation methods (for executing validation rules)
  • Validation rules (for defining validation rules on datatypes and fields)
  • Buttons (for saving a record or canceling its edition)

And here is what remains to be done:

  • Calculated fields (with real-time calculation, if we can implement it)
  • Conditional fields (for which editable, hidden, and required are set by rules)
  • Dynamic lookups (for relationship fields)
  • Parsers (for gathering data)

At this point, we can safely say that the hardest is behind us. Now, it’s time to wrap it up.