Comments (15)
I appreciate your feedback. Lots of good points. I agree functions would be great -- as well as assignments.
Master pages -- I personally use the closure syntax -- have you given that a try? You can use any number of other templates and include them.
other.template(param1) -> {
}
Or if you don't need to pass along a block to render you could just do
other.template(param1)
You're correct that the main issue w/ hot reloading is the strongly typed construction between the controller & view code. A potential compromise is 1) adding a weakly-typed alternative or 2) only hot-reloading the logic, but if you change the template interface then you'd need to restart your app. Maybe with signature-checking magic, a friendly error could be rendered that we detected the interface has changed and you need to restart the app.
from rocker.
Thanks for your comments.
For the second, I was thinkg of option 1, provide a weakly typed method entry to the generated classes, that supports a Map<String,Object> as a unique parameter maybe.
This way, we could completely break the dependency between project and generated classes.
Something like getTemplate(templateName).render(map), as it is done in ninja framework.
For the master pages, this is not quite the same, as master page allows to define more than one section, providing a simple way to build complex pages with a lot of holes in it.
@*
Example of common header and footer
*@
@args (String title, RockerBody content)
@section("before")
Header with title @title
@content
Footer
@section("after")
and
@*
Example of index that uses common header and footer
*@
@args (String message)
@views.main.template("Home") -> {
@render("before") -> {
This is berfore Header
}
Hello @message!
@render("after") -> {
This is after Footer
}
}
from rocker.
The more I think of it, the more I believe it should be possible to do it, without being to difficult !
index.java
@Override
protected void __render() throws IOException, RenderingException {
// ...
__internal.renderValue(main.template("Home")
.__section("before", () -> {})
.__section("after", () -> {})
.__body("after", () -> {})
);
// ...
}
main.java
public main() {
super();
__internal.setCharset("UTF-8");
__internal.setContentType(ContentType.HTML);
__internal.setTemplateName("main.rocker.html");
__internal.setTemplatePackageName("views");
__section("before", () -> {
__internal.writeValue("This is before Header default content");
});
}
//@Override <= this should exists by default, not something that should be declared in templates
public main __body(RockerContent body) {
this.__content = body;
return this;
}
//@Override <= this should exists by default, not something that should be declared in templates
public main __section(String name, RockerContent body) {
this.__sections.put(name, body); // will replace default value
return this;
}
@Override
protected void __render() throws IOException, RenderingException {
// ...
// what if section has not been overridden?
// we should render default value, and if null then nothing - of course !
__internal.renderValue(this.__sections.get("before"));
__internal.renderValue(this.__content);
__internal.renderValue(this.__sections.get("after"));
);
// ...
}
main.rocker.html
@args (String title)
<html>
...
@section("before") { // default content, displayed only if not overridden
default before
}
...
@section or @ section() ?
...
@section("after") // no default content, so default is empty
...
</html>
index.rocker.html
@main.template("Home") -> {
@render("before") -> {
This is berfore Header default content
}
Hello @message!
@render("after") -> {
This is after Footer
}
}
...
</html>
Order of @render should not disturb the way it works, I can't remember the rule in .net razor for this, but here we can decide that it does not matter.
from rocker.
Now with this + another class with hot reloading strategy + a few other minor things to find, and we can have a very very good templating engine :)
from rocker.
This is my perception of work to do now...
MUST master pages, of course
MUST @locale and @i18n
MUST @def to regroup functions in a single template, maybe generate subclasses ?
MUST @break and @continue in @for loops
SHOULD @raw() -> {}, and @html @js @json @xml @csv @css => maybe using providers for all these ?
SHOULD @transformer (like extensions in .net - very very powerfull), in that case also allows to "string".raw()
SHOULD @compact and @nocompact
NICE @cache
NICE @invoke for dynamic invocation of @def or templates
NICE @return and @returnIf to immediatly exit rendering
NICE @verbatim
I see here two point of extensions :
- for @raw @js ... which in fact defines how content should be rendered
- for @transformer... which allows to add static utilities and call them with obj.util() instead of StaticClass.util(obj)
See http://rythmengine.org/doc/developer_guide.md#extension for transformers
What do you plan for rocker in the future, is it worth to invest time to work on this ?
from rocker.
https://github.com/jmarranz/relproxy/ for hot-reloading, looks very nice, isn't it ?
from rocker.
@momiji Love your feedback. If you have any interest in implementing some of those features, I'd welcome pull requests. Things like @return and @break and @continue are pretty good, simple ideas. Actually not too bad to implement either. Of course, unit tests for everything is critical.
from rocker.
@momiji You'll want to checkout version 0.10.0 which I released a couple hours ago. Hot reloading support in a couple flavors. README is updated with info on it.
Regarding sections, did you take a look at content chunks? I use those to build complex pages. Or I simply write another "method" in its own template file and can call it where needed. Anyway, content chunks looks like this in views/main.rocker.html
@args (String title, RockerContent extracss, RockerContent extrajs, RockerBody content)
<html>
<head>
<title>@title</title>
<link rel="stylesheet" href='@N.assetsAt("plugins/bootstrap/css/bootstrap.min.css")' >
@extracss
<body>
@content
<script type="text/javascript" src='@N.assetsAt("js/jquery-1.10.2.min.js")'></script>
@extrajs
</body>
</html>
This can be used like so in views/index.rocker.html
@args ()
@extracss -> {
<link rel="stylesheet" href='@N.assetsAt("css/main.css")' >
}
@views.main.template("Home", extracss, RockerContent.NONE) -> {
<h1>Hello!</h1>
}
This will result in rendering
<html>
<head>
<title>Home</title>
<link rel="stylesheet" href='@N.assetsAt("plugins/bootstrap/css/bootstrap.min.css")' >
<link rel="stylesheet" href='@N.assetsAt("css/main.css")' >
<body>
<h1>Hello!</h1>
<script type="text/javascript" src='@N.assetsAt("js/jquery-1.10.2.min.js")'></script>
</body>
</html>
from rocker.
Yes, great work!
Maybe i didn't catch it, but is it possible to invoke controller action method directly from within the template and fill the render result content in place:
@controllers.ArticleController.articleShow()
So that the above statement will invoke the controller @controllers.ArticleController
's action method articleShow()
, and fill the render result content in place of the statement?
I found, that In Play-Framework that is possible in conjunction with rythm-template-engine, as you can read here: https://www.playframework.com/modules/rythm-1.0.0-RC5/user_guide
That would be awesome!
from rocker.
Well, you could make it work. The output of the method you are calling
would need to be a String. I'm not sure of the return type of your
controller, but you could write a utility method that converts it to a
string and then call your controller method, and pass its return value to
your utility method to convert it to a String.
For example,
@com.example.Utils.controllerToString(controllers.ArticleController.articleShow())
would then inline the result.
On Sun, Dec 6, 2015 at 1:07 PM, rranke [email protected] wrote:
Yes, great work!
Maybe i didn't catch it, but is it possible to invoke controller action
method directly from within the template and fill the render result content
in place:@controllers.ArticleController.articleShow()
So that the above statement will invoke the controller
@controllers.ArticleController's action method articleShow(), and fill
the render result content in place of the statement?I found, that In Play-Framework that is possible in conjunction with
rythm-template-engine, as you can read here:
https://www.playframework.com/modules/rythm-1.0.0-RC5/user_guideThat would be awesome!
—
Reply to this email directly or view it on GitHub
#1 (comment).
from rocker.
My controller-method is not returning a String
, it is returning a ninja.Result
.
So, i want to have a controllers template rendered (with all @ -parameters) by the template-engine and would like to "include" the html-output of that rendering-process into another template.
A short and simple example:
My Controller ApplicationController
:
public class ApplicationController {
public Result index() {
return Results.ok().render(
views.index.template("IndexTitle")
);
}
public Result show() {
return Results.ok().render(
views.show.template("ShowName")
);
}
}
Template1 index.rocker.html
:
@import controllers.ApplicationController
@args (String title)
<html>
Hi, i am @title!
Lets see how the rendered template of "show()" looks like:
@controllers.ApplicationController.show()
</html>
Template2 show.rocker.html
:
@import controllers.ApplicationController
@args (String name)
<body>
i am the rendered output with name: @name
</body>
The result of opening http://localhost:8080/index
in a webbrowser should be:
<html>
Hi, i am IndexTitle!
Lets see how the rendered template of "show()" looks like:
<body>
i am the rendered output with name: ShowName
</body>
</html>
How to do that?
from rocker.
A couple thoughts:
- Your example seems like you merely want template reuse, not controller
reuse. So Template1 could simply be:
@Args (String title)
Hi, i am @title! Lets see how the rendered template of "show()" looks like: @views.show.template("ShowName") 1. Maybe your example is too simplified and you really want controller 2. template reuse. While I mentioned you could write a utility to do that and then convert the ninja.Result to a String -- you'd miss out on Ninja's injection of session variables, parameters, etc. that any useful controller method would use. Seems like poor design to try and reuse a controller method in this way. You should probably refactor your controller methods into more generic, non-ninja, reusable methods that you want to access from other controller methods and/or templates.-Joe
On Mon, Dec 7, 2015 at 8:25 AM, rranke [email protected] wrote:
My controller-method is not returning a String, it is returning a
ninja.Result.So, i want to have a controllers template rendered (with all @
-parameters) by the template-engine and would like to "include" the
html-output of that rendering-process into another template.A short and simple example:
My Controller ApplicationController:
public class ApplicationController {
public Result index() {
return Results.ok().render(
views.index.template("IndexTitle")
);
}
public Result show() {
return Results.ok().render(
views.show.template("ShowName")
);
}
}Template1 index.rocker.html:
@import controllers.ApplicationController
Hi, i am @title! Lets see how the rendered template of "show()" looks like: @controllers.ApplicationController.show()
@Args (String title)Template2 show.rocker.html:
@import controllers.ApplicationController
i am the rendered output with name: @name
@Args (String name)The result of opening http://localhost:8080/index in a webbrowser should
Hi, i am IndexTitle! Lets see how the rendered template of "show()" looks like: i am the rendered output with name: ShowName
be:How to do that?
—
Reply to this email directly or view it on GitHub
#1 (comment).
from rocker.
For me, as a backend-developer ("BD"), i have absolutely no idea where exactly the frontend-developer ("FD") wants to place a specific piece of rendered html-code (some java-objects rendered to html-code by the template-engine).
Okay, you think, that my example is too simplified? Yes, you are right :) !
Lets take an example out of the ecommerce-world: A shopping-basket (like an amazon-basket, that lists some products a customer wants to buy).
Lets say, the "FD"-guy wants to place that basket (the content of the basket-Template) in some different places, such as in the footer (the footer-template), the main-page (the main-template) or anywhere else.
For me as a "BD", i have no idea (and i dont care about) where exactly (at which place within the page, the footer or header or...) the basket gets rendered. As a "BD", i only care about that i provide the logic and collect and provide all the data (like user-object, product-objects, basket-object, ...) that is needed ONLY for the purpose to display the shopping-basket.
The "FD"-guy has (and should have) absolutely no idea about which potential data (user-object, product-objects, ...) is needed to calculate and render the shopping-basket-html.
Ideally, the "FD" just calls a simple method "@controllers.ApplicationController.renderShoppingBasket()" to get the rendered html-stuff.
So the interface between the "FD"-guy and the "BD"-guy is this one call to "@controllers.ApplicationController.renderShoppingBasket()", which should return all the rendered html-stuff to display the shopping-basket (and nothing more).
And that call can be flexibly placed ANYWHERE in the homepage, in ANY template, without the requirement of existent variables within the current template.
So that the BD has not to worry about where the FD will finally place the call to the method, and so has not to worry about at the time within the controller, to provide the correct arguments to the template.
The BD has luckily only to calculate and only to provide that stuff, which is directly related to the shopping-basket-data.
So fortunately, the BD therefore has not to calculate and has not to provide any data which is not related to the shopping-cart html-stuff.
This approach should simplify the interface between FD and BD and would increase encapsulation and increases the ability to reuse code.
So that the BD has not to provide the data needed for to render within in controller that is indirectly chosen by the request.
Right now it seems like, that the BD has to calculate, collect and provide ALL the data within the chosen controller (specified in the routes-file), that should get rendered somewhere in the main-template or somewhere in any of the included templates.
What are actually the problems i want so get solved?
The first problem is in the controller:
Right now, unfortunately for me it seems like, that i have to call in EVERY controller (which get called based on the specification of the routes-file) a method, that returns the calculated and collected data to provide ALL the shopping-basket data that should get rendered SOMEWHERE in the main-template or SOMEWHERE in any of the included templates. As a BD, i actually have no idea where exactly this will be printed out. EVERY controller has then to send this calculated data to the template which gets rendered (which brings us to the second problem...)
The second problem is in the templates:
And because this template (which gets rendered after the controller has finished) itself is an include of another template, it has to send the shopping-basket data to its super-template ... and so on). Additionally to that, because the other templates would now need a shopping-basket parameter (argument), we'll have to change every action method in our web-application to pass this parameter. This gets tedious quickly.
What could be the solution?
Right now, i am not quite sure if the above described FD/BD scenario is possible to implement, so that the described problems can be solved and if yes, how to do it.
Or maybe you have just a very short solution to my very long text :)
My Controller ApplicationController:
public class ApplicationController {
public Result index() {
return Results.ok().render(
views.index.template("IndexTitle")
);
}
public String renderShoppingBasket() {
//Get the ninja-Session-object to identify the current user (and get other cookie information)
//query the database for user-object, shopping-basket-object, product-objects
//##############################
// i am not quite sure how to realize the following three steps:
//##############################
//1.) set the template that should get rendered
setTemplateToGetRendered("views.shoppingbasket.rocker.html");
//2.) pass the following arguments to the template, that are used in rendering process.
UnknownType2 renderResult = unknownMethodToRenderTheTemplateByRocker(user-object, shopping-basket-object, products-object);
//3.) get the rendered HTML-stuff as String
String renderResultString = unknownMethodToConvertRenderResultIntoString(renderResult);
return renderResultString;
}
}
Template1 index.rocker.html:
@import controllers.ApplicationController
@args (String title)
<html>
Hi, i am @title!
the shopping-basket looks like:
@controllers.ApplicationController.renderShoppingBasket()
</html>
Template2 shoppingbasket.rocker.html:
@import controllers.ApplicationController
@args (User user-object, Basket shopping-basket-object, List<Product> products-object)
<body>
This is the cart of: @User.name
These are your products:
//loop over the @products-object...
</body>
The result of opening http://localhost:8080/index in a webbrowser should be:
<html>
Hi, i am IndexTitle!
the shopping-basket looks like:
<body>
This is the cart of: Michael
These are your products:
TV, iPhone, iPad
</body>
</html>
from rocker.
Haha omg, one line-of-code did the trick.
public String renderShoppingBasket() {
return views.shoppingbasket.template().render().toString();
}
Found that by just browsing the doku. Please dont say "rtfm" ;)
Thanks anyway for your time and your hints!!
from rocker.
@rranke Glad you figured out a solution, although views.shoppingbasket.template().render().toString();
could be inline rendered inside a template directly by doing something like @views.shoppingbasket.template()
in another template. However, if you wanted other logic then your solution would work as well. Only other comment is that remember render() will escape characters so if you use that string in another template, you'll likely want to @raw()
it -- to prevent duplicate char escaping.
Cheers,
Joe
from rocker.
Related Issues (20)
- Using template in RockerBody does not produce expected result HOT 1
- rocker-compiler.conf is in incorrect folder HOT 1
- Centralized conditional RockerContent rendering ? HOT 2
- Document required template extensions
- GraalVM compatibility HOT 3
- Warnings in Gradle Plugin (@Input annotation used on property of type 'File') HOT 6
- call java function in template HOT 1
- rocker-maven-plugin templateDirectory
- With block invalid: multiple equals symbols found for assignment String href = "/abc?x=y"
- [Doc] it's unclear how to instantiate Rocker
- Loading templates from classpath / jar ? HOT 1
- Parentheses right after variable HOT 2
- Compile time include postprocessor
- support Java 11 HOT 1
- Github latest release points to outdated release.
- Unable to get hot-reload working with tomcat server HOT 3
- Question about formatting
- Generation crashes with NPE if a non-normal file is present in the template directory HOT 1
- Global default null value rendering to avoid NPEs
- it's abandoned? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from rocker.