Open rafde opened 7 years ago
That would possible, although obviously not recommended in production due to the additional computation required to internalize the input. Agree it would be nice to be able to run everything in a browser, though, esp. for testing and tinkering. My argument against this was that precompiling templates as per the supplied examples is fairly trivial to do / modify, but if there's enough interest (not enough atm to justify the effort) I can provide a browser version at some point
I'd be interested in this.
but if there's enough interest (not enough atm to justify the effort) I can provide a browser version at some point
+100 please! ) It'd be really nice to have a browser-ready build. I tried to combine all scripts with Browserify but failed. browserify-ed version failed on load somewhere deep inside uglifyjs with error:
Uncaught TypeError: require.resolve is not a function
It's a known issue with UglifyJS https://github.com/browserify/browserify/issues/1832.
May I ask how do you use the lib in runtime?
Unfortunately atm I am a bit swamped with things to look into this. The versions I use are slightly different from the one in this repo for some reason, so it's a bit tricky to "port" the browser version here. However, as you can see the dependencies are not that special - and there in principle no reason why a browserified version of this could not work with minor tweaks. iIRC html-minifier was a bit of a PITA for some reason so you may want to disable that temporarily to get things going.
thanks. As I removed usage of html-minifier
and fs
in transpiler\index.js then Browerify starts working.
just removing "auto registration emmiters" in transpiler\index.js
makes the lib to be just Handlebars - it behaves as w/o backend specified. So I had to add explicit import of idom backend:
var emitter = require('./backends/idom');
TemplateTranspiler.registerBackend('idom', emitter);
That makes sense, since you're not using fs any longer. Let me know how it works - and bear in mind that full in-browser transpile+compile may get prohibitively slow for production (but still pretty fun, tho :))
On 21 May 2018, at 20:48, Sergei Dorogin notifications@github.com wrote:
just removing "auto registration emmiters" in transpiler\index.js makes the lib to be just Handlebars - it behaves as w/o backend specified. So I had to add explicit import of idom backend:
var emitter = require('./backends/idom'); TemplateTranspiler.registerBackend('idom', emitter); — You are receiving this because you were assigned. Reply to this email directly, view it on GitHub, or mute the thread.
Thanks for your interest. And thanks for the lib by the way. It looks like very interesting piece of software.
Previously with Handlebars I loaded all template via my requireJS-plugin that compiles templates on the fly. At the same time for production they were compiled in build-time and plugins detected that and did nothing. So all other code isn't dependent on the fact whether templates are compiled or not. I want to keep this approach. Unfortunately I have lots of block helpers which returns html strings and all they won't work any more. So I'm far away from something working. But it seems that the lib itself works - I can see compiled js code with IncrementalDOM calls.
BTW I think it makes sense to mention in the README that apps should import IncrementalDOM
globally.
If you wonder what kind of helpers I have here's some details. For example my templates have code like:
{{render someProp}}
that means the view should take a object from property someProp
and treat it as a nested view - so that call its render
method. But during helper execution it's not possible (as we have string and no DOM element available yet - it's needed for render). So render
helper return a simple html stub (like <span id="some-generatd-id"></span>
) and adds some callback into context associate with the current view. Then (after html inserter into DOM) the view executes all callbacks, and the added callaback finds its stub as DOM element and execute render
method for appropriate object.
Hi. Well, it turned out that I have difficulties even without any custom helper. Any advice is appreciated.
Given a HB template containg only html (no helpers at all)
The templates is compiled with a compile
call:
let template = Handlebars.compile(data, { transpilerOptions : { backend: "idom" }, data: true})
then that tmpl
is executed:
markup = template(viewModel, { data: data });
data
is:
backend:"idom"
callbacks:[]
context:{}
view:View
I never got a result from template()
function. It fails somewhere inside incremental-dom
Here's template function:
(function anonymous(container,depth0,helpers,partials,data
) {
return (IncrementalDOM.elementVoid("ul", "idom-1", ["class", "x-breadcrumb"]) & IncrementalDOM.elementOpen("div", "idom-2", ["class", "x-areas-container container"]) & IncrementalDOM.elementOpen("div", "idom-3", ["class", "x-area", "data-area-options", "isDefault:true", "style", "display: none;"]) & IncrementalDOM.elementVoid("div", "idom-4", ["class", "x-region", "id", "mainmenu"]) & IncrementalDOM.elementVoid("div", "idom-5", ["class", "x-region", "data-region-options", "navigable:true", "id", "main"]) & IncrementalDOM.elementClose("div") & IncrementalDOM.elementOpen("div", "idom-6", ["class", "x-area", "data-area", "admin", "style", "display: none;"]) & IncrementalDOM.elementVoid("div", "idom-7", ["class", "x-region", "data-region", "mainmenu"]) & IncrementalDOM.elementVoid("div", "idom-8", ["data-region", "main", "class", "x-region", "data-region-options", "navigable:true"]) & IncrementalDOM.elementClose("div") & IncrementalDOM.elementOpen("div", "idom-9", ["class", "x-area", "data-area", "about", "style", "display: none;"]) & IncrementalDOM.elementOpen("p", "idom-10", null) & IncrementalDOM.text(" ") & IncrementalDOM.elementClose("p") & IncrementalDOM.elementOpen("header", "idom-11", ["class", "slick-box-container"]) & IncrementalDOM.elementOpen("div", "idom-12", ["class", "slick-box"]) & IncrementalDOM.elementOpen("p", "idom-13", null) & IncrementalDOM.text("Ajax WebClient Demo") & IncrementalDOM.elementClose("p") & IncrementalDOM.elementClose("div") & IncrementalDOM.elementClose("header") & IncrementalDOM.elementOpen("p", "idom-14", null) & IncrementalDOM.text(" ") & IncrementalDOM.elementClose("p") & IncrementalDOM.elementVoid("div", "idom-15", ["data-region", "about-history", "class", "x-region x-scroll-accel-bottom x-scroll-accel-top"]) & IncrementalDOM.elementClose("div") & IncrementalDOM.elementClose("div") & IncrementalDOM.elementVoid("div", "idom-16", ["class", "clearfloat"]) & IncrementalDOM.elementVoid("div", "idom-17", ["class", "footer-stub"]));
})
it fails on first IncrementalDOM.elementVoid
call with the following stack:
TypeError: Cannot read property 'firstChild' of null
at getNextNode (incremental-dom.js?v=2.36.0-SNAPSHOT-180320T1423-20180521T2002:722)
at nextNode (incremental-dom.js?v=2.36.0-SNAPSHOT-180320T1423-20180521T2002:730)
at coreElementOpen (incremental-dom.js?v=2.36.0-SNAPSHOT-180320T1423-20180521T2002:754)
at elementOpen (incremental-dom.js?v=2.36.0-SNAPSHOT-180320T1423-20180521T2002:1005)
at Object.elementVoid (incremental-dom.js?v=2.36.0-SNAPSHOT-180320T1423-20180521T2002:1156)
in getNextNode
the field currentParent
is null:
var getNextNode = function () {
if (currentNode) {
return currentNode.nextSibling;
} else {
return currentParent.firstChild;
}
};
Are you sure you are correctly invoking the template function? It should be something like
IncrementalDOM.patch(someElement, templateFn, someData);
Thanks, definitely I'm not. So HB-like approach:
markup = that.template(that.viewModel || that, { data: data });
$container.html(markup);
is becoming:
IncrementalDOM.patch($container[0], that.template.bind(that), viewModel);
but let me ask what's about the second argument for HB-template function - data
(see http://handlebarsjs.com/block_helpers.html#block-params). Where is it going in the new idom-approach?
have a look at the examples perhaps that can help clarifying things a bit
thanks it's helpful, I should look there at start.
but as I can see there's no way to pass additional options into templates as it was be possible with HB.
What I mean. registerHelper
's callbacks have context
and options
args. context
is an object the template is being applied to, kinda viewmodel. And options
is:
fn: TemplateDelegate;
inverse: TemplateDelegate;
hash: any;
data?: any;
fn
, inverse
and hash
are taken from template, but data
can be an arbitrary object passed via template function as second arg.
That second argument for template function is documented here - http://handlebarsjs.com/execution.html
Besides arbitrary data we could pass additional helpers and partials.
With ibar/idom it seems not possible due to patch
method accept only one arg with data.
But ibar's compile return a function that expect two args:
function ret(context, execOptions) {
if (!compiled) {
compiled = compileInput();
}
return compiled.call(this, context, execOptions);
}
that execOptions
seems exactly what I need. Currently is always undefined. The only problem is how to pass it via IncrementalDOM.patch
:
var patchInner = patchFactory(function (node, fn, data) {
currentNode = node;
enterNode();
fn(data);
exitNode();
return node;
});
So I would suggest to pack context and options into data
. To distinguish that case from the simple one (the current) we can tell via specifying data:true
argument for compile
:
Handlebars.compile(data, { transpilerOptions : { backend: "idom" }, data: true})
that would mean - data
argument for patch
will contain context
and options
and so instead of:
return compiled.call(this, context, execOptions);
ibars would call:
function ret(data) {
return compiled.call(this, data.context, data.options);
}
and then:
IncrementalDOM.patch(container, template, {context: viewModel, options: {data: { /*some data for helpers*/}}} );
Incremental DOM wants a function and a context. But that does not mean you cannot pass options to the template, you just need to do it in a slightly different way - like curry them etc. Example
var template = ....
var templatefn = function(options) {
return function(context) {
return template(context, options);
}
};
....
IncrementalDOM.patch(container, templatefn({ data : .... }), { something : ... });
Yeah, I've just discovered that as well, but unfortunately it's not enough as template function returned by compile
method is a wrapper which should support two args as well.
So to make the approach you mentioned to work I had to change code in i-bars:
function ret(context) {
// skipped
function main(context , options) {
var opts = container.merge(options || {}, data); // this is my change
return '' + templateSpec.main(container, context, container.helpers, container.partials, opts, blockParams, depths);
}
main = executeDecorators(templateSpec.main, main, container, options.depths || [], data, blockParams);
return main(context, options);
}
In the main
wrapper I added merging options
and data
(initially options
is commented)
I use it all the time i guarantee it works - you may just need to try out a few patterns that suit your needs
I use it all the time i guarantee it works
Did you use compile
in the browser? As I can see all samples compile templates via precompile
in build-time and import templates as js-code in run-time. I'm trying to compile template on the fly.
And it seems it's not working as intended.
As I can see ibars' compile
returns a function which ultimately calls into the Handlebars' compileInput
func:
function compileInput() {
var ast = env.parse(input, options),
environment = new env.Compiler().compile(ast, options),
templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true);
return env.template(templateSpec);
}
That compileInput
returns a function from Handlebars:
function main(context , options) {
return '' + templateSpec.main(container, context, container.helpers, container.partials, data, blockParams, depths);
}
templateSpec.main
is ibars' generated code with i-dom calls but wrapping it with Handlerbars's main that returns string seems weird for me. As in the end we call for HB's parse and compile for already compiled input, and template function returns string. Is it intended behavior?
I also have a question about migrating code like <div {{helper..}}>
but will ask it in a separate issue.
I read the last part of the readme but it would be nice if this can be used by browsers via UMD.