Git Product home page Git Product logo

Comments (15)

jjlauer avatar jjlauer commented on June 26, 2024

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.

momiji avatar momiji commented on June 26, 2024

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.

momiji avatar momiji commented on June 26, 2024

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.

momiji avatar momiji commented on June 26, 2024

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.

momiji avatar momiji commented on June 26, 2024

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.

momiji avatar momiji commented on June 26, 2024

https://github.com/jmarranz/relproxy/ for hot-reloading, looks very nice, isn't it ?

from rocker.

jjlauer avatar jjlauer commented on June 26, 2024

@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.

jjlauer avatar jjlauer commented on June 26, 2024

@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.

rrarrarra avatar rrarrarra commented on June 26, 2024

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.

jjlauer avatar jjlauer commented on June 26, 2024

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_guide

That would be awesome!


Reply to this email directly or view it on GitHub
#1 (comment).

from rocker.

rrarrarra avatar rrarrarra commented on June 26, 2024

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.

jjlauer avatar jjlauer commented on June 26, 2024

A couple thoughts:

  1. 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
@Args (String title)

Hi, i am @title! Lets see how the rendered template of "show()" looks like: @controllers.ApplicationController.show()

Template2 show.rocker.html:

@import controllers.ApplicationController
@Args (String name)

i am the rendered output with name: @name

The result of opening http://localhost:8080/index in a webbrowser should
be:

Hi, i am IndexTitle! Lets see how the rendered template of "show()" looks like: i am the rendered output with name: ShowName

How to do that?


Reply to this email directly or view it on GitHub
#1 (comment).

from rocker.

rrarrarra avatar rrarrarra commented on June 26, 2024

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.

rrarrarra avatar rrarrarra commented on June 26, 2024

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.

jjlauer avatar jjlauer commented on June 26, 2024

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.