GeneC
GeneC

Reputation: 103

Vuetify Text Field : Changes To Values Don't Persist (Lost on Blur or Focus)

Here is my code. See the <v-text-field> tag. On keypress event, it intercepts and transforms the input value into something else if the textbox is full (insert the keypress character at the cursor and push the rest of the characters into the subsequent input boxes).

The input updates with the correct values at first BUT when I blur it (focus out), it goes back to the value that it had before the keypress event. Also, the spillover character gets correctly prepended in the subsequent input box, but its value doesn't stay -- it changes on focus.

I have not written any event handlers that would do these. I have tried removing <v-form> and the :rules attribute.

I've also tried tying each <v-text-field> to a v-model and assigning the new input value to the model (2-level array) and when I inspect it in Vue Devtools, I notice that it doesn't update the value until I do the blur/focus. The updated values also seem to be wonky.

I've tried triggering change, even keyup, after setting the input value, but to no avail.

I've tried the non-JQuery way of setting the input value, no difference.

Tried with or without event.preventDefault() within the keypress handler, no difference.

Tried keyup instead of keypress just to see the behavior (I need keyup) but, same problem.

**UPDATE : also tried removing all v-models, no difference.

Vuetify version : 1.5.17.

What could be causing this? Please help! I promise I'll pay it forward and help others here when I have answering rights! Every little hint will help a lot!

            <v-stepper
                    v-model="stepperStep"
            >
                <v-stepper-items>
                    <v-form autocomplete="off"
                            ref="form"
                            v-model="valid"
                            lazy-validation >
                        <v-stepper-content
                                v-for="n in numLines"
                                :key="`${n}-content`"
                                :step="n"
                        >
                            <div v-show="false" :id="`wordLine_`+(n-1)" :data-numWords="wordLines[n-1].length" class="wordLine" style="display:flex; justify-content:center; margin:0 0 25px;">
                                <div v-for="(wordSize, key2) in wordLines[n-1]">
                                    <div class="wordBox" style="display:flex; color:#f00;">
                                        <v-text-field required class="wordInput" data-isWordInput="1" :stepperStep="n" :rel="key2" :rules="requiredRule" outline single-line dense height="36" @click:clear="wordInputCleared(n-1, key2)" :size="wordSize" :maxlength="wordSize"></v-text-field>
                                    </div>
                                </div>
                            </div>
                        </v-stepper-content>
                    </v-form>
                </v-stepper-items>
                <v-stepper-header v-show="numLines > 1">
                    <template v-for="n in numLines">
                        <v-stepper-step
                                :key="`${n}-step`"
                                :complete="stepperStep == n"
                                :step="n"
                                :editable="true"
                        >
                        </v-stepper-step>
                    </template>
                </v-stepper-header>
            </v-stepper>

Under mounted() :

        $(document).on('keypress', '.wordInput input[type="text"]', function(event) {
            if (event.keyCode == 32) { event.preventDefault(); return; }
            if (event.keyCode == 37 || event.keyCode == 39 || event.keyCode == 9 || event.keyCode == 16) return; // arrow keys, tab, shift

            let sizeRemaining = $(this).attr('maxlength') - $(this).val().length;

            let selectedText = $(this)[0].value.slice($(this)[0].selectionStart, $(this)[0].selectionEnd);
            if (selectedText.length > 0) return;    // if text is selected, we want this triggered keystroke to replace the selection (default behavior), not auto-advance

            let cursorAtLastPosition = ($(this)[0].selectionEnd == $(this).attr('maxlength'));

            if (event.keyCode != 8 && event.keyCode != 46) {
                if (sizeRemaining == 0) {
                    event.preventDefault();

                    let keyVal = event.key;
                    let lastCharBeforeDisplaced = $(this).val().charAt($(this).val().length-1);

                    if (cursorAtLastPosition) {
                        if (parseInt($(this).attr('rel'))+1 == $(this).closest('.wordLine').attr('data-numWords')) {    // last wordbox
                            if (_this.stepperStep == _this.numLines) {  // last/only line
                                $('#btnFinished').focus(); return;
                            }
                        } else {
                            let $nextWord = $(this).closest('.wordLine').find('.wordInput').find('input[type="text"][rel="'+(parseInt($(this).attr('rel'))+1)+'"]');
                            lastCharBeforeDisplaced = keyVal;
                        }
                    } else {    // insert keyVal at cursor
                        let lastCharRemoved = $(this).val().slice(0, -1);

                        $(this).val(lastCharRemoved.slice(0, $(this)[0].selectionEnd) + keyVal + lastCharRemoved.slice($(this)[0].selectionEnd));
                    }

                    _this.cascadeInsertNextWordbox($(this), lastCharBeforeDisplaced);
                }
            }
        });

Under methods :

        cascadeInsertNextWordbox($currentWordbox, displacedChar) {
            let $nextWordbox = this.getNextWordbox($currentWordbox);
            if ($nextWordbox.length) {  // exists
                if ($nextWordbox.val().length == $nextWordbox.attr('maxlength')) {      // is full
                    let lastCharBeforeDisplaced = $nextWordbox.val().charAt($nextWordbox.val().length-1);
                    $nextWordbox.val($currentWordbox.val().slice(0, -1));   // remove last char
                    this.cascadeInsertNextWordbox($nextWordbox, lastCharBeforeDisplaced);
                }
            }
            $nextWordbox.val(displacedChar + $nextWordbox.val());
        },

Upvotes: 4

Views: 3781

Answers (1)

GeneC
GeneC

Reputation: 103

Found the solution at this link! $forceUpdate() is what was needed. Updated via v-model :

                            <div v-show="false" :id="`wordLine_`+(n-1)" :data-numWords="wordLines[n-1].length" class="wordLine" style="display:flex; justify-content:center; margin:0 0 25px;">
                                <div v-for="(wordSize, key2) in wordLines[n-1]">
                                    <div class="wordBox" style="display:flex; color:#f00;">
                                        <v-text-field v-model="wordInputs[n-1][key2]" required class="wordInput" data-isWordInput="1" :stepperStep="n" :rel="key2" :rules="requiredRule" outline single-line dense height="36" @click:clear="wordInputCleared(n-1, key2)" :size="wordSize" :maxlength="wordSize"></v-text-field>
                                        <div v-show="false" class="wordInputSizeRemaining" style="margin:4px 0px 0px 6px;">{{wordSize}}</div>
                                    </div>
                                </div>
                            </div>

And :

                    } else {    // insert keyVal at cursor
                        let lastCharRemoved = $(this).val().slice(0, -1);
                        _this.wordInputs[$(this).attr('stepperstep')-1][$(this).attr('rel')] = lastCharRemoved.slice(0, $(this)[0].selectionEnd) + keyVal + lastCharRemoved.slice($(this)[0].selectionEnd);
                        _this.$forceUpdate();
                    }

Upvotes: 1

Related Questions