2

I am trying to pass a variable from Flask, to an HTML template, and then use that variable in a separate JavaScript file.

routes.py

fav_photo_index = Photo_Index()


@app.route("/slideshow", methods=["POST", "GET"])
def slideshow():
    cur_image_index = fav_photo_index
    print(cur_image_index.index, image_urls[cur_image_index.index])
    if request.method == "POST":
        update_index(request, cur_image_index)
    favorite = is_favorite(image_urls[cur_image_index.index])
    return render_template('slideshow.html',
                           title="Slideshow",
                           images=image_urls,
                           favorite=favorite,
                           index=cur_image_index.index)

I want to use index variable in a JS file. I've seen in various places how to use that variable in the HTML template, but not how to pass it to a .js file for use there...

slideshow.js

var slideIndex = "{{index}}";
showDivs(slideIndex);

function plusDivs(n) {
  showDivs(slideIndex += n);
}

function showDivs(n) {
  var i;
  var x = document.getElementsByClassName("slide");
  if (n > x.length) {slideIndex = 1} 
  if (n < 1) {slideIndex = x.length} ;
  for (i = 0; i < x.length; i++) {
    // x[i].style.display = "none";
    x[i].classList.remove('slide-visible'); 
  }
  // x[slideIndex-1].style.display = "block"; 
  x[slideIndex-1].classList.add('slide-visible');
}

As you can hopefully see, I want the index variable to get used in the .js file...How can I do that?

So far I've just tried adding the variable in the head of slideshow.html but it's not working:

Current slideshow.html:

{% extends "layout.html" %}
{% block topscripts %}
    <link rel="stylesheet" type="text/css" href= "{{ url_for('static',filename='styles/slideshow.css') }}">
{% endblock %}
{% block content %}
{% include "/HTML Snippets/favorite_button.html" %}
<div class="slideshow-container">
    {% for image in images %}
        {% if image != "favicon.ico" %}
        <img class="slide" src="{{ url_for('static', filename='images/' + image) }}">
        {% endif %}
    {% endfor %}    

<form action="" method="post">
<input type="hidden" name="prev-next-buttons">
<button class="w3-button w3-display-left" name='prev-photo' onclick="plusDivs(-1)">&#10094;</button>
<button class="w3-button w3-display-right" name='next-photo' onclick="plusDivs(+1)">&#10095;</button>
</div>
</form>

{% endblock %}

{% block endscripts %}
    <script type="text/javascript" src="{{ url_for('static', filename='scripts/slideshow.js') }}"></script>
{% endblock %}

But it doesn't pass that variable to the .js file for use. The console shows the error:

Uncaught TypeError: Cannot read property 'classList' of undefined

at showDivs (slideshow.js:24)

at slideshow.js:2

FYI here's layout.html:

<!DOCTYPE html>
<html>
    <head>
        <link rel="stylesheet" type="text/css" href= "{{ url_for('static',filename='styles/layout.css') }}">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
        {% block topscripts %}
        {% endblock %}
    </head>
  <body>
    <div class="topnav">
      <a class="active" href="{{url_for('main')}}">Index</a>
      <a class="active" href="{{url_for('favorites')}}">Favorites</a>
      <a class="active" href="{{url_for('slideshow')}}">Slideshow</a>
    </div>
    <div class="container">
        {% block content %}
        {% endblock %}
    </div>

  </body>
  <script type="text/javascript" src="{{ url_for('static', filename='scripts/jscript.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='scripts/jQueryRotate.js') }}"></script>
    {% block endscripts %}
    {% endblock %}
</html>
Community
  • 1
  • 1
BruceWayne
  • 22,923
  • 15
  • 65
  • 110
  • What does viewing the source of the rendered page look like? – Dave W. Smith Apr 11 '19 at 21:23
  • @DaveW.Smith - The rendered page looks "normal", except all the images have class `slide`, when one of them should be `slide-visible`. I can update the `slideshow.html` template code if that would help understand. Will edit. – BruceWayne Apr 11 '19 at 21:25
  • BruceWayne, you should just easily pass data to a js file like the way you had with no problem. Have you tried echoing out the content of the sildeIndex variable to the console in your slideshow.js file? The error with the "classList" of undefined is probably something else completely unrelated to the data of slideIndex. It seems like the "x" isn't capturing the elements and thus undefined, which may be why the "classList" cannot be read. I would also check to make sure that "x[i]" really is returning one of the elements. – Christian Hur Apr 11 '19 at 22:05
  • @ChristianHur - Hmm, if I simply put the following in the .js file, it just shows `"{{index}}"` when I do `slideIndex` in the console from Chrome. --> `var slideIndex = "{{index}}"; // showDivs(slideIndex);` – BruceWayne Apr 11 '19 at 22:25
  • I am curious what you had in the "layout.html" file. Is that your base template? If so, then it might cause a problem the way you setup the slideshow.html (child template). Assuming that the layout.html is the base template, then you probably want to clean up the child template (e.g. remove , ,). Another option is to move the {% block scripts %} to the base template (layout.html) instead of having it in the child template. I tried replicating yours and it's not working. Once I put the {% block script %} ... {% endblock %} to the base template, it works fine. – Christian Hur Apr 11 '19 at 23:00
  • @ChristianHur - Ah! Yeah, I should have removed that extra stuff from `slideshow.html`, as you're right and it's in layout.html. However, after doing that, the js *still* isn't "translating" `var slideIndex = "{{ index }}";` to the variable, it stays the string `"{{ index }}"`...I've edited the post to include `layout.html`. – BruceWayne Apr 11 '19 at 23:15
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/191689/discussion-between-brucewayne-and-christian-hur). – BruceWayne Apr 11 '19 at 23:21

3 Answers3

2

Try this. Place the variable inside the slideshow.html within the {% block content %} .. {% endblock %} because it needs to be declared and created first and passed to the base template. Anything outside of the block will likely be ignored by the base template.

{% block content %} 

<script> 
  var slideIndex= {{ index }} 
</script> 

...rest of your content 

{% endblock %}
Christian Hur
  • 429
  • 3
  • 10
1

It might be a problem because by doing inline scripting, you're defining the variable slideIndex outside the scope of the JavaScript code imported (thus making the function showDivs(n) not recognize that variable)

 <script type="text/javascript" src="{{ url_for('static', filename='scripts/slideshow.js') }}"></script

One way I found to tackle the problem was by looking at: How can I pass data from Flask to JavaScript in a template?

Answer: mcote

Basically he is saying that you can embed a data attribute in your HTML inside a tag, for example a div, which can later be retrieved from inside JavaScript, like so:

<div id="hisDiv" data-slindex="{{slideIndex|tojson}}">
var slideIndex = JSON.parse(document.getElementById("hisDiv").dataset.slindex);
0'mihai'
  • 11
  • 2
  • Yeah, I had this idea too. And that most likely would work - but I'm curious how to get the `.js` file to accept the Flask variable, because from what I've read elsewhere, it should... – BruceWayne Apr 11 '19 at 23:17
0

Put your JS in the HTML file and it will work just fine.

<html>
<head>
</head>
<body>
{% extends "layout.html" %}
{% block content %}
{% include "/HTML Snippets/favorite_button.html" %}
<div class="slideshow-container">
    {% for image in images %}
        {% if image != "favicon.ico" %}
        <img class="slide" src="{{ url_for('static', filename='images/' + image) }}">
        {% endif %}
    {% endfor %}    

<form action="" method="post">
<input type="hidden" name="prev-next-buttons">
<button class="w3-button w3-display-left" name='prev-photo' onclick="plusDivs(-1)">&#10094;</button>
<button class="w3-button w3-display-right" name='next-photo' onclick="plusDivs(+1)">&#10095;</button>
</div>
</form>

{% endblock %}
</body>
{% block scripts %}
{% endblock %}
<script type="text/javascript">
var slideIndex = "{{index}}";
showDivs(slideIndex);

function plusDivs(n) {
  showDivs(slideIndex += n);
}

function showDivs(n) {
  var i;
  var x = document.getElementsByClassName("slide");
  if (n > x.length) {slideIndex = 1} 
  if (n < 1) {slideIndex = x.length} ;
  for (i = 0; i < x.length; i++) {
    // x[i].style.display = "none";
    x[i].classList.remove('slide-visible'); 
  }
  // x[slideIndex-1].style.display = "block"; 
  x[slideIndex-1].classList.add('slide-visible');
}

</script>
</html>
IceBawkz
  • 41
  • 1
  • 6
  • I'll try this, but I'd hope there's a solution to do this without putting the .js in the actual html document. Mainly for future cases where there's much more javascript, I'd like to load from a file. – BruceWayne Apr 11 '19 at 22:26
  • 1
    If you don't like this solution you could use this: https://stackoverflow.com/a/15069289/8508792 – IceBawkz Apr 12 '19 at 16:28