Flow/Architecture/Templating

The Flow/Epic Front-End rewrite of April 2014 uses Handlebars to render in JavaScript, and the lightncandy PHP implementation. This allows templates to be shared between front-end and back-end.

MediaWiki is in the middle of Requests for comment/HTML templating library, but the Flow team couldnt wait.

Using
The templates are in the handlebars subdirectory.

For example, :

This
 * 1) sets the scope to the 'revision' key of the API response.
 * 2) outputs some HTML
 * 3) uses handlebars' limited logic to conditionally output a class if the key   is true in this scope
 * 4) sets the scope to the 'author' key within 'revision', in preparation of outputting the usual MediaWiki Username (talk | contribs) HTML.
 * 5) outputs the value of various bits within the response.revision.author structure.
 * 6) Uses the   helper function to output some message strings.
 * 7) Then uses the   helper function (described below) to render the revision.content.

General template structure
In handlebars templates:
 * includes in another template
 * outputs the string value of some key in the current "this" scope
 * inside the curly braces eats whitespace, thus eats all whitespace before the   and   eats all trailing whitespace.
 * invokes a block function, like  above. Flow has its own block helper function, like
 * for comments
 * for comments

Note that templates aren't JavaScript (or PHP). You can't have expressions in template parameters. For example,  won't work. You have to supply appropriate values to the template, or in this case nest #if blocks.

In general one main template includes other templates, often in #if or #each or #eachPost blocks.

PHP
We compile the templates into PHP in advance by make compile-lightncandy, and we check the compiled templates into git.

On your development server, set  to compile templates as needed (you may have to change permissions on handlebars/compiled.

JavaScript
The API response is in JSON.

We use a ResourceLoader add-on in extension:Mantle allowing us to specify "resource blobs". This makes the template available to front-end. See that extension's page for some documentation.

TODO compile JS templates in advance? Use ResourceLoader to do this?

Helper functions
Handlebars helper functions have to be re-implemented in JavaScript (in ) and PHP in order for a template to work both front-end and back-end.

In a template,  in a template invokes. You can also call helpers with key-value parameters:.

E.g. invokes the   helper function to render HTML (rather than escaping '<' and '>', etc.).

Another example, the template code invokes the  helper to get the timestamp from the UUID   and format it using the 'time_ago' i18n message.

It's fine to create a helper which in turn calls a template with extra parameters.

In JavaScript
In JS, you define the helper implementation, then call   to associate it with the helper helpername in Handlebars templates.

SafeString is for when you know you're outputting safe HTML, it's used by helper.

Wiring up functions in JavaScript
flow-handlebars.js wraps Mantle extension which provides a generic interface to JS templating.

will return a DocumentFragment of the named template rendered using the data in the provided object. It caches the compiled template, so there's no reason to get templates before rendering them.

Note: Seems _tplcache[ name] duplicates Mantle's cache of compiled templates!?

Init
flow.js runs  for every component on the page.

It looks for data-flow-component="some_name" tags in the page HTML, then instantiates that component's class

E.g. a mention of data-flow-component="board" on page will initialize the FlowBoardComponent JavaScript object.

flow-board.js has  That's how the data-flow-component "board" and class FlowBoardComponent are glued together. The class extends, does the usual OO dance of constructor, calling parent (FlowComponent), creating an object.

FlowBoardComponent is currently the only component.

Associating actions
Typical front-end interaction code locates some elements in the HTML with selectors and binds various actions to them. Flow has a better way:


 * 1) give flow-xxx classes to HTML elements in templates that indicate that something should be done
 * 2) a data-flow-yyy attribute on the same HTML element can give information, e.g. the handler to be called.
 * 3) declare the handler function in the appropriate handlers part of the FlowBoardComponent so flow.js can find it.

Load handlers
For example


 * in HTML
 * define the function

It will be invoked at load time for that element. Flow load handlers include topicElement, timestamp

Click handlers
Similarly, for click interactions and on click this will be called. Flow click handlers include cancelForm, topicCollapserToggle
 * in HTML  on elements
 * define the function

The class name is only necessary for non-interactable elements. A, BUTTON, and INPUT can all have the  alone.

General approach: bind on the root container rather than individual bits of HTML, let the event bubble up to this and then call the component function.

Example: add an interactive handler function for music, define in Handlers.xxx

add to some .html.handlebars template

See

API handlers

 * in HTML code
 * in JavaScript, add the callback for the API request as

For example you would add this to some  link, The built-in handler  intercepts the usual link/form target and submits the anchor or the form as an API call. It parses out the URL and turns the GET params or the form parameters into api params and turns it into an API call. There's a translation layer to translate from URL/Form paramerters to API parameters. Your  function is called when the API call returns. Typically that would replace part of the page with the results of the API call, by rendering parts of the JSON response using templates.

The apiHandler callback handles both success and error state.

Questions
 * How does Flow know what the API call is? It seems   converts an existing static URL into an API call, but I don't see anything that calls this?

api pre-handler
Sends an overrideobject to the API to modify the request parameters.

If you need one of these you typically give it the same name as your api handler callback.

e.g. the  api handler has a matching  api pre-handler which returns some API parameters for editing header that are not in the   URL parameters. Another example: the  API pre-handler changes the   from the default   to.

Register these with

Can return  to stop an API request from being made, e.g. form isn't ready.

Other

 * data-flow-initial-state= collapsed / hidden
 * sets initial state of a Flow form (such as textarea + Cancel Preview Reply) in a Flow board component to be collapsed or hidden

Debugging
Comment out a line in View.php to view the JSON output before the PHP template.

If you see [Object object] in output instead of the contents you expect, it's because the API return value was some complex object. Templates expected a string.

Best practices
Handlebars has implicit triple-braces to output pre-computed HTML. This is easy to miss, so invoke an explicit  when we want to output HTML

TODO: have a flag to disable this?