0

This thread here discussed using variables in inline JavaScript in templates. If I have separate .js files containing scripts, sitting in static folder, such as following:

utils.js

const createButton = (buttonCount) => {
    containerId = "myContainerId"
    container = document.getElementById(containerId)
    for (var i = 0; i < buttonCount; i++) {}
        newButton = document.createElement("button")
        newButton.value = "Test"
        newButton.id = "testButton" + i
        container.appendChild(newButton)
    }
}

createButton(buttonCount)

mytemplate.html

{% extends "base.html" %}
{% load static %}

{% block title %}Testpage{% endblock %}


{% block content-main %}
  <link href="{% static "css/mycss.css" %}" rel="stylesheet">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.4.0/css/bulma.css" /> 

  <div id="myContainerId"></div>

  <script src="{% static 'js/utils.js' %}"> </script>

{% endblock %}

If I have a variable buttonCount passed into this template via a view function's context, how do I pass it to the utils.js file to be called by function createButton()?

views.py

def button_view(request):
    ...
    buttonCount = 5
    return render(request, 'mytemplate.html', {'buttonCount': buttonCount})
Tristan Tran
  • 1,351
  • 1
  • 10
  • 36

2 Answers2

2

There can be a few ways:

  • Using Input Field

    <input id="buttonCount" value = "{{buttonCount}}" style="display:none;">

Then read value of element with id= buttonCount in utils.js.

  • Inline Script **Not Suggested,Use Document.onload instead.

     <script>
     set_button_count({{buttonCount}});
     </script>
    

But this will create a problem when your utils.js is not loaded yet.

  • document.onload Place the script source in <head></head>

    <script src="{% static 'js/utils.js' %}" defer> </script>
    <script>
    document.addEventListener('onload',function(
    {set_button_count({{buttonCount}});
    })
    </script>

  1. set_button_count() is to be placed in utils.js
  2. Defer will ask browser to only fire document load when utils.js is complete and it will be fetched and loaded after the document is loaded.

Warning: Inline scripts are to be used with strict CSP (Content Security Policy).Any inline script can be given a src as nonce. CSP can be done on Server Side on apache or Nginx which are very common web server/reverse proxy or you can also mention the same in HTML if you don't have control on that.


<meta http-equiv="Content-Security-Policy" 
        content="default-src 'self';
        script-src 'self' 'nonce-{{nonce}}';">

and this nonce can be generated something like this:


    import random,base64
    
    def usersession_processor(request):
        user = request.user
        unbaked_nonce = '%32x' % random.getrandbits(16*8)
        unbaked_nonce = unbaked_nonce.encode('utf-8')
        baked_nonce = base64.b64encode(unbaked_nonce)
        baked_nonce = baked_nonce.decode('utf-8')

Then <script src="{{nonce}}"></script> can be used for safe inlines.

Dushyant Deshwal
  • 388
  • 4
  • 17
  • Thanks. It seems I can only declare a function in the ```.js``` file but not calling it in there. I have to call it inline n the template, and pass the variable as argument, like your option 3 above. Also, it seems as long as I declare the script source right before calling the function inline, it works ``` ``` – Tristan Tran Jul 13 '21 at 20:20
  • 1
    It works in development but in product things sometimes don't work the way they work in development. I would suggest using defer and EventListener together.I faced a lot of difficulty the first the script were loading in random order because of the different sizes and inline scripts ran before that. – Dushyant Deshwal Jul 13 '21 at 20:29
1

I don't think this is recommended but you could do something like this if you're using the django template context. Put the script at the bottom of the page and include the buttoncount as a Django Templating Language variable. I don't think it's recommended to mix Django template variables with javascript though.

You can put a new block in your 'base.html' file, at the bottom inside the body tag like this:

{% block inline_javascript %}
{% enblock inline_javascript %}

Then inside the page you want the function to run on you put the script inside the same tags at the bottom of that page outside the 'block content' like:

{% block inline_javascript %}
    <script>
        const createButton = ({{ buttonCount }}) => {
            containerId = "myContainerId"
            container = document.getElementById(containerId)
            for (var i = 0; i < {{ buttonCount }}; i++) {}
                newButton = document.createElement("button")
                newButton.value = "Test"
                newButton.id = "testButton" + i
                container.appendChild(newButton)
            }
        }
    </script>
{% enblock inline_javascript %}
beasyx
  • 152
  • 9
  • Thanks. It seems passing variables into separate ```.js``` files, where JavaScript functions are called is not possible. The calling of function and passing of variables has to be done inline in template. – Tristan Tran Jul 13 '21 at 20:22
  • 1
    Yes but I would probably look into doing a hidden input with the name attribute set to the button count variable and then use the inline javascript to pull the value from that instead of putting the variable right in the JS – beasyx Jul 13 '21 at 20:30
  • Is this to avoid potential "injection"? If so, thanks, that makes sense. – Tristan Tran Jul 13 '21 at 20:34