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
Asset Handling #115
Comments
I have implement a version which is cable of doing this kind of stuff. However the implementation is hard to achieve within the current setup of Pebble. Some portion of the template requires a second rendering phase. Meaning we render some portion of the template twice! This has to do with the fact that you can define certain assets after you include them. Hence we need a first phase in which we collect the assets and in the second phase we include the assets at the right location. If such a feature should be added to the core of Pebble we need to change the way we render the template. We need to introduce a second phase which allows to parse all the tags before we actually starting with the rendering of the template. |
You should leave the bundling of resources to a tool like webpack or browserify? Pebble is a rendering engine, not a framework. |
@pacey you are right that Pebble is not a framework. However I see use cases where you want to collect the CSS and JS and produce one single tag at the end of the page (or for CSS in the head). You can collect those things with other solutions. There I see two options:
For both solutions I see some serious problems:
As I said I have implement the solution for us already and it seems to work quite well. |
I had the same discussion with a friend of mine (@balamaci). My friend suggest me to use webpack or browserify. From what I read I understand that browserify depends on nodejs. I am very curious to see a basic demo application that integrates webpack/browserify with java. Another solution from my point of view is to use an web optimizer wrote in Java (for example https://github.com/wro4j/wro4j) but to be honest I prefer a javascript solution if this solution is easy to integrate. Recently I released on github (https://github.com/decebals/matilda), one of my application that uses Pippo and Pebble. I made this move to try to find together with the Pippo's comunity some of best practices related to how to build an web application using this micro web framework. I think that it's good to improve the performance of the application with a such functionality.
Can you share with us some code? @pacey |
I will share the code, but for this I need to be sure that it will go into the core. Since it is a bit of work to extract it from our system. Actually what you really what is an example of how to use it. So I add below some example templates and the resulting output: File: base.twig
File: other.twig
The output of the include statements can be controlled via Java. The example below would require some implementation which uses a CSS / JS compile which combine all files together. The implementation of the concrete output can be changed depending on the use case:
The only thing you need to write is a controller which can handle the aggregation of all the provided css and JS files. The real advantage of this asset tags is that you can include your resource where you use them in the templates and you do not need to include them somewhere else. |
So we use Gradle to build our application and have made a node proxy plugin that can call node scripts from the Gradle scripts. When we do a full build we attach the Webpack build into the processResources task I think, and then copy them into the build/resources directory so they are on the classpath when it gets archived into a jar. In Webpack we create a Js and CSS bundle for each page, so that each page only has to make 1 request for the Js file and 1 request for the CSS file. We use the ES6 module definition of import, export etc. and Webpack can follow your dependency tree to build up bundled java script file (which is awesome). If you use Gradle too I can share the plugin we wrote to call the node scripts, if it help you guys. |
@thomashunziker, This sounds like a really interesting idea but I'm a little hesitant. Because Pebble is an all-purpose template engine it can be used for generating any type of textual output, not just HTML. It can be used to generate CSV, JS, XML, SQL, etc., and because of that I'm always reluctant to add new features that are specific to HTML. I would much rather see it implemented as an optional third-party extension, at least at first. I do, however, see why the requirement of a second rendering phase makes that impossible with the current state of Pebble. My preference would be to make the minimal amount of change to the core that will give third-party extensions the power to do this; perhaps give the extensions some sort of "pre-render" phase where they have access to the template and the user-provided data before it gets rendered. Do you think a "pre-render" phase would be enough for you to be able to implement your idea as an extension? |
On second though, a "pre-render" phase would probably not suffice. The import tag supports dynamic expressions which won't be evaluated until the actual "render" phase so the extension wouldn't have access to the imported templates. I suppose it would have to be some sort of a "post-render" phase but what could we provide the extension without it having to resort to parsing the already-generated HTML? Hmm. |
In our implementation we trigger the rendering twice. The first time we use a 'NullWriter'. So actually we render the template twice. This works. However in theory this can be optimized when certain tags, filter etc. are aware of this pre-rendering phase, because those tags could skip certain stuff. |
@pacey I don't use Gradle but from your description I see your solution a little bit complicated. @mbosecke One or more extensions with stuff related to html sounds good for me. I use Pebble because I have not found anything better than Pebble to help me generate html pages. @thomashunziker Your example looks good for a starting discussion. I prefer multiple Now my code looks like:
where Sure I can add an asset as static block if I know the path to that resource. The new Pebble |
I'm just kind of thinking out loud here as a way of getting my thoughts down regarding @thomashunziker's original proposal: Problem to solveAllow INCLUDED templates to add content to designated sections of the original template. Blocks are more limited because a template has to explicitly extend a parent template in order to override it's blocks plus multiple inheritance isn't supported. Also, the existing block system completely OVERRIDES a parent block whereas it would be nice to include an arbitrary amount of templates that each APPEND content to a particular section. New tags
Naming is up for debate. Implementation:
Examplebase.html:
module1.html:
module2.html:
result:
Considerations
|
The two phase model solves the issue with the rendering order. A second rendering is not required anymore. However do not expect to much out of it, because you need also to evaluate all control flow tags (such as if, for etc.) in phase 1. So you can skip certain tags, but most you need to evaluate normally. We need at least two tags:
The first one is easy. It only records what it finds within the first phase. The second is a bit more complicated because it really depends on what you try to achieve. For CSS / JS you want to combine the recorded files into a single URL. Normally you need to attach a hash. In our case we even compress the URL string to reduce the length. Therefore I recommend the ability to register a handler for the generation of the output. Our handler interface looks like:
This way everyone can inject a different strategy to handle the assets. Our implementation looks like:
Eventually we can convert the above concept into a more generic one which can also be used for other stuff than CSS / JS. Eventually in other use cases such as e-mail generation we face similar issues. |
You're right about having to evaluate all control flow statements during the first phase, I didn't think of that. That's very worrisome, because I would need somewhere to store all the evaluated results so that all these nodes don't need to be re-evaluated during the second phase. This is a big red flag that this might be more effort than it's worth. But as for the "Handler" interface, Pebble already has functionality that allows you to take an input and manipulate it to provide a custom output, which is by using a "filter". Here's how your parent.html:
module1.html:
module2.html:
custom filter:
result:
So I still think the I just need to think more about where to store all those evaluated results during the first rendering phase so that the relevant nodes don't have to be re-evaluated during the second phase. |
To avoid to execute to much twice I introduced this assetSection tag to limit the scope of the second rendering. I see two options:
It's a trade-off between memory and CPU. |
It sounds good, but in my opinion this is out of the scope of a template engine. Still, for a 100% java solution (with nodejs as lib) see: https://github.com/eclipsesource/J2V8 I built an asset module on top of J2V8 for Jooby My asset module works with the template engine of your choice and of course next release of Jooby comes with pebble: jooby-project/jooby#247 |
I'm still a fan of the idea of being able to create content in one section of a template and having it "hoisted" to another part of the template but the idea of virtually doubling the render time and making such large architectural changes just doesn't seem worth it. I'm closing this for now. |
In a typical web scenario you want to add external resources (e.g. CSS, JavaScript etc.) to the produced output.
For HTML5 all CSS resources should be placed within the 'head' tag. The JavaScript resources should be placed before the closing 'body' tag to improve the speed of the loading. Additionally those resources should be combined together into one file to reduce the number of HTTP requests to the server, because the browser is allowed only to open two connections per domain (see HTTP specification).
Hence whats happening is typically all resources are manually combined and added to the template. But this is hard with twig since you will add more resources in other child templates. E.g. there is a base.twig from which all other templates extends. From a practical standpoint of view it would be easier when each sub template and included template can add new resources and the base.twig does not need to know anything about the additional resources. This way only those resources are included in the HTML page which are actually required and not all potential resources which eventually are required.
We are currently implementing such a solution. Is there any interest that I create a pull request for this feature?
The text was updated successfully, but these errors were encountered: