Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UMD dist file? #2

Open
rafde opened this issue Jun 2, 2017 · 17 comments
Open

UMD dist file? #2

rafde opened this issue Jun 2, 2017 · 17 comments
Assignees
Labels

Comments

@rafde
Copy link

rafde commented Jun 2, 2017

I read the last part of the readme but it would be nice if this can be used by browsers via UMD.

@atomictag atomictag self-assigned this Jun 2, 2017
@atomictag
Copy link
Owner

atomictag commented Jun 2, 2017

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

@maxymajzr
Copy link

I'd be interested in this.

@evil-shrike
Copy link

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 browserify/browserify#1832.

May I ask how do you use the lib in runtime?

@atomictag
Copy link
Owner

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.

@evil-shrike
Copy link

thanks. As I removed usage of html-minifier and fs in transpiler\index.js then Browerify starts working.

@evil-shrike
Copy link

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);

@atomictag
Copy link
Owner

atomictag commented May 21, 2018 via email

@evil-shrike
Copy link

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.

@evil-shrike
Copy link

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;
    }
  };

@atomictag
Copy link
Owner

Are you sure you are correctly invoking the template function? It should be something like

IncrementalDOM.patch(someElement, templateFn, someData);

@evil-shrike
Copy link

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?

@atomictag
Copy link
Owner

have a look at the examples perhaps that can help clarifying things a bit

@evil-shrike
Copy link

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*/}}} );

@atomictag
Copy link
Owner

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 : ... });

@evil-shrike
Copy link

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)

@atomictag
Copy link
Owner

I use it all the time i guarantee it works - you may just need to try out a few patterns that suit your needs

@evil-shrike
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants