Skip to content
This repository was archived by the owner on Apr 4, 2019. It is now read-only.

[WIP] Reactive rerendering #282

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ba63f61
Add ability for templates to reuse morphs
Feb 3, 2015
7d7aa98
Encapsulate state needed for re-render
Feb 3, 2015
d65b07c
Extract static cached fragment logic to runtime
tomdale Feb 4, 2015
a3d7112
Break apart hydration code into phases
tomdale Feb 4, 2015
b3730bd
Introduce element morphs
tomdale Feb 5, 2015
2b0312f
Fix failing tests
tomdale Feb 5, 2015
1066f32
renderNodes -> buildRenderNodes
Feb 5, 2015
fe8096e
Too many hooks.
Feb 5, 2015
97f0caf
Guard if no parent and no contextualElement
Feb 5, 2015
81b787a
Expose public API for easy rerendering
Feb 5, 2015
ddccc30
Block helpers mutate in place rather than return
Feb 6, 2015
de981e2
Rename rerender to revalidate
Feb 6, 2015
2fd475f
Simplify template rendering in block helpers
Feb 6, 2015
ff0dfea
Propagate ownerNode through the render nodes
Feb 6, 2015
61a2fe9
Maintain consistent “shape” for options hash
Feb 6, 2015
7098b6b
`get` helper should get render node for dirtying
Feb 7, 2015
13b8337
Introduce dirtiness to render node invalidation
Feb 7, 2015
1ce5d4a
Defer more of rendering to the runtime
wycats Feb 8, 2015
2ecb7e6
Remove unnecessary inlined render function
wycats Feb 8, 2015
cf27996
Use extracted morph-range library
Feb 9, 2015
5a223d1
Ensure templates always have stable boundaries
Feb 10, 2015
4082379
Clean up scope handling
wycats Feb 10, 2015
5c06422
Make ordering of scope/env consistent
wycats Feb 11, 2015
613babc
Tighten up argument order
Feb 12, 2015
fe711cc
Provide `this.yield` sugar in block helpers
Feb 12, 2015
4ee6315
Hooks don't need to understand contextualElement
Feb 13, 2015
e836007
Start writing host hook documentation
Feb 13, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions demos/compile-and-run.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@
var compiler = requireModule('htmlbars-compiler'),
DOMHelper = requireModule('dom-helper').default,
hooks = requireModule('htmlbars-runtime').hooks,
helpers = requireModule('htmlbars-runtime').helpers;
helpers = requireModule('htmlbars-runtime').helpers,
render = requireModule('htmlbars-runtime').render;

var templateSource = localStorage.getItem('templateSource');
var data = localStorage.getItem('templateData');
var shouldRender = localStorage.getItem('shouldRender');

if (templateSource) {
textarea.value = templateSource;
Expand All @@ -46,13 +48,19 @@
dataarea.value = data;
}

if (shouldRender === "false") {
skipRender.checked = true;
}

button.addEventListener('click', function() {
var source = textarea.value,
data = dataarea.value,
shouldRender = !skipRender.checked,
compileOptions;

localStorage.setItem('templateSource', source);
localStorage.setItem('templateData', data);
localStorage.setItem('shouldRender', shouldRender);

try {
data = JSON.parse(data);
Expand All @@ -70,10 +78,10 @@
var templateSpec = compiler.compileSpec(source, compileOptions);
output.innerHTML = '<pre><code>' + templateSpec + '</code></pre>';

if (!skipRender.checked) {
if (shouldRender) {
var env = { dom: new DOMHelper(), hooks: hooks, helpers: helpers };
var template = compiler.compile(source, compileOptions);
var dom = template.render(data, env, output);
var template = compiler.template(templateSpec);
var dom = render(template, data, env, { contextualElement: output }).fragment;

output.innerHTML += '<hr><pre><code>' + JSON.stringify(data) + '</code></pre><hr>';
output.appendChild(dom);
Expand Down
17 changes: 15 additions & 2 deletions packages/dom-helper/lib/main.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Morph from "./morph-range";
import Morph from "../morph-range";
import AttrMorph from "./morph-attr";
import {
buildHTMLDOM,
Expand Down Expand Up @@ -105,6 +105,15 @@ function buildSVGDOM(html, dom){
return div.firstChild.childNodes;
}

function ElementMorph(element, dom, namespace) {
this.element = element;
this.dom = dom;
this.namespace = namespace;

this.state = {};
this.isDirty = true;
}

/*
* A class wrapping DOM functions to address environment compatibility,
* namespaces, contextual elements for morph un-escaped content
Expand Down Expand Up @@ -346,6 +355,10 @@ prototype.createAttrMorph = function(element, attrName, namespace){
return new AttrMorph(element, attrName, this, namespace);
};

prototype.createElementMorph = function(element, namespace){
return new ElementMorph(element, this, namespace);
};

prototype.createUnsafeAttrMorph = function(element, attrName, namespace){
var morph = this.createAttrMorph(element, attrName, namespace);
morph.escaped = false;
Expand All @@ -357,7 +370,7 @@ prototype.createMorph = function(parent, start, end, contextualElement){
throw new Error("Cannot pass a fragment as the contextual element to createMorph");
}

if (!contextualElement && parent.nodeType === 1) {
if (!contextualElement && parent && parent.nodeType === 1) {
contextualElement = parent;
}
var morph = new Morph(this, contextualElement);
Expand Down
30 changes: 3 additions & 27 deletions packages/htmlbars-compiler/lib/compiler.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/*jshint evil:true*/
import { preprocess } from "../htmlbars-syntax/parser";
import TemplateCompiler from "./template-compiler";
import { wrap } from "../htmlbars-runtime/hooks";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't be importing from htmlbars-runtime from within htmlbars-compiler for anything other than tests (the runtime is not available within Ember for example). If the wrap function needs to be shared and available to compiler (and ultimately to the consuming library) it should be in htmlbars-utils.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. If utils is imported into both runtime and compiler, that's the right place for this for sure.

import render from "../htmlbars-runtime/render";

/*
* Compile a string into a template spec string. The template spec is a string
Expand Down Expand Up @@ -65,31 +67,5 @@ export function template(templateSpec) {
* @return {Template} A function for rendering the template
*/
export function compile(string, options) {
return template(compileSpec(string, options));
}

/*
* Compile a string into a template spec string. The template spec is a string
* representation of a template. Usually, you would use compileSpec for
* pre-compilation of a template on the server.
*
* Example usage:
*
* var templateSpec = compileSpec("Howdy {{name}}");
* // This next step is basically what plain compile does
* var template = new Function("return " + templateSpec)();
*
* @method compileSpec
* @param {String} string An htmlbars template string
* @return {Function} A template spec string
*/
export function compileSpec(string, options) {
var ast = preprocess(string, options);
var compiler = new TemplateCompiler(options);
var program = compiler.compile(ast);
return program;
}

export function template(program) {
return new Function("return " + program)();
return wrap(template(compileSpec(string, options)), render);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ FragmentJavaScriptCompiler.prototype.compile = function(opcodes, options) {
this.namespaceFrameStack = [{namespace: null, depth: null}];
this.domNamespace = null;

this.source.push('function build(dom) {\n');
this.source.push('function buildFragment(dom) {\n');
processOpcodes(this, opcodes);
this.source.push(this.indent+'}');

Expand Down
Loading