Eel icon indicating copy to clipboard operation
Eel copied to clipboard

Implementation of Jinja2 Variables

Open Nootaku opened this issue 5 years ago • 7 comments

First of all : Eel is amazing ! This is a project I'd love to participate with.

One of the features I find lacking is the implementation of Jinja Variables. While the documentation and the options allow for inserting and using templates (with the jinja_templates argument in the start() function), I did not see any possibility to transfer variables within the templates.

It would be nice if this could be included either as a decorator or in a more "flask-like" fashion in the return of a function.

One solution that I tried to fiddle with but to no avail, was to create a decorator for the _static function (in the __init__.py) that seems to handle Jinja's template.render() function.

Here's what I got so far:

def jinjaVariables(_static):
    def innerFunction(path, jinja_variable=None):
        # the decorator recieved a Jinja Variable
        if "jinja_variables" is not None:
            response = None
            if 'jinja_env' in _start_args and 'jinja_templates' in _start_args:
                template_prefix = _start_args['jinja_templates'] + '/'
                if path.startswith(template_prefix):
                    n = len(template_prefix)
                    template = _start_args['jinja_env'].get_template(path[n:])
                    response = btl.HTTPResponse(template.render(jinja_variable))

            if response is None:
                response = btl.static_file(path, root=root_path)

            _set_response_headers(response)
            return response

        _static(path)

    return innerFunction

The idea was to create a global dictionary variable called jinja_variable and to update that variable depending on the page you want to visit.

If you were to include the jinja_variable in the kwargs of the start() function, wouldn't it be possible to dynamically change the output of the html page by accessing the required keys ?

Nootaku avatar Dec 05 '19 10:12 Nootaku

Hey @Nootaku - thanks for your interest in Eel!

You're definitely right that the implementation of jinja templating is a little lacking here, and being able to pass variables into the render calls does limit its utility.

I'd love to understand your use-case a little better so we can start to work out the best way forward. At the moment I'm not sure I understand your proposed solution or that it works for a general case. I'll start to have a think about it, but any details you can provide will help me understand where you're coming from.

Thanks!

samuelhwilliams avatar Dec 06 '19 21:12 samuelhwilliams

Hey @samuelhwilliams - thank you for your swift answer.

I'm am a relatively inexperienced programmer, so the code above was simply something that I thought could be possible to integrate in the framework.

I'll try to be as short as possible, but here is my thought process:

  • Based on what Flask does, it is possible to nest Jinja's template.render() function inside flask's render_template() function (see templating.py line 116 to 141). This means the arguments of Flask's render_template() function are being passed on to Jinja's template.render() function.
  • Currently Eel does something similar in its _static() function.
  • This means that it should be possible to modify the _static() function as follows (or something similar)
def _static(path, **context):
    """Arguments:
    		- path: path to the jinja template
    		- context: dictionary with
    			key = jinja variable name
    			value = jinja variable value
    """
    response = None
    if (
        'jinja_env' in _start_args and
        'jinja_templates' in _start_args
    ):
        template_prefix = _start_args['jinja_templates'] + '/'
        if path.startswith(template_prefix):
            n = len(template_prefix)
            template = _start_args['jinja_env'].get_template(path[n:])
            if context is not None:
                response = btl.HTTPRespons(
                    template.render(context)
                )
            else:
                response = btl.HTTPRespons(
                    template.render()
                )
     
    if response is None:
        response = btl.static_file(path, root=root_path)

    _set_response_headers(response)
    return response
  • By doing this, the last problem we are facing is to provide the **context argument to _static() in a way that is possible for the user to send variable for each html-page he/she wants to load. To do so, I have two options that come to mind:
    1. the use of a decorator
    2. create an independant function

In both cases, taking Eel's structure in consideration, we should pass 2 arguments:

  • a path_to_template
  • the context_dictionary

I believe it might end up looking something like this (and it is what I tried to make last week):

@eel.jinja_variables(some_context_dictionary)
def load_template(path_to_template):
    html_page = eel.render_page(path_to_template, some_context_dictionary)
    return html_page

Cheers

Nootaku avatar Dec 09 '19 10:12 Nootaku

It's making building html in for-loops waaaay cleaner.

I would just pass an object to the template and get the values in a for-loop. But for Eel, I have to get the values in a for-loop, run the python command from javascript and do another for-loop for building the html.

And putting in values in html is in jinja itself muuch fancier.

Nama avatar Sep 30 '20 10:09 Nama

There's some update on this topic? I'm falling in love with this project and having the possibility to pass context to jinja would be super helpfull

forno96 avatar Jul 06 '21 21:07 forno96

So am I correct in understanding that you can't pass a python variable to jinja or for example a data table?

eddyizm avatar Jan 28 '22 21:01 eddyizm

I also hope this will be implemented, before or (probably) later! 👯

Khharua avatar Jul 24 '23 11:07 Khharua

Hello, would it be possible to implement that, please? Just change of 1-2 lines of code in init.py is required:

Line 245 before: response = btl.HTTPResponse(template.render())

Line 245 after: response = btl.HTTPResponse(template.render(_start_args['jinja_context']))

And then optionally add 'jinja_context': None to _start_args on line 53

TadeasFrycak avatar Aug 16 '23 16:08 TadeasFrycak