Nuxt + Vuex + Computed Property - javascript

I am currently trying Nuxt.js with Vuex. And I Built a simple form, where I have an email field, a password field and a button.
All my state, mutations and actions are working as they should be. But When I created a computed property just to validate the password, I always have an error with an if statement to validate length of the password.
My Vuex state looks like this:
export default () => ({
// Register Init States
registerEmail: null,
registerPassword: null,
})
My mutation:
export default {
setRegisterEmail(state, registerEmail) {
state.registerEmail = registerEmail
},
setRegisterPassword(state, registerPassword) {
state.registerPassword = registerPassword
},
}
My template:
<vs-input
:value="registerPassword"
label="Password"
primary
type="password"
:progress="getProgress"
:visible-password="hasVisiblePassword"
icon-after
#input="setRegisterPassword"
#click-icon="hasVisiblePassword = !hasVisiblePassword"
>
<template #icon>
<i v-if="!hasVisiblePassword" class="bx bx-show-alt"></i>
<i v-else class="bx bx-hide"></i>
</template>
<template v-if="getProgress == 100" #message-success
>Secure password</template
>
</vs-input>
My Computed property:
getProgress() {
let progress = 0
// at least one number
if (/\d/.test(this.$store.state.auth.registerPassword)) {
progress += 30
}
// at least one capital letter
if (/(.*[A-Z].*)/.test(this.$store.state.auth.registerPassword)) {
progress += 20
}
// at least a lowercase
if (/(.*[a-z].*)/.test(this.$store.state.auth.registerPassword)) {
progress += 25
}
// more than 8 digits
if (this.$store.state.auth.registerPassword === null) {
progress += 0
} else if (this.$store.state.auth.registerPassword.length >= 8) {
progress += 25
}
return progress
},
So because the password init state is null, there should be no progress, but as I type the password, it should to the else if and verify the number of chars.
But when I type the password, the input and my state only keeps the last letter I typed.
Imagine that I typed "overflow", my state password would only have "w". And if I remove the password validation length, my state looks like this "overflow".
Am I doing something wrong? I hope I was clear 🥺 Because I am so confused right now. 😩

The problem is being caused when you call setRegisterPassword in the template. That input event is only returning the last input character. You can add a handler to update that value properly. One option is to use a local data property as a v-model binding and then setRegisterPassword to that value in the input handler.
<vs-input
v-model="localPassword"
label="Password"
primary
type="password"
:progress="getProgress"
:visible-password="hasVisiblePassword"
icon-after
#input="handleRegisterPassword"
#click-icon="hasVisiblePassword = !hasVisiblePassword"
>
and in your
data(){
return {
localPassword:''
}
},
methods: {
handleRegisterPassword() {
this.setRegisterPassword(this.localPassword);
}
}
Note: I sam not familiar with vs-input so your :value may work the same as v-model so you may be able to leave that as value.

Related

how to set usestate value in ternery operator pass a callback functoin setState()

<label htmlFor="rank" className="font-bbold">Rank:
</label>
<InputNumber
id="rank"
value={singlepoints.rank}
onValueChange={(e) =>
onRankChange(e, index)}
required>
</InputNumber>
{
// (singlepoints.rank === 0 || singlepoints.rank === null ) ? () => console.log('fjdkfhdfhd') : null
// ||
(singlepoints.rank === 0 ||
singlepoints.rank === null ) ?
**(() => setInvalid(true))** &&
<small className="p-error ml-6">
Rank is required.</small>
: null
}
Hi this is not how you handle state change.
First to validate something you usually have onBlur event (it fires when the input looses focus)
Second instead of trying to running code in ternary you have to do it in the useEffect hook:
useEffect(() => {
if (singlepoints.rank === 0 ||
singlepoints.rank === null )
setInvalid(true)
}, [singlepoints])
However I can recommend use formik and yup to do the validation, once you figure it out it will make your life much and much easier in terms of form validation and change handling
You're off to a good start here.
I see that you have a label and an InputName component, and it looks like the "rank" must be required and not zero.
I want to start by making a reference to React "controlled component" which essentially is "an input form element (for example <input />) whose value is controlled by React".
The code below will give you an idea of how to rewrite your code (please note that I added a submit button to handle the conditional statement):
import { useState } from "react";
function App() {
const [singlePointsRank, setSinglePointsRank] = useState("");
const [invalid, setInvalid] = useState(false);
function handleSubmit(e) {
e.preventDefault();
if (singlePointsRank == 0) {
setInvalid(true);
} else {
//do something else if the user provides a valid answer
}
}
return (
<div className="App">
<form>
<label>Rank:</label>
<input
required
name="rank"
value={singlePointsRank}
onChange={(e) => setSinglePointsRank(e.target.value)}
/>
<button onClick={handleSubmit} type="submit">
Submit
</button>
</form>
{/*The error message below will only be displayed if invalid is set to true */}
{invalid && <p>Please provide a valid rank number.</p>}
</div>
);
}
export default App;
Note that the required property in the input element prevents the form from being submitted if it is left blank. Therefore, you do not really need to check for singlePointsRank === null.
This way React will update the state variable singlePointsRank as the user types something in it. Then, when the user clicks on the submit button, React will call the handleSubmit function to which you can add your conditional statement set your second state variable invalid to false.
Regarding ternary operator, I do not recommend using it in this case since you might want to add some extra code to your conditional statement, such as re-setting invalid back to false, removing the error message and clearing the input field to allow the user to submit another rank number.
See the example below just to give you an idea:
import { useState } from "react";
function Random2() {
const [singlePointsRank, setSinglePointsRank] = useState("");
const [invalid, setInvalid] = useState(false);
function handleSubmit(e) {
e.preventDefault();
if(singlePointsRank == 0) {
setInvalid(true);
setSinglePointsRank("");
} else {
// do something else and clear the input field and reset invalid back to false
setInvalid(false);
setSinglePointsRank("");
}
}
function handleInputChange(e) {
if(setSinglePointsRank === "") {
setInvalid(false);
}
setSinglePointsRank(e.target.value)
}
return (
<div className="App">
<form>
<label>Rank:</label>
<input
required
name="rank"
value={singlePointsRank}
onChange={handleInputChange}
/>
<button onClick={handleSubmit} type="submit">
Submit
</button>
</form>
{/*The error message below will only be displayed if invalid is set to true */}
{invalid && <p>Please provide a valid rank number.</p>}
</div>
);
}
export default Random2;
Again, since the main question here is regarding ternary operators and setting state, although I do not recommend using it in this case, you could rewrite the initial handleSubmit function as follows just to play around with your code:
function handleSubmit(e) {
e.preventDefault();
singlePointsRank == 0 ? setInvalid(true) : console.log("do something else");
}
To get a little more practice, you could rewrite
{invalid && <p>Please provide a valid rank number.</p>}
as
{invalid ? <p>Please provide a valid rank number.</p> : ""}

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

Change input placeholder on global state change

I am in the mid of building a shopping cart demo, I am almost done for my purposes however I faced a frustrating bug that I can't seem to get around
The box in the middle is supposed to be an input field so that if a user requests a large number of products they can easily type it down instead of incrementing.
When I type in a number it reflects correctly and produces the desired change.
However, if I typed in a number and later used the (+ / -) buttons the placeholder value doesn't seem to change.
Here is my code
<div className="prod-count">
<button className="plus" onClick={()=> onIncrement(product.id)}>+</button>
<input type="text" onChange={(e)=> handleValue(product.id, valueExtracter(e))} className="num-box" placeholder={product.quantity}/>
<button className="minus" onClick={()=> onDecrement(product.id, product.quantity)}>-</button>
</div>
and here is the onChange function
const valueExtracter = (e) => {
return parseInt(e.target.value)
}
//handle value will only run if input is a number
const handleValue = (id, value) => {
if (!isNaN(value) && value > 0){
dispatch(setQuantity(id, value))
}
}
I am pretty sure the action gets dispatched correctly and I can see it in the total values, so the (product.quantity) value changes, but the placeholder doesn't get updated
One Last thing: The desired effect gets applied once I switched placeholder to value, however, once the component is created with the value of (1) the end user cannot erase this (1) and needs to select and overwrite it
I would just trade the placeholder for value.
EDIT: 'I would trade minus button position with the increment one' its seens more user friendly
<div className="prod-count">
<button className="minus" onClick={()=> onDecrement(product.id, product.quantity)}>-</button>
<input type="text" onChange={(e)=> handleValue(product.id, valueExtracter(e))} className="num-box" value={product.quantity}/>
<button className="plus" onClick={()=> onIncrement(product.id)}>+</button>
</div>
Looks like you're missing the value prop
<input
type="text"
onChange={(e)=> handleValue(product.id, valueExtracter(e))}
className="num-box"
value={product.quantity}
placeholder={product.quantity}
/>
Also, you may not need placeholder prop with value prop there.
Had to make the value extracter accept any thing and turn to an empty string and pass it to the handle value
const valueExtracter = (e) => {
let value = parseInt(e.target.value)
if (isNaN(value)) return ''
else return value
}
//handle value will only run if input is a number
const handleValue = (id, value) => {
if (value === '' || value > 0){
dispatch(setQuantity(id, value))
}
}
switched the placeholder to value since it can accept an empty string now
<input type="text" onChange={(e)=> handleValue(product.id, valueExtracter(e))} className="num-box" value={product.quantity}/>
if the reducer catches an empty string it'll multiply it by the price which would turn the displayed price to 0 so I made a fix for that too
<div className="prod-price">
{itemPrice >= product.price ? `$ ${itemPrice.toFixed(2)}` : <span className="disabled-price">$ {product.price}</span>}
</div>
the displayed price will display in red instead if the quantity is '' since '' * a number will return 0
and finally
the total quantity and price will be updated via the reducer and if the products quantity is '' it'll turn the totals to a string so I can error check on submition and alert the user to write a quantity or delete the item
so I wrote this in the reducer to get every thing back if the user decides to use the increment value once the field has beenn cleared "" + 1 = "1" , so this was fixed by checking in the educer function
case "UPDATE_CART":
const indexToUpdate = cartCopy.findIndex(product => product.id === action.id)
// if condition checks for when the item is set to '' to reset its value to 1
if (cartCopy[indexToUpdate].quantity === ''){
let itemReadded = {
...cartCopy[indexToUpdate],
quantity: 1
}
newCart = [
...cartCopy.slice(0,indexToUpdate),
itemReadded,
...cartCopy.slice(indexToUpdate + 1)
]
return {
...state,
cart: newCart ,
totalAmount: totals(newCart).amount,
totalQty: totals(newCart).qty,
}
}

Reset placeholder to default in a Vue JS form after submission

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

Validating React-Bootstrap-Typeahead input

I am using a validation function in my form, that checks whether a field is empty or not. This works for normal text input boxes, but not for the Typeahead box.
I want to check if the Typeahead box is empty, and if it is, display an error message i.e. this.state.industryError
This is my state:
state = {
name: "",
industry: [],
nameError: "",
industryError: "",
}
And this is my Typeahead form element:
<Form>
<Label>Industry</Label>
<Typeahead
id="industryMultiSelect"
multiple
options={industries}
placeholder="Select industry..."
onChange={this.handleTypeaheadChangeIndustry} />
<div>
{this.state.industryError} // Where the error message appears
</div>
</Form>
My validate function is as follows:
validate = () => {
let nameError = "";
let industryError = "";
if(!this.state.name) { // Checks if field is empty
nameError = "Please enter a name";
}
if(this.state.industry === []) { // I think this is the problem
industryError = "Please enter an industry";
}
if (nameError || industryError) {
this.setState({nameError, industryError});
return false;
}
return true;
};
This is handleChange function I call for the typeahead:
handleTypeaheadChangeIndustry = selected => {
const industry = selected.map(option => option.value);
this.setState({industry})
};
It works fine with the name field because it equals an empty string. But with the Typeahead, it's not a string, and so I'm unsure what to set this.state.industry to.
Any help or advice is much appreciated, and I can quickly update my answer if you need more detail.
It's a little hard to pinpoint why your code isn't working without seeing everything. That said, I created a sandbox which I believe does what you're after using the code you posted above:
https://codesandbox.io/s/react-bootstrap-typeahead-form-validation-686qe
As #ravibagul91 notes, you should be using one of
if (this.state.industry.length === 0) { ... }
if (!this.state.industry.length) { ... }
rather than
if (this.state.industry === []) { ... }
Using the latter in the sandbox I posted causes the code not to work, while things work fine when using either of the first two.

Categories