Skip to content

Instantly share code, notes, and snippets.

@sdesai
Created November 22, 2011 20:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sdesai/1386836 to your computer and use it in GitHub Desktop.
Save sdesai/1386836 to your computer and use it in GitHub Desktop.
WidgetStringRenderer

USE CASES

  • Render widgets on NodeJS, where DOM is absent.
  • Optimize rendering of Widgets at scale (1000s of TreeViewNodes).

GOAL

  • Avoid all Node references from Widget through the end of renderUI().
  • bindUI()/syncUI() will still have Node references (to bind events and incrementally update DOM).

DESIGN

Opt-in Widget Extension, to maintain backwards compatibility.

A boundingBox/contentBox Node instance won't be present for initializer/attr setters/attr getters/HTML_PARSER, so needs to be opt-in.

  1. Component Developer can opt-in, if they have a 100% string rendered component (e.g. TreeViewNode)

    In this case, get("boundingBox") etc. needs to be documented accordingly for the end user.

  2. Environment can opt-in (e.g. NodeJS - conditional loading)

Example:

Y.WidgetStringRenderer = function() {...};

Y.WidgetStringRenderer.prototype = {
   // override what needs to be overridden in Y.Widget to support string template based rendering.
}

Y.Foo = Y.Base.create("foo", Y.Widget, [Y.WidgetStringRenderer]);

Handlebars Default

It'll use Handlebar style templates as opposed to substitute, for forward compatibility.

However we should maybe look into a "handlebars-core" to satisfy the kweight nitpickers. We've been asked to break out less than 3KB chunks before which is where handlebars-base-min.js currently is.

"handlebars-core" could provide basic {{ }} and {{{ }}} support, and also maybe provide substitute compatibility (how to identify single { tokens, from content in a handlebars template?).

Two Phase Render

We'll need to break up the render() phase, into the renderUI() portion and the bind/syncUI() portion

render()
    renderUI() : No Node references
    bindUI()   : Node references
    syncUI()   : Node references

Not sure what the method split should be yet. Options are below, first one is my leading candidate

Options

TreeView use case:

// While iterating 1000s treeview nodes ...
treeviewNode.renderHTML(buffer);     // only renderUI() - is it OK that render event is not fired? 
                                     //                   I think so. Nothing is in the DOM yet.

// Once injected into the DOM ...
treeviewNode.render();         // renderUI() [if not invoked before], bindUI(), syncUI()

NodeJS use case:

// On Server
calendar.renderHTML();

// On Client
calendar.render();

Or,

treeviewNode.render();
treeviewNode.bind();        // How about if they want to do it all at once in render(); bind() and Y.bind() confusion

Or,

treeviewNode.renderHTML();
treeviewNode.bind();

Or,

treeviewNode.renderUI() // When do we fire/set render state?
treeviewNode.bindUI()
treeviewNode.syncUI()

Will require re-establishing boundingBox/contentBox Node references, post-renderUI().

Widget will generate node instance from rendered template content for boundingBox, contentBox

Usage

TreeViewNode - Always 100% String rendered

Y.TreeViewNode = Y.Base.create("treeViewNode", Y.Widget, [Y.Parent, Y.Child, Y.WidgetStringRenderer]);

// In Parent.render() ...
var buffer = [];
for (i = 0; i < children.length; i++) {
    child.renderHTML(buffer);   
}

var allChildrenHTML = buffer.join("");

Only on NodeJS (conditionally loaded extension mix)

// calendar-nodejs, or maybe just widget-base-nodejs
Y.Calendar = Y.Base.create("calendar", Y.Widget, [Y.WidgetStringRenderer]);

var buffer = []
calendar.renderHTML(buffer);

var calendarHTML = buffer.join("");

We can add sugar in the future (render straight into a template for example). Not enough time for Sprint 1.

calendar.renderHTML(template, token);
@sdesai
Copy link
Author

sdesai commented May 17, 2012

Hey Nate,

The breakup of Widget into these separate pieces doesn't appear to be on the near term horizon, as far as the base Widget goes. It's mainly because I haven't had a chance to think through how we'd get there, while maintaining backwards compatibility. But I'd imagine if it was broken out it would be:

StringRendering Based Widget : Widget = Base + Lifecycle + StringBasedView + Model + Controller 

The basic idea being - I should be able to switch out Views with the same Model, and one of those views could be purely string/template based.

Whether these are extensions or plugins doesn't make that much difference at the high level.

The idea is to be able to re-use/extend/switch out each piece independently.

That said, at the lower level, "extend" is where it does make a difference - since we'd need full "multiple inheritance" support, if you wanted to mix in an extension with it's own prototype chain.

If they are "plugins", they'd be separate objects which interact with each other. The downside there being the potential performance overhead of a single object now being three separate objects - the M, V and C instances. Not a big deal for a handful of instances, but for Tree etc, it may bubble up. If they were "extensions", they'd go through Y.Base.create, to create a single object (with the multiple inheritance drawback).

If I could start from scratch on a Widget2 base class, it would be broken out, with separately extendable M, V and C pieces. That doesn't mean folks extending Widget, can't begin to explore breaking out the M, V and C responsibilities which currently get rolled into a single class, as Luke is doing for the refactored DataTable.


As for the WidgetStringRenderer, I'd be interested in your feedback on this initial approach, which we're trying to iron out the gaps for:

https://github.com/sdesai/yui3/blob/widget-htmlrenderer/src/widget/tests/manual/widget-htmlrenderer.html
https://github.com/sdesai/yui3/blob/widget-htmlrenderer/src/widget/tests/manual/tree-htmlrenderer.html

Which is essentially the implementation based on the discussion above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment