Reset placeholder to default in a Vue JS form after submission - javascript

I am wanting to reset the name value in a swatch generator I am building, after the swatch is published, but struggling to get it right. Firstly, all the values in the app, 2 colors and swatch name, are watched and emitted. Here is the name value (value3) but the colors are set up the same, just value1 and value2 (not resetting the colors)
<b-form-input
id="name"
size="lg"
type="text"
class="search-bar"
placeholder="Name Your Swatch and Enter to Save"
v-model="value3"
ref="value3"
:value="value3"
#keypress="publishSwatch"
>
</b-form-input>
and what collects the name is here:
props: ['value'],
publishSwatch(e) {
this.$emit('input', {
value3: +this.$refs.value3.value,
});
if (e.key == "Enter") {
this.createSwatch();
this.resetForm(); //Not done yet
this.handleSwatch();
}
}
the relevant working part of the createSwatch function is just:
let name = (`${this.value3}`); //this works fine to set and output the inputted value
resetForm() {
// Stuck for what to put here
}
After the swatch is published I want to reset the placeholder to the default in resetForm() function, which I can place at the relevant place in publishSwatch method, which should be as above, but can't get anywhere near to getting it right. The colors are in another function and not resetting those. I have tried this, but it seems to have no relevance to how the inputs are set up:
resetForm() {
let clear = document.getElementById('name');
clear.input.value.reset();
}
And doesn't work
Tips welcome.
Thanks

Don't use :value and v-model together, because v-model creates :value automatically so there would be a conflict.
There is no need for a ref because the correct way is to use the v-model binding (value3) in the instance instead of a DOM value
HTML
<b-form-input
id="name"
size="lg"
type="text"
class="search-bar"
placeholder="Name Your Swatch and Enter to Save"
v-model="value3"
#keypress="publishSwatch">
</b-form-input>
Methods should look like this:
methods: {
publishSwatch(e) {
this.$emit('input', {
value3: +this.value3
});
if (e.key == "Enter") {
this.createSwatch();
this.resetForm();
this.handleSwatch();
}
},
resetForm() {
this.value3 = ''; // <-- Reset the instance value
}
}
Here is a demo

Related

Vue.js input value not reflecting value in component data

I have the following input:
<input :value="inputAmount" #input="handleAmountInput($event)" placeholder="Enter amount..." type="text">
I don't want 2-way binding with inputAmount because I want to clean the input of non-numeric characters in the handleAmountInput() function whenever the user inputs something:
handleAmountInput(e) {
const cleanInput = e.target.value.replace(/\D/g, '');
this.inputAmount = cleanInput;
console.log(cleanInput);
},
The issue is, the input itself doesn't reflect this cleaned up string set to inputAmount. If I show inputAmount in a separate element or console.log it like in the snippet above, it shows up just fine, but binding the value to the input with :value doesn't seem to work and shows the full inputted string, including non-numeric characters. What am I doing wrong here and how do I get the input to show the cleaned up string?
I'm not yet sure why exactly your code doesn't work as I would expect it to, but the way to fix it is to use both v-model and #input handler at the same time...
const app = Vue.createApp({
data() {
return {
inputAmount: ''
}
},
methods: {
handleAmountInput(e) {
this.inputAmount = e.target.value.replace(/\D/g, '');
console.log(this.inputAmount);
},
},
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.1.5/dist/vue.global.js"></script>
<div id='app'>
<input v-model="inputAmount" #input="handleAmountInput($event)" placeholder="Enter amount..." type="text">
<pre>{{ inputAmount }}</pre>
</div>
Update
Ok, I now understand the reason why your code does not work. What happens:
Value of inputAmount is for example '123' (reflected in <input>)
User types a
Your #input handler is called. It receives the value '123a', do it's job creating cleaned value '123' and assigns it into inputAmount
From Vue POV the value of inputAmount did not changed at all so no re-render is required and <input> still shows '123a' even tho inputAmount has a value of '123'
So another way of fixing your code is just to assign some value <input> can never produce into inputAmount 1st just to trigger the update (demo below)
const app = Vue.createApp({
data() {
return {
inputAmount: ''
}
},
methods: {
handleAmountInput(e) {
this.inputAmount = null
this.inputAmount = e.target.value.replace(/\D/g, '');
console.log(this.inputAmount);
},
},
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.1.5/dist/vue.global.js"></script>
<div id='app'>
<input :value="inputAmount" #input="handleAmountInput($event)" placeholder="Enter amount..." type="text">
<pre>{{ inputAmount }}</pre>
</div>
Have you tried using #change event
<input :value="message" #change="getInput($event)" placeholder="edit me" />
Use computed getter setter instead, Link :
example :
computed: {
inputAmount: {
get(){
//perform your logic
return 'value'
},
set(newValue){
this.value= newValue;
}
}
}
use v-model="inputAmount"? please see: https://cn.vuejs.org/v2/guide/forms.html
then you can just edit like this.inputAmount= this.inputAmount.replace(/\D/g, '');

Javascript / Vue3 - Mixins - Return 'null' by default

I'm building a basic 'required' form validation function. Here's the function:
JS:
export default {
methods: {
required(string) {
if (!string) {
return 'This field is required!'
}
}
}
}
HTML:
<input id="username"
v-model="credentials.username"
type="text"
name="username"
/>
<span>{{ required(credentials.username) }}</span>
The above works great. If I start typing in the input, the returned value goes null. If I empty the input, the returned value comes back as expected, "This field is required".
My question is, how can I return the value as null/blank to start? Expected flow is:
Returned value is null/blank to start
User starts typing, nothing changes because string.length != 0
User deletes all characters, causing string.length == 0, causing the returned value to be 'This field is required!'
One solution is to use an input-event handler (called for every new value in the input) that sets a flag to indicate the field is "dirty". Then conditionally render the validation result (the <span>) based on the flag:
Declare a data property named "dirty":
export default {
data() {
return {
dirty: false,
}
}
}
In the template, add an input-event handler on the <input> that sets the dirty flag:
<input #input="dirty = true">
Also, conditionally render the <span> field based on dirty:
<span v-if="dirty">{{ required(credentials.username) }}</span>
demo

React not triggering re-rendering of form inputs after state is updated via onChange

I am trying to build out a verification code page.
If I create an individual state for each input box, and then use the code below, it works appropriately.
<input type="number" value={inputOne} className={styles.codeInput} onChange={e => setInputOne(e.target.value}/>
However, I was trying to consolidate the state for all four of the input boxes, into one state object.
Now, when I type in a number, it moves on to the next input, but it never renders the value. In dev tools, I see the value flash like it updates, but it still stays as "value" and not "value="1"" for example.
However, if I do anything else to my code, like for example, change a p tag's text, then suddenly it updates and the inputs show the correct value.
I'm just trying to figure out what I'm doing wrong here.
Here's my current code.
import { useState } from 'react'
import styles from '../../styles/SASS/login.module.scss'
export default function Verify(params) {
const [verifCode, setVerifCode] = useState(['','','','']);
const inputHandler = (e, index) => {
// get event target value
let value = e.target.value;
// update state
let newState = verifCode;
newState[index] = value;
setVerifCode(newState);
// move focus to next input
if (e.target.nextSibling) {
e.target.nextSibling.focus()
} else {
// if at the last input, remove focus
e.target.blur();
}
}
return (
<div className={styles.verify}>
<p className={styles.title}>Verification</p>
<p className={styles.message}>Please enter the verification code we sent to your email / mobile phone.</p>
<div className={styles.form}>
<input type="number" value={verifCode[0]} className={styles.codeInput} onChange={e => inputHandler(e, 0)}/>
<input type="number" value={verifCode[1]} className={styles.codeInput} onChange={e => inputHandler(e, 1)}/>
<input type="number" value={verifCode[2]} className={styles.codeInput} onChange={e => inputHandler(e, 2)}/>
<input type="number" value={verifCode[3]} className={styles.codeInput} onChange={e => inputHandler(e, 3)}/>
</div>
<div className={styles.footer}>
<button>Verify Code</button>
</div>
</div>
)
};
I believe the problem lies in the following code
// update state
let newState = verifCode;
newState[index] = value;
setVerifCode(newState);
First line of the code just adds a pointer to the value verifCode.
You modify an element in that array, but newState is still the same variable verifCode. Even though the array elements have changed essentially it is still same variable (same reference).
Try something like:
// update state
const newState = [...verifCode]; // create a copy of the old verifCode, creating new array
newState[index] = value; // modify element
setVerifCode(newState); // set the new array to the state

How to set class to input when data returned from api. Vue

I have inputs with labels that rise when input has data, like materialize ( https://materializecss.com/text-inputs.html ).
I use my checkInputData function to check if input has any value and set 'with-data' class. It works when user input some data to input but when i initialize the component and data returned from api it does`n works.
How can i set 'with-data' class to my input when data returned from api?
My HTML
<input type="text" :value="name" #input="setName($event.target.value); checkInputData($event);" />
<label class="input-label">name</label>
checkInputData function
checkInputData(event) {
let input = event.target;
let hasValueClass = 'with-data';
if(input.value != '') {
input.classList.add(hasValueClass);
} else {
input.classList.remove(hasValueClass);
}
},
Use v-model for 2 way bind and use it for altering class(it will ignore the :value property).
<input type="text" v-model="nameValue" :class="{'with-data' : nameValue !== ''}" #input="setName($event.target.value);" />
<label class="input-label">name</label>
within data add nameValueproperty as well.
data(){
return {
/*other values*/
nameValue:''
}
}

Can't bind to dynamically added input in Bootstrap modal of Angular component

First of all, I don't see how the modal could have anything to do with this issue since its actually in this component's code, not a child. Still, this is in a modal, just in case.
I'm opting to not use FormArray since I need to keep track of my selections that may be added in a separate object, so I'm just creating unique IDs for the controls and adding them to the FormGroup. I can access the controls in the ts code, set values, get values, etc, but the form isn't binding and I can't figure out why not.
I can have an unknown number of items in this modal form, which will each have a selector (dropdown to select a property) and then the input to be able to modify some data. The input could be of different types, so it needs to be added and binded upon the choice from the selector.
<form [formGroup]="multiEditFormGroup" novalidate>
<div *ngFor="let item of multiEditSelections; let i = index">
<div>
<mdb-select [options]="availablePropsSelect"
placeholder="Choose property to edit"
class="colorful-select dropdown-main"
(selected)="multiEditSelectProp(item.selectorId, $event)"></mdb-select>
<label>Property to edit</label>
</div>
<div>
<div>
<input mdbActive
type="text"
class="form-control"
[formControlName]="item.controlId" />
</div>
</div>
</div>
</form>
Excepts of ts code:
public multiEditFormGroup = new FormGroup({});
onModalOpen():void {
const selectorId = this.utils.uuid();
this.multiEditFormGroup.addControl(selectorId, this.propSelector);
this.multiEditSelections.push({
selectorId: selectorId,
prop: '',
label: '',
type: '',
controlId: '' // not yet known since type hasn't been chosen
});
}
onSelect(selectorId: string, selectEvent: any): void {
let selection = this.multiEditSelections.find(selection => {
return selection.selectorId === selectorId;
});
const controlId = this.utils.uuid();
const prop = selectEvent.value;
this.multiEditFormGroup.get(selection.selectorId).setValue(prop);
this.multiEditFormGroup.markAsDirty();
this.multiEditFormGroup.markAsTouched();
const model = this.multiEditModel.find(model => {
return model.prop === prop;
});
this.multiEditFormGroup.addControl(controlId, this.testCtrl);
selection.controlId = controlId;
selection.prop = prop;
selection.label = model.label;
selection.type = model.type;
}
Logging to console shows that items are being added to the FormGroup, but the binding isn't happening to the DOM. For example, I can add a (keyup) event handler to my input and set the value in the form control which has already been created, and the FormGroup is updated. However, any input added in the front-end doesn't update the FG since it obviously isn't binding. Is this a timing issue or because the controlId is being updated later? I'm creating the FormControl before updating my array that is being iterated.
Oh and I get no errors in console on this.
I think you need to make this change:
[formControlName]="item.controlId"
needs to be:
formControlName="{{item.controlId}}"

Categories