bootstrap-touchspin icon indicating copy to clipboard operation
bootstrap-touchspin copied to clipboard

Change event firing multiple times

Open softelos opened this issue 8 years ago • 2 comments

The change event fires twice when pressing any of the the spinner buttons and 3 times when moving out the input field by pressing the TAB key after modifying its value.

I'm listening to this event in order to update a record in a database. Because of this behavior, I'm hitting my database 2 to 3 times per change.

I've confirmed this is not something else in my application by disabling the TouchSpin and leaving the input as the regular HTML one. In this case the change event happens only once.

Any way to solve this?

softelos avatar Jan 10 '17 14:01 softelos

There is the problem. The change event triggered two times when entering to the input value from keybord which is between allowed values by step. For example, I set step = 10. Current value is 0, I entered 7 from keyboard and change event triggered with 7 value, after that plugin rounded 7 to 10 and change event triggered for second time

I cant even find a temporary solution because there is no way to figure out in change callback it was triggered by keyboard entering or from touchspin.js

MaxSpirihin avatar Feb 20 '17 09:02 MaxSpirihin

I resolved this issue for my purposes by adding an additional if statement in the _checkValue function:

        if (Number(val).toString() !== returnval.toString()) {
          if (originalinput.val() === returnval) { 
            return;
          }
          originalinput.val(returnval);
          originalinput.trigger('change');
        }

Hope this helps.

esapy avatar May 28 '18 08:05 esapy

Cant we merge this solution to the component library ?

gokhangulgezen avatar Mar 20 '23 11:03 gokhangulgezen

I found a temporary work around,

    $(this).TouchSpin({ .... , forcestepdivisibility: null, ....});
    

By default the forcestepdivisibility is set to round which converts the value to a float. The problem arises when the val is a string like 2.0 and the conversion Number(val).toString() will convert it to 2. This will result in the change event being triggered each time the blur event is triggered, not just when the value is changed.

I suspect a better fix to the snippet of code above is:

    if (parseFloat(Number(val).toString()) !== parseFloat(returnval.toString())) {
      originalinput.val(returnval);
      originalinput.trigger('change');
    }

The inner if statement will now only be entered if the numeric value has actually changed.

esapy avatar Mar 22 '23 12:03 esapy

thanks a lot for even a better solution. Since it is your idea, can you maybe commit this solution to the project so we can all use it?

gokhangulgezen avatar Mar 24 '23 18:03 gokhangulgezen

I created a pull request. See: https://github.com/istvan-ujjmeszaros/bootstrap-touchspin/pull/132

esapy avatar Mar 27 '23 08:03 esapy

It is fixed in master, and a new version is published to npm (v4.4.0). Feel free to reopen if you still encounter multiple change events, but please provide the steps to reproduce. I could only reproduce what @MaxSpirihin described and fixed that by specially handling the TAB and ENTER keys on the input.

istvan-ujjmeszaros avatar Mar 30 '23 07:03 istvan-ujjmeszaros

For my purposes the problem is resolved. If tab is pressed when the numeric input field is focused, the change event is only triggered when the value is modified (good). The change event is (however) triggered each time the ENTER key is pressed regardless if the value wsa changed or not (may be good - not sure).

I ran npm test with the latest version 4.4.0 and I do get one test failure...

    TouchSpin Tests › should fire the change event only once when updating the value using the keyboard and pressing TAB

    expect(received).toBe(expected) // Object.is equality

    Expected: 1
    Received: 2

      103 |     const changeEventCounter = (eventLogContent.match(/testinput1: change\n/g) || []).length;
      104 |
    > 105 |     expect(changeEventCounter).toBe(1);
          |                                ^
      106 |   });

I managed to resolve this failure by inserting this code into index.html,

    <div class="col-md-3">
      <label for="testinput3">#testinput3:</label>
      <input id="testinput3" type="text" value="0" name="testinput3">
    </div>

And replacing testinput1 by testinput3 in the test-html-page.test.js file on line 103 and line 82. I cannot figure out why this would make it work.

btw: thank you for introducing me to jest. Very useful! I may actually start writing some JS tests for my application now :-)

esapy avatar Mar 30 '23 08:03 esapy

@esapy FYI the code snippet from the failing test doesn't seem to be coming from v4.4.0 as const changeEventCounter = (eventLogContent.match(/testinput1: change\n/g) || []).length; was replaced with a helper method in the published version: expect(await touchspinHelpers.changeEventCounter(page)).toBe(1);

I think you have tried the latest commit from your own branch (from the PR you have created), as there was one failing test there which I have fixed later in a different branch.

As for Jest, I really like it, but it took me three full days to figure out how to use it with this plugin, and I am not sure if I did everything properly. But it definitely helps to have tests. ChatGPT was helping with making it work :)

istvan-ujjmeszaros avatar Mar 30 '23 23:03 istvan-ujjmeszaros