0

My issue is simple enough--I am trying to render a form field from a django form into a javascript variable, defined within a <script> tag, within a django template.

When I output a CharField, there's no problem. But when I try to render a ChoiceField, the resulting output breaks the html, and prevents the script tag from correctly parsing my variable.

To demonstrate my setup, I have a form defined in forms.py, exactly like this example form:

from django import forms
form = TestForm(forms.Form):
    testfield = forms.ChoiceField(initial="increase_rate", 
                                  choices=[
                                        ("a", "option a"),
                                        ("b", "option b"),
                                        ("c", "option c"),
                                        ("d", "option d")
                                  ])

I am instantiating the form in views.py, and passing it into a django template to be rendered.

from django.shortcuts import render
from .forms import TestForm

[...]

@require_http_methods(["GET"])
def webpage(request):
    form = TestForm()
    return render(request, 'index.html', {"form":form})

Then, finally, in my template, I have something like the following:

[...]
<script>
    window.testfield = '{{ form.testfield }}'
</script>
[...]

Up until this point, everything works perfectly. No trouble at all. But when I render the field into the template, and inspect it in my browser, I get the following:

<script>
    window.testfield = '<select name="trigger" id="id_trigger">
  <option value="a" selected>option a</option>

  <option value="b">option b</option>

  <option value="c">option c</option>

  <option value="d">option d</option>

</select>'
</script>

This output breaks the html, and prevents the script tag from being interpreted as a variable like I want. This is a major problem, because I want to reuse these programmatically elsewhere on the page.

I tried the following:

<script>
    window.testfield = '{{ form.testfield|escape }}'
</script>

But was still unsuccessful. Any help anyone can give would be greatly appreciated!

I am actively researching a solution. My current guess is that the output needs to be escaped somehow that I don't understand. I figure the template tags and filters have my answer, I just have to find it. Will post an update once a solution is found.

Sleneau
  • 31
  • 1
  • 7

2 Answers2

0

Use <script type="text/template"></script>.

This way the browser knows it's just text and will ignore it.

lucutzu33
  • 3,470
  • 1
  • 10
  • 24
  • I tried your approach, but the variable was still broken when I tried to access it in the console. Meaning that when I tried to access the variable in the template, it returned `undefined`. – Sleneau Jun 24 '21 at 08:06
  • Did you try using var testfield instead of window.testfield? – lucutzu33 Jun 24 '21 at 08:12
  • Just did. No luck. Out of curiosity, I thought that using `var variable=value` created a globally scoped variable which is often defined to the window, or whatever the highest available scope was. Are there any differences between the two approaches that would have made the outcome different? – Sleneau Jun 24 '21 at 08:22
  • https://stackoverflow.com/questions/6904166/should-i-use-window-variable-or-var – lucutzu33 Jun 24 '21 at 08:26
  • That's actually really helpful. Answered a question I didn't think to ask. Thank you. – Sleneau Jun 24 '21 at 14:02
0

So, I figured it out. Turns out that the issue was that the presence of white space, line breaks, and unescaped double quotes (") were breaking the tag when it was parsed at HTML.

So I ended up using the following:

{% spaceless %}
<script>
    window.testfield = '{{ form.testfield|addslashes }}'
</script>
{% endspaceless %}

And it worked, allowing me to store the string representation of the django form field in a javascript variable. As per the documentation, the {% spaceless %} "Removes whitespace between HTML tags. This includes tab characters and newlines." [1]. As for the filter |addslashes, it "Adds slashes before quotes. Useful for escaping strings in CSV" [2]. In my case, both solutions were needed, as without either of them, the script tag broke.

As for why the |escape filter didn't work on it's own, I'm not sure. Reading the documentation, it seems like it probably should have. The following is what the |escape filter actually does [3]:

Escapes a string’s HTML. Specifically, it makes these replacements:

< is converted to &lt;
> is converted to &gt;
' (single quote) is converted to &#x27;
" (double quote) is converted to &quot;
& is converted to &amp;

I can only guess why this didn't work. I figure it's because it didn't do anything about the white space. But I shouldn't speculate. I welcome any explanations you might have. As ever, understanding the way the machine thinks is better than any single, specific solution. Thanks.

[1] - https://docs.djangoproject.com/en/dev/ref/templates/builtins/#spaceless

[2] - https://docs.djangoproject.com/en/dev/ref/templates/builtins/#addslashes

[3] - https://docs.djangoproject.com/en/dev/ref/templates/builtins/#escape

Sleneau
  • 31
  • 1
  • 7