Modern Componentized UI

(most) frameworks

  • quick startup time
  • 90% use case solved
  • community


  • monolithic
  • opinionative
  • single point of failure
  • lack of interopability

but that's ok

It's just a tradeoff.  Sacrifice some ownership and flexibility for rapid development.

Constant Change

Sproutcore, Knockout.js, JavascriptMVC, Batman.js, Bootstrap, Ember, Angular, Polymer, React, ...

They all work, and can probably be made to do whatever you need

Every level of abstraction you add, is a level of complexity and usually a price in performance.

They are all way better than a procedural mess of spaghetti code.

( I really don't hate frameworks, I enjoy the new shiny )

Along came node.js & npm


  • a la carte libraries
    • pick & choose functionality you need / want
  • encapsulates functionality into reusable modules
  • build targetted applications for exactly what we need / want

I want to build ui code like that!



UI Module System


  • easy way to organize and structure front-end code & assets
    • javascript
    • templates
    • translations
    • styles
    • moar?
  • leverage other people's work (npm)

Browserify

"browser-side require() the node.js way"

Javascript

  • point browserify @ top level file
    • one file per page/app
    • "app bootstrap" file
  • dependencies are traversed, and bundled into 1 file

app structure

  • create an app EventEmitter
  • bolt on plugins to app
    • app.log
    • app.data
    • app.translate
    • app.router
      • map paths to controller functions
      • crossroads.js/hasher/history.js
  • controller functions require their "components"
  • components require their assets
    • templates
    • translations
    • styles (css or less)

t3h codez

look at them

Managing non-javascript assets


Typically we do this at the component level.

A UI component such as a list of content, will be responsible for it's own assets that control the behavior and presentation of that list of content.

Browserify Transforms

Turn non-javascript into javascript

Templates


 div.contianer
	ul.images
		li
			img(src=imageSource)
transforms into

function template(locals) {
    var buf = [];
    var jade_mixins = {};
    var locals_ = locals || {}, imageSource = locals_.imageSource;
    buf.push('<div class="contianer"><ul class="images"><li><img' + jade.attr("src", imageSource, true, false) + "/></li></ul></div>");
    return buf.join("");
}

Using a template in your app


 var template = require('./example.jade');

sorta becomes

 var template = function template(locals) {
    var buf = [];
    var jade_mixins = {};
    var locals_ = locals || {}, imageSource = locals_.imageSource;
    buf.push('<div class="contianer"><ul class="images"><li><img' + jade.attr("src", imageSource, true, false) + "/></li></ul></div>");
    return buf.join("");
}

Additional Template Challenges


  • common view mixins
    • format functions
    • translation functions
  • localized templates

 <span>{{ helpers.date.relative(myDate) }}</span>

require('./example.jade');

// what you really want for en-US userrequire('./example_en-US.jade'); // fall back to example.jade if it doesn't exist

Template Registry

global or scoped access to templates

var tempreg = require('tempreg');
tempreg.register('example.jade', fn); // register a template fntempreg.get(example.jade'); // get a handle to the render fntempreg('example.jade', data); // render it
// mixin common view helpers in one place, even use common npm moduleshelpers = { date: require('moment') };tempreg.on('pre-render', function(data) { data.helpers = helpers;});
// create scoped instancevar myTempreg = tempreg.create();myTempreg.on('pre-render', fn);

Incorporate Registry into Transform

// vanilla template transformvar template = function template(locals) {...};
// register template with tempreg in transformvar template = require('tempreg') .register('example.jade', function template(locals) {...}) .get('example.jade');
template(data); // renders template via registry, calling all pre-render hooks

Localized template files

You can look for "localized" files in your transform and use them instead.

CODE EXAMPLE

Translations

  • require translation file in javascript similar to templates
  • transform .pres files
  • convert proprietary ini style format to JSON
  • register with global registry module called "lexicon"
  • mixin "lexicon" to view helpers

// usually we don't use the return result of the require, we just want it registered for any templates that will be renderedrequire('./index.pres');

Styles

Combine your css into your js file payload.

  • May reduce cache benefits gained by having css as separate files
  • Can cause "jumping" if used on a non single-page-app.
    • markup rendered prior to styles being injected into head

  • No need to manage a separate manifest of what css files are needed where.  
  • Let them live with their components, and be required there as well.

Process

Transform a css (or less or stylus or w/e) file into parseable javascript that writes the css to a <style> tag in the <head>

use an existing transform:

or write your own, it's really simple
var inserted = [];

module.exports = function (css) {
    if (inserted.indexOf(css) >= 0) return;
    inserted.push(css);
    
    var elem = document.createElement('style');
    var text = document.createTextNode(css);
    elem.appendChild(text);
    
    document.head.appendChild(elem);
};

Useage

// these styles are automatically bundled into this javascript filerequire('./index.less');// they have now been appended to the document in a style tag

Complexity

Using a precompiler like LESS, SASS, Stylus will add some complexity to your transform.

  • determine how best to share common variables and mixins
    • import in each file
    • write a transform that automatically prepends them to the file contents before compiling

Questions?

Thanks!

Modern Componentized ui

By Brad Harris

Loading comments...

More from Brad Harris