3

I am using a django template to loop through hp_tracker objects. Each hp_tracker has its own js code which is used for changing the value via ajax. I'm almost certain I should have this code in the .js file but I was having trouble finding a way to uniquely select each hp_tracker without access to the django template variables in the .js file. So...I moved the js to the html template itself. I am aware this undoubtedly has massive security implications, and I'm open to discussing those as well.

Anyway, on to my question. My js fails because I have global variables(!) declared that are not unique. They are being used to control some counters and a setTimeout and I don't see how to now use them to do what I want to do.

So the for loop tries to re-declare the same variable over and over. Throughout the rest of the script I'm using JQuery which is perfectly happy to use the django variable {{ hp_tracker.id }}, but javascript is not because "-" characters, which are in the object IDs are not allowed in variable names.

What the heck should I do here to fix this hot mess. Is there a way to run my code without globals? Can I identify the objects in my for loop without using object ids?

<div id="ToolSessionPageWrapper">
<div class="tool-session-page-header">
    <div id=OpenToolSelectionMenuBtn class="arrow-down"></div>
</div>
<div class="tool-session-page-body">
    <div id="HpTrackersViewWrapper" class="tool-body">
        {% for hp_tracker in hp_trackers %}
            <div class="hp-tracker-box">
                <div id="{{hp_tracker.id}}-HpTrackerTitle" class="hp-tracker-title">{{ hp_tracker.title }}</div>
                <br />
                <form method="POST" action="{% url 'hp_change_value' hp_tracker.id %}" id="{{ hp_tracker.id }}-HpValueForm">
                    {% csrf_token %}
                    <div class="hp-control-box">
                        <button type="button" id="{{ hp_tracker.id }}-HpValueDecreaseBtn" class="hp-value-change-btn decrease">-</button>
                        <div id="{{hp_tracker.id}}-HpValue" class="hp-value">{{ hp_tracker.hp_value }}</div>
                        <div id="{{ hp_tracker.id }}-HpValueInput">{{ hp_change_value_form.hp_value }}</div>
                        <div id="{{ hp_tracker.id }}-HpChangeValueCover" class="hp-value hp-change-value-cover"></div>
                        <div id="{{ hp_tracker.id }}-HpChangeValue" class="hp-value hp-change-value"></div>
                        <button type="button" id="{{ hp_tracker.id }}-HpValueIncreaseBtn" class="hp-value-change-btn increase">+</button>
                    </div>
                </form>
            </div>
            <script>
                {#ajax call for HpValueForm#}
                function changeHpValue(form) {
                    'use strict';
                    $(form).submit(function (e) {
                        // preventing from page reload and default actions
                        e.preventDefault();
                        // serialize the form data.
                        let serializedData = $(form).serialize();
                        // make POST ajax call
                        $.ajax({
                            type: 'POST',
                            url: '{% url 'hp_change_value' hp_tracker.id %}',
                            data: serializedData,
                            success: function (response) {
                                let form_instance = JSON.parse(response['form_instance']);
                                let fields = form_instance[0]['fields'];
                                $('#{{hp_tracker.id}}-HpValue').empty().prepend(fields.hp_value);
                                console.log('ajaxSuccess');
                            },
                            error: function (response) {
                                console.log(response["responseJSON"]["error"]);
                            }
                        });
                    });
                }

                {#control timeout before hp_value increase or decrease is submitted#}
                let {{ hp_tracker.id|escapejs }}hp_add_subtract_value = $('#{{hp_tracker.id}}-HpValue').text(),
                    hp_change_value = 0,
                    timeoutHandler;
                function timeoutControl() {
                    clearTimeout(timeoutHandler);
                    timeoutHandler = setTimeout(function () {
                        $('#{{ hp_tracker.id }}-HpValueInput input').val(hp_add_subtract_value);
                        $('#{{ hp_tracker.id }}-HpValueForm').submit();
                        $('#{{hp_tracker.id}}-HpChangeValue').css({'display': 'none'});
                        $('#{{hp_tracker.id}}-HpChangeValueCover').css({'display': 'none'});
                        hp_change_value = 0;
                        $('#{{hp_tracker.id}}-HpChangeValue').empty().prepend(hp_change_value);
                    }, 2000);
                }
                {#increase the hp value#}
                $('#{{ hp_tracker.id }}-HpValueIncreaseBtn').click(function (e) {
                    'use strict';
                    hp_add_subtract_value++;
                    hp_change_value++;
                    $('#{{hp_tracker.id}}-HpChangeValue').css({'display': 'inline'});
                    $('#{{hp_tracker.id}}-HpChangeValueCover').css({'display': 'inline'});
                    $('#{{hp_tracker.id}}-HpChangeValue').empty().prepend(hp_change_value);
                    timeoutControl();
                });

                {#decrease the hp value#}
                $('#{{ hp_tracker.id }}-HpValueDecreaseBtn').click(function (e) {
                    'use strict';
                    hp_add_subtract_value--;
                    hp_change_value--;
                    $('#{{hp_tracker.id}}-HpChangeValue').css({'display': 'inline'});
                    $('#{{hp_tracker.id}}-HpChangeValueCover').css({'display': 'inline'});
                    $('#{{hp_tracker.id}}-HpChangeValue').empty().prepend(hp_change_value);
                    timeoutControl();
                });

                {#submit HpValueForm#}
                $('#{{ hp_tracker.id }}-HpValueForm').on('submit', changeHpValue('#{{ hp_tracker.id }}-HpValueForm'));
            </script>
        {% endfor %}
    </div>
</div>
4
  • Check out the docs on {% for. You can do things like {{ forloop.counter }} to get the current loop index. That should help you create unique names - docs.djangoproject.com/en/3.1/ref/templates/builtins/#for Commented Mar 20, 2021 at 1:19
  • Ah yes, I had not thought of that. I'll check it out! Commented Mar 20, 2021 at 1:41
  • Hi, is it necessary to put django code inside jquery ? Commented Mar 20, 2021 at 4:21
  • I'm using the Django variables as selectors in jQuery to identify elements that are being dynamically generated by the Django templating system. I'm open to all ideas though! Commented Mar 20, 2021 at 4:45

2 Answers 2

2

Instead of creating mutliple scripts you can have only one script which will work for all form . So , changes you can make inside django code :

<div id="HpTrackersViewWrapper" class="tool-body">
        {% for hp_tracker in hp_trackers %}
            <div class="hp-tracker-box">
                <div id="{{hp_tracker.id}}-HpTrackerTitle" class="hp-tracker-title">{{ hp_tracker.title }}</div>
                <br />
                //add data-id ... and a class
                <form method="POST" action="{% url 'hp_change_value' hp_tracker.id %}" data-id ="{{hp_tracker.id}}" class="form_to_submit" id="{{ hp_tracker.id }}-HpValueForm">
                    {% csrf_token %}
                    <div class="hp-control-box">
                        <button type="button" id="{{ hp_tracker.id }}-HpValueDecreaseBtn" class="hp-value-change-btn decrease">-</button>
                        <div id="{{hp_tracker.id}}-HpValue" class="hp-value">{{ hp_tracker.hp_value }}</div>
                        <div id="{{ hp_tracker.id }}-HpValueInput">{{ hp_change_value_form.hp_value }}</div>
                        <div id="{{ hp_tracker.id }}-HpChangeValueCover" class="hp-value hp-change-value-cover"></div>
                        <div id="{{ hp_tracker.id }}-HpChangeValue" class="hp-value hp-change-value"></div>
                        <button type="button" id="{{ hp_tracker.id }}-HpValueIncreaseBtn" class="hp-value-change-btn increase">+</button>
                    </div>
                </form>
            </div>
            //remove whole script from here 
        {% endfor %}        
    </div>

Then , your jquery code will look like below :

//on submit of form
$(".form_to_submit").submit(function(e) {
  e.preventDefault();
  //get ids..
  var data_id = $(this).data("id")
  let serializedData = $(this).serialize();
  console.log(data_id)
  // make POST ajax call
  $.ajax({
    type: 'POST',
    url: '{% url hp_change_value ' + data_id + ' %}', //pass here id
    data: serializedData,
    success: function(response) {
      let form_instance = JSON.parse(response['form_instance']);
      let fields = form_instance[0]['fields'];
  $('#' + data_id + '-HpValue').empty().prepend(fields.hp_value); 
   console.log('ajaxSuccess');
    },
    error: function(response) {
      console.log(response["responseJSON"]["error"]);
    }
  });
});


var timeoutHandler;

function timeoutControl(el) {
  clearTimeout(timeoutHandler);
  //get closet form
  var selector = $(el).closest("form")
  //get hp values
  var hp_add_subtract_value = selector.find(".hp-value").text()
  //get id
  var data_id = selector.data('id')
  timeoutHandler = setTimeout(function() {
    $('#' + data_id + '-HpValueInput input').val(hp_add_subtract_value);
    selector.submit();
    selector.find('.hp-change-value').css({
      'display': 'none'
    });
    selector.find('.hp-change-value-cover').css({
      'display': 'none'
    });
    hp_change_value = 0;
    selector.find('.hp-change-value').empty().prepend(hp_change_value);
  }, 2000);
}

$('.increase').click(function(e) {
  var selector = $(this).closest(".hp-control-box")
  var hp_add_subtract_value = parseInt(selector.find(".hp-value").text())
  hp_add_subtract_value++;
  var hp_change_value = selector.find(".hp-change-value").text().trim() != "" ? parseInt(selector.find(".hp-change-value").text().trim()) : 0
  selector.find('.hp-change-value').css({
    'display': 'inline'
  });
  selector.find('.hp-change-value-cover').css({
    'display': 'inline'
  });
  selector.find('.hp-change-value').empty().prepend(hp_change_value);
  timeoutControl(this); //here this refer to increase button which is clickde..
});

$('.decrease').click(function(e) {
  //get closest outer box..
  var selector = $(this).closest(".hp-control-box")
  //use find to get other values..
  var hp_add_subtract_value = parseInt(selector.find(".hp-value").text())
  var hp_change_value = selector.find(".hp-change-value").text().trim() != "" ? parseInt(selector.find(".hp-change-value").text().trim()) : 0

  hp_add_subtract_value--;
  hp_change_value--;
  //change doms ,,,
  selector.find('.hp-change-value').css({
    'display': 'inline'
  });
  selector.find('.hp-change-value-cover').css({
    'display': 'inline'
  });
  selector.find('.hp-change-value').empty().prepend(hp_change_value);
  timeoutControl(this); //refer the button which is clicked
});

Demo Code :

//on submit of form
$(".form_to_submit").submit(function(e) {
  e.preventDefault();
  //get ids..
  var data_id = $(this).data("id")
  let serializedData = $(this).serialize();
  console.log(data_id)
  // make POST ajax call
  /*$.ajax({
    type: 'POST',
    url: '{% url hp_change_value ' + data_id + ' %}', //pass here id
    data: serializedData,
    success: function(response) {
      let form_instance = JSON.parse(response['form_instance']);
      let fields = form_instance[0]['fields'];*/
  $('#' + data_id + '-HpValue').empty().prepend(44); //just for demo...
  /* console.log('ajaxSuccess');
    },
    error: function(response) {
      console.log(response["responseJSON"]["error"]);
    }
  });*/
});


var timeoutHandler;

function timeoutControl(el) {
  clearTimeout(timeoutHandler);
  //get closet form
  var selector = $(el).closest("form")
  //get hp values
  var hp_add_subtract_value = selector.find(".hp-value").text()
  //get id
  var data_id = selector.data('id')
  timeoutHandler = setTimeout(function() {
    $('#' + data_id + '-HpValueInput input').val(hp_add_subtract_value);//don't know where is input in your code..:P 
    selector.submit();
    selector.find('.hp-change-value').css({
      'display': 'none'
    });
    selector.find('.hp-change-value-cover').css({
      'display': 'none'
    });
    hp_change_value = 0;
    selector.find('.hp-change-value').empty().prepend(hp_change_value);
  }, 2000);
}

$('.increase').click(function(e) {
  var selector = $(this).closest(".hp-control-box")
  var hp_add_subtract_value = parseInt(selector.find(".hp-value").text())
  hp_add_subtract_value++;
  var hp_change_value = selector.find(".hp-change-value").text().trim() != "" ? parseInt(selector.find(".hp-change-value").text().trim()) : 0
  selector.find('.hp-change-value').css({
    'display': 'inline'
  });
  selector.find('.hp-change-value-cover').css({
    'display': 'inline'
  });
  selector.find('.hp-change-value').empty().prepend(hp_change_value);
  timeoutControl(this); //here this refer to increase button which is clickde..
});

$('.decrease').click(function(e) {
  //get closest outer box..
  var selector = $(this).closest(".hp-control-box")
  //use find to get other values..
  var hp_add_subtract_value = parseInt(selector.find(".hp-value").text())
  var hp_change_value = selector.find(".hp-change-value").text().trim() != "" ? parseInt(selector.find(".hp-change-value").text().trim()) : 0

  hp_add_subtract_value--;
  hp_change_value--;
  //change doms ,,,
  selector.find('.hp-change-value').css({
    'display': 'inline'
  });
  selector.find('.hp-change-value-cover').css({
    'display': 'inline'
  });
  selector.find('.hp-change-value').empty().prepend(hp_change_value);
  timeoutControl(this); //refer the button which is clicked
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="hp-tracker-box">
  <div id="1-HpTrackerTitle" class="hp-tracker-title">Soemthings ...,,</div>
  <br />
  <!--here added data-id , class as well-->
  <form method="POST" action="{% url 'hp_change_value' 1 %}" data-id="1" class="form_to_submit" id="1-HpValueForm">
    {% csrf_token %}
    <div class="hp-control-box">
      <button type="button" id="1-HpValueDecreaseBtn" class="hp-value-change-btn decrease">-</button>
      <div id="1-HpValue" class="hp-value">22</div>
      <div id="1-HpValueInput">23</div>
      <div id="1-HpChangeValueCover" class="hp-value hp-change-value-cover"></div>
      <div id="1-HpChangeValue" class="hp-value hp-change-value"></div>
      <button type="button" id="1-HpValueIncreaseBtn" class="hp-value-change-btn increase">+</button>
    </div>
  </form>
</div>
<div class="hp-tracker-box">
  <div id="1-HpTrackerTitle" class="hp-tracker-title">Soemthings ...,,</div>
  <br />
  <form method="POST" action="{% url 'hp_change_value' 2 %}" data-id="2" class="form_to_submit" id="2-HpValueForm">
    {% csrf_token %}
    <div class="hp-control-box">
      <button type="button" id="2-HpValueDecreaseBtn" class="hp-value-change-btn decrease">-</button>
      <div id="2-HpValue" class="hp-value">22</div>
      <div id="2-HpValueInput">23</div>
      <div id="2-HpChangeValueCover" class="hp-value hp-change-value-cover"></div>
      <div id="2-HpChangeValue" class="hp-value hp-change-value"></div>
      <button type="button" id="2-HpValueIncreaseBtn" class="hp-value-change-btn increase">+</button>
    </div>
  </form>
</div>

Sign up to request clarification or add additional context in comments.

Comments

0

@Swati, your answer did not do quite what I needed but you definitely pushed me in the right direction and helped me fill in some holes in my understanding of how this all works. So I marked your answer as correct, thank you for your help with this! Here is the code that I actually ended up using (I am aware the ajax error function does not work, but that's for another day...):

html

<div id="ToolSessionPageWrapper">
<div class="tool-session-page-header">
    <div id=OpenToolSelectionMenuBtn class="arrow-down"></div>
</div>
<div class="tool-session-page-body">
    <div id="HpTrackersViewWrapper" class="tool-body">
        {% for hp_tracker in hp_trackers %}
            <div class="hp-tracker-box">
                <div id="{{hp_tracker.id}}-HpTrackerTitle" class="hp-tracker-title">{{ hp_tracker.title }}</div>
                <br />
                <form method="POST" action="{% url 'hp_change_value' hp_tracker.id %}" id="{{ hp_tracker.id }}-HpValueForm" data-id="{{ hp_tracker.id }}" class="hp-value-change-form">
                    {% csrf_token %}
                    <div class="hp-control-box">
                        <button type="button" data-id="{{ hp_tracker.id }}-HpValueDecreaseBtn" class="hp-value-change-btn decrease">-</button>
                        <div id="{{ hp_tracker.id }}-HpValue" class="hp-value">{{ hp_tracker.hp_value }}</div>
                        <div id="{{ hp_tracker.id }}-HpValueInput">{{ hp_change_value_form.hp_value }}</div>
                        <div id="{{ hp_tracker.id }}-HpValueChangeCover" class="hp-value hp-change-value-cover"></div>
                        <div id="{{ hp_tracker.id }}-HpValueChange" class="hp-value hp-change-value"></div>
                        <button type="button" data-id="{{ hp_tracker.id }}-HpValueIncreaseBtn" class="hp-value-change-btn increase">+</button>
                    </div>
                </form>
            </div>
        {% endfor %}
    </div>
</div>

js

// ajax call for HpValueForm
$('.hp-value-change-form').submit(function(e) {
    'use strict';
    e.preventDefault();
    let data_id = $(this).attr("data-id");
    // serialize the form data.
    let serializedData = $(this).serialize();
    // make POST ajax call
    $.ajax({
        type: 'POST',
        url: $(this).attr('action'),
        data: serializedData,
        success: function (response) {
            let form_instance = JSON.parse(response['form_instance']);
            let fields = form_instance[0]['fields'];
            $("#" + data_id + "-HpValue").empty().prepend(fields.hp_value);
            console.log('ajaxSuccess');
        },
        error: function (response) {
            console.log(response["responseJSON"]["error"]);
        }
    });
});



//control timeout before hp_value increase or decrease is submitted
localStorage.setItem('hp_change_value', '0');
let timeoutHandler;

function timeoutControl(element) {
    'use strict';
    clearTimeout(timeoutHandler);
    // select the closest for to the clicked button
    let selector = $(element).closest('.hp-value-change-form');

    // assign the unique django object id to a variable
    let data_id = selector.attr("data-id");

    // get the hp change value from local storage
    let hp_change_value = parseInt(localStorage.getItem('hp_change_value'));

    // get the current hp value being provided by django context
    let hp_initial_value = parseInt($("#" + data_id + "-HpValue").text());

    // calculate the amount to be entered into the hp value change form
    let hp_add_subtract_value = hp_initial_value + hp_change_value;

    // After a 2 second delay submit the form and reset the change value to 0
    timeoutHandler = setTimeout(function () {
        $('#' + data_id + '-HpValueInput input').val(hp_add_subtract_value);
        $('#' + data_id + '-HpValueForm').submit();
        $('#' + data_id + '-HpValueChange').css({'display': 'none'});
        $('#' + data_id + '-HpValueChangeCover').css({'display': 'none'});
        localStorage.setItem('hp_change_value', '0');
    }, 2000);
}
// increase the hp value with each button click - value is not submitted until 
// after a 2 second delay via timeoutControl()
$('.hp-value-change-btn.increase').click(function (e) {
    'use strict';
    let selector = $(this).closest('.hp-control-box');
    let hp_change_value = parseInt(localStorage.getItem('hp_change_value'));
    hp_change_value++;
    localStorage.setItem('hp_change_value', hp_change_value.toString());
    $(selector.find('.hp-change-value')).css({'display': 'inline'});
    $(selector.find('.hp-change-value-cover')).css({'display': 'inline'});
    $(selector.find('.hp-change-value')).empty().prepend(hp_change_value);
    timeoutControl(this);

});

// decrease the hp value with each button click - value is not submitted until 
// after a 2 second delay via timeoutControl()
$('.hp-value-change-btn.decrease').click(function (e) {
    'use strict';
    let selector = $(this).closest('.hp-control-box');
    let hp_change_value = parseInt(localStorage.getItem('hp_change_value'));
    hp_change_value--;
    localStorage.setItem('hp_change_value', hp_change_value.toString());
    $(selector.find('.hp-change-value')).css({'display': 'inline'});
    $(selector.find('.hp-change-value-cover')).css({'display': 'inline'});
    $(selector.find('.hp-change-value')).empty().prepend(hp_change_value);
    timeoutControl(this);

});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.