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 user
require('./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 fn
tempreg.get(example.jade'); // get a handle to the render fn
tempreg('example.jade', data); // render it
// mixin common view helpers in one place, even use common npm modules
helpers = { date: require('moment') };
tempreg.on('pre-render', function(data) {
data.helpers = helpers;
});
// create scoped instance
var myTempreg = tempreg.create();
myTempreg.on('pre-render', fn);
Incorporate Registry into Transform
// vanilla template transform
var template = function template(locals) {...};
// register template with tempreg in transform
var 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 rendered
require('./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 file
require('./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
Modern Componentized ui
- 6,903