0

I am using Inline Button Loader For Bootstrap 4 - Bootstrap4C Spinners to show loading action when clicked. I am also calling the click action manually in my javascript for every word that gets entered. Problem is when I type the word one by one and waiting for the response (which takes a second), the button transition works correctly. However if i type multiple words back to back without waiting for the response then the button always gets stuck in loading state.

You can observe the behavior here: http://34.66.22.154:8000/

Here is my index.html:

<html lang='en'>
    <head>
        <meta charset='utf-8'>
        <link rel='stylesheet' href='../static/style.css'>        
        <link rel="stylesheet" href="../static/cmGauge.css">
        <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
        <script src="../static/cmGauge.js"></script>
        <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css" rel="stylesheet">
        <link href="../static/component-spinners.css" rel="stylesheet">
    </head>
    <body>
        <div>
            <div class='center'>
                <div class='title'>Sentiment Analysis of Movie Reviews</div>
                <div class='content'>
                    <form action="/analyze" id="analyzeForm" class="form" method="post">
                        <div class="form-group">                            
                            <textarea id = "review_id" rows = "10" cols = "100" name = "review_name" class="review_class"></textarea><br>
                        </div> 
                        <div class='analyze'>                            
                            <button id="submit_button" type="submit" class="btn btn-primary btn-spinner btn-spinner-example" value="Analyze" data-spinner-text="Analyzing...">Analyze</button>
                        </div>                        
                    </form> 
                    <b>Negative</b>
                    <div id="gaugeDemo" class="gauge gauge-big gauge-green">                        
                        <div class="gauge-arrow" data-percentage="40"
                            style="transform: rotate(0deg);"></div>
                    </div>
                    <b>Positive</b>
                    <div class='result'>
                        <br><p id="result"></p>
                    </div>
                    <div id="result2"></div>
                    <script type="text/javascript">
                        var globalWordCount = 0
                        var indicator = 50
                        var btn
                        var gauge = $('#gaugeDemo .gauge-arrow')
                        gauge.cmGauge();
                        gauge.trigger('updateGauge', indicator);
                        $('.btn-spinner-example').click(function() {
                            console.log("Button click start");
                            btn = $(this);
                            $(btn).buttonLoader('start');
                        });                         
                        function word_count(field, count) {
                            var number = 0;
                            var matches = $(field).val().match(/\s/g);                            
                            if(matches) {
                                number = matches.length;
                            }                           

                            if (globalWordCount != number) {                                
                                globalWordCount = number                               
                                $("#submit_button").click();
                            }
                        }
                        // Enable button if some text entered
                        $(document).ready(function(){
                            $('.btn').attr('disabled',true);                            
                            $('#review_id').keyup(function(){
                                if($(this).val().length !=0){
                                    $('.btn').attr('disabled', false);
                                }
                                else
                                {
                                    $('.btn').attr('disabled', true);        
                                }
                            })                            
                        });

                        $('.review_class').each(function() {
                            var input = '#' + this.id;
                            var count = input + '_count';
                            $(count).show();
                            word_count(input, count);
                            $(this).keyup(function() { word_count(input, count) });
                        });

                        // Attach a submit handler to the form
                        $( "#analyzeForm" ).submit(function( event ) {                         
                          // Stop form from submitting normally
                          event.preventDefault();                          
                          // Get some values from elements on the page:
                          var $form = $( this ),
                            //term = $form.find( "input[name='review_name']" ).val(),
                            text = $form.find( "textarea" ).val(),
                            url = $form.attr( "action" );
                            //console.log(text, "text");

                          // Send the data using post
                          var posting = $.post( url, { review_name: text } );

                          // Put the results in a div
                          posting.done(function( data ) {
                            var indicator = ('Negative' === data.prediction) ? 100 - data.confidence : data.confidence;            
                            gauge.trigger('updateGauge', indicator);                                                        
                            document.getElementById("result").innerHTML = data.prediction + " with confidence of " + data.confidence + "%";
                            $(btn).buttonLoader('stop');
                            console.log("Button click stop");
                          });                          
                        });
                    </script>                                                             
                </div>
                <div class='footer'>
                    <p><br><br><br><br><br><br><a href="https://github.com/nikhilno1/nlp_projects">Code on GitHub</a>
                    <br><i>Powered by Hugging Face's awesome pytorch-transformers library</i></p>                    
                </div>
            </div>
        </div>
        <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
        <script src="../static/component-spinners.js"></script>
    </body>
</html>

Can you please let me know how to fix this? Thank you.

Nikhil Utane
  • 1,141
  • 2
  • 12
  • 29

1 Answers1

0

The problem seems to be in the spinner component.

When buttonLoader('start') is called, it saves the original button text in an attribute:

$(self).attr('data-btn-text', $(self).text());

But if the button is already spinning and its text is already set to data-spinner-text, then both data-spinner-text and data-btn-text are set to the same value and the original button text is lost.

It seems that this is supposed to be prevented by returning false if the button is already disabled:

if ($(self).attr('disabled') == 'disabled') {
  return false;
}

But the component doesn't set the disabled attribute when it's started:

$(self).addClass('disabled');

One quick solution is to stop the button before starting it.
This should prevent multiple overlapping calls to start the button.

$('.btn-spinner-example').click(function() {
  console.log("Button click start");
  $btn = $(this);
  $btn.buttonLoader('stop');
  $btn.buttonLoader('start');
});

Note that it's not necessary to make a jQuery object of a variable that's already a jQuery object:

btn = $(this);
$(btn).buttonLoader('start');

In fact, I might define the submit button once and use that definition throughout:

let $submitBtn = $('#submit_button');

$submitBtn.on('click',(function() {
  console.log("Button click start");
  $submitBtn.buttonLoader('stop');
  $submitBtn.buttonLoader('start');
});

Also, it seems that you're including the jQuery library twice in your document.

showdev
  • 28,454
  • 37
  • 55
  • 73
  • Thank you for your response. I tried adding stop before start but with this I don't even see the button changing to loading. It always remains the same. I am not a UI guy, I just wanna put together something to demonstrate my ML app. So once something works, I am hesitant to change it. :) – Nikhil Utane Jul 31 '19 at 14:56
  • Hey, i used $(btn) instead of $btn and it worked. :) What is the difference between $btn and $(btn)? – Nikhil Utane Jul 31 '19 at 15:06
  • [`$()`](https://api.jquery.com/jQuery/) "searches through the DOM for any elements that match the provided selector and creates a new jQuery object that references these elements". But since you've already defined `btn` as `$(this)`, it's already a jQuery object. I often define such a variable [with a dollar sign](https://stackoverflow.com/a/553734/924299) like `$btn` to help me remember it's a jQuery object. – showdev Jul 31 '19 at 15:24