Input number to add new text box Vue3 - vue.js

I want to try handle add new inutbox. I try to follow this example
https://demo.smarttutorials.net/vuejs-dynamically-add-remove-input-textbox/
but I can't do
This is i want todo
When i type number in inputbox
add new inputbox
if input 1 add 1
if input 2 add 2 box
My code
<DialogTitle as="h3" class="text-xl leading-6 font-medium text-gray-900 py-2">Transaction #1</DialogTitle>
<input type="text" v-model="inputs.sequence" #inputs='$emit("update:modelValue", inputs)' class="border border-gray-300 py-2 px-1 text-lg" />
export default {
name:'prize-editor',
components: {
DropImages,
date,
DialogTitle
},
props:{
modelValue:Object
},
setup(props){
let inputs = ref({
detail:'',
name:'',
startDate:'',
sequence:'',
rewardId:'',
imageUrl:"",
})
onMounted( () => {
inputs.value=props.modelValue
});
watch(props.modelValue, (value) => {
inputs.value = value
console.log('watch',value)
})
return{
inputs,
}
},
};

You have given little info about how those inputs are supposed to behave exactly, but if we imagine you want to store (and do something with) the value of each, you need an array. and a function that adds as many elements to that array as you put in the input (how you call the function is up to you):
addInput(number) {
for(let i = 0; i < number; i++) {
this.inputArray.push({ value: "" });
}
}
Then you need to use v-for to render inputs based on your array :
<input v-for="(input, index) in inputArray" :key="index" v-model="input.value" type="text" />
To access the elements you can use index (like inputArray[2].value for the third input's value).

This would be a full vue component with your what you want, note that its based on options api and has no styles, those are on yourself:
<template>
<div>
<input type="text" v-model="inputCounter" />
<button #click="setInputs">Set</button>
<input v-for="(input, index) in inputs" :key="index" v-model="input.value" type="text" />
</div>
</template>
<script>
export default ({
data() {
return {
inputCounter: "",
inputs: []
}
},
methods: {
setInputs() {
for(let i = 0; i < parseInt(this.inputCounter); i++) {
this.inputs.push({ value: "" });
}
}
}
})
</script>

Related

How do have unique variables for each dynamically created buttons/text fields?

I'm trying to create buttons and vue element inputs for each item on the page. I'm iterating through the items and rendering them with v-for and so I decided to expand on that and do it for both the rest as well. The problem i'm having is that I need to to bind textInput as well as displayTextbox to each one and i'm not sure how to achieve that.
currently all the input text in the el-inputs are bound to the same variable, and clicking to display the inputs will display them all at once.
<template>
<div class="container">
<div v-for="(item, index) in items" :key="index">
<icon #click="showTextbox"/>
<el-input v-if="displayTextbox" v-model="textInput" />
<el-button v-if="displayTextbox" type="primary" #click="confirm" />
<ItemDisplay :data-id="item.id" />
</div>
</div>
</template>
<script>
import ItemDisplay from '#/components/ItemDisplay';
export default {
name: 'ItemList',
components: {
ItemDisplay,
},
props: {
items: {
type: Array,
required: true,
},
}
data() {
displayTextbox = false,
textInput = '',
},
methods: {
confirm() {
// todo send request here
this.displayTextbox = false;
},
showTextbox() {
this.displayTextbox = true;
}
}
}
</script>
EDIT: with the help of #kissu here's the updated and working version
<template>
<div class="container">
<div v-for="(item, index) in itemDataList" :key="itemDataList.id">
<icon #click="showTextbox(item.id)"/>
<El-Input v-if="item.displayTextbox" v-model="item.textInput" />
<El-Button v-if="item.displayTextbox" type="primary" #click="confirm(item.id)" />
<ItemDisplay :data-id="item.item.uuid" />
</div>
</div>
</template>
<script>
import ItemDisplay from '#/components/ItemDisplay';
export default {
name: 'ItemList',
components: {
ItemDisplay,
},
props: {
items: {
type: Array,
required: true,
},
}
data() {
itemDataList = [],
},
methods: {
confirm(id) {
const selected = this.itemDataList.find(
(item) => item.id === id,
)
selected.displayTextbox = false;
console.log(selected.textInput);
// todo send request here
},
showTextbox(id) {
this.itemDataList.find(
(item) => item.id === id,
).displayTextbox = true;
},
populateItemData() {
this.items.forEach((item, index) => {
this.itemDataList.push({
id: item.uuid + index,
displayTextbox: false,
textInput: '',
item: item,
});
});
}
},
created() {
// items prop is obtained from parent component vuex
// generate itemDataList before DOM is rendered so we can render it correctly
this.populateItemData();
},
}
</script>
[assuming you're using Vue2]
If you want to interact with multiple displayTextbox + textInput state, you will need to have an array of objects with a specific key tied to each one of them like in this example.
As of right now, you do have only 1 state for them all, meaning that as you can see: you can toggle it for all or none only.
You'll need to refactor it with an object as in my above example to allow a case-per-case iteration on each state individually.
PS: :key="index" is not a valid solution, you should never use the index of a v-for as explained here.
PS2: please follow the conventions in terms of component naming in your template.
Also, I'm not sure how deep you were planning to go with your components since we don't know the internals of <ItemDisplay :data-id="item.id" />.
But if you also want to manage the labels for each of your inputs, you can do that with nanoid, that way you will be able to have unique UUIDs for each one of your inputs, quite useful.
Use an array to store the values, like this:
<template>
<div v-for="(item, index) in items" :key="index">
<el-input v-model="textInputs[index]" />
</div>
<template>
<script>
export default {
props: {
items: {
type: Array,
required: true,
},
},
data() {
textInputs: []
}
}
</script>

update DOM only after submit form vue.js

I have a form with two checkboxes. each of them are related to an object to change its props 'is_active' on true or false. on click, DOM updates immediatly : if checked, object appears and if not, disappears. on submit, object is persisted in database.
<form #submit.prevent="submit()">
<div v-for="(checker_website, index) in website.checkers" :key="index">
<input type="checkbox" class="mr-2" :id="index" :true-value="1" :false-value="0" v-model="website.checkers[index].is_active">
<label :for="index">{{checker_website.checker.name}}</label>
</div>
<div class="text-right mt-5">
<button class="save flex font-semibold py-2 px-4 rounded">Enregistrer</button>
</div>
</form>
submit() {
axios.post('/app/store',{websites: this.websites})
},
also, I have a {{total}} prop which is updated after any change on checkbox. it is calculated this way :
computed: {
total() {
let total = 0;
this.websites.forEach(website => {
website.checkers.forEach(checker => {
if (checker.is_active === 1 && checker.checker.status === 1) {
total += checker.checker.price
}
})
})
return total
}
},
and displayed like this :
<span class="p-6">
Total mensuel : {{total}}€/HT
</span>
what I want is to update DOM right after the submit and I find no way to do that. any help will be very appreciated !
One solution is to convert the total computed prop into a data property that gets updated on submit:
export default {
data() {
return {
total: 0,
}
},
computed: {
// total() {⋯} // now a data property
},
methods: {
submit() {
this.total = this.getTotal()
},
getTotal() {
let total = 0
this.websites.forEach(website => {
website.checkers.forEach(checker => {
if (checker.is_active === 1 && checker.checker.status === 1) {
total += checker.checker.price
}
})
})
return total
},
},
}
demo

v-on does not working why not receive $emit in custom component

I'm create a an component which represents my money field.
My target is on add element in list, set zero on money field to add next element in list...
But, my problem is that not working when send using $emit event to clear input to improve usability.
$emit works as described on image bellow
My money field:
<template>
<div class="input-group" #clear="clearInputField()">
<span>{{ title }}</span>
<input ref="displayMoney" type="text" v-model="displayMoney" #focus="isActive = true" #blur="isActive = false" />
</div>
</template>
<script>
export default {
props: {
title: String,
},
data() {
return {
money: 0,
isActive: false,
};
},
methods: {
clearInputField() {
console.log("Its work event");
this.money = 0;
this.displayMoney = "";
},
},
computed: {
displayMoney: {
get: function () {
if (this.isActive) {
return this.money;
} else {
return this.money.toLocaleString("pt-br", { style: "currency", currency: "BRL" });
}
},
set: function (modifiedMoney) {
let newMoney = parseFloat(modifiedMoney.replace(/[^\d.]/g, "."));
if (isNaN(newMoney) || newMoney.length == 0) {
newMoney = 0;
}
this.$emit("input", newMoney);
return (this.money = parseFloat(newMoney));
},
},
},
};
</script>
My principal component
<template>
<div class="wish-list">
<div class="row">
<div class="input-group">
<span>Digite sua meta: </span>
<input ref="descriptionWish" type="text" v-model="descriptionWish" />
</div>
<MoneyField title="Valor (R$): " v-model="valueWish" #keyup.native.enter="addWish" />
<button id="btnCalculate" #click="addWish()">Adicionar a lista de desejos</button>
</div>
<div class="list-items">
<ul>
<li v-for="wish in wishes" :key="wish">{{ wish }}</li>
</ul>
</div>
</div>
</template>
<script>
import MoneyField from "./Fields/MoneyField";
export default {
components: {
MoneyField,
},
data() {
return {
wishes: [],
valueWish: 0,
descriptionWish: "",
};
},
methods: {
addWish() {
if (!isNaN(this.valueWish) && this.valueWish > 0 && this.descriptionWish.length > 0) {
this.wishes.push(
`${this.descriptionWish} => ${this.valueWish.toLocaleString("pt-BR", { currency: "BRl", style: "currency" })}`
);
this.descriptionWish = "";
console.log("addWish");
this.valueWish = 0;
this.$emit("clear");
this.$refs.descriptionWish.focus();
}
this.valueWish = 0;
},
},
};
</script>
I still don't understand much about vueJS, but I believe it's something related to parent and child elements, but I've done numerous and I can't get my answer.
sorry for my bad english .
The emit sends an event from the child to the parent component not as you've done, to run a method from the child component you could add a ref in the component inside the parent one like :
<MoneyField title="Valor (R$): "
ref="moneyField" v-model="valueWish" #keyup.native.enter="addWish" />
then run this.$refs.moneyField.clearInputField() instead this.$emit("clear")

v-model input working after pressing enter

I am working in Vue
My input search bar is filtering after every letter that I type. I want it to filter after I pressed the enter key.
Can somebody help me please?
<template>
<div id="show-blogs">
<h1>All Blog Articles</h1>
<input type="text" v-model="search" placeholder="Find Car" />
<div v-for="blog in filteredBlogs" :key="blog.id" class="single-blog">
<h2>{{blog.title | to-uppercase}}</h2>
<article>{{blog.body}}</article>
</div>
</div>
</template>
<script>
export default {
data() {
return {
blogs: "",
search: ""
};
},
methods: {},
created() {
this.$http
.get("https://jsonplaceholder.typicode.com/posts")
.then(function(data) {
// eslint-disable-next-line
console.log(data);
this.blogs = data.body.slice(0, 10);
});
},
computed: {
filteredBlogs: function() {
return this.blogs.filter(blog => {
return blog.title.match(this.search);
});
}
}
};
</script>
There are a few ways you could accomplish this. Probably the most accessible would be to wrap the input in a form and then user the submit event to track the value you want to search for. Here's an example:
<template>
<div id="show-blogs">
<h1>All Blog Articles</h1>
<form #submit.prevent="onSubmit">
<input v-model="search" type="text" placeholder="Find Car" />
</form>
</div>
</template>
export default {
data() {
return {
search: '',
blogSearch: '',
};
},
computed: {
filteredBlogs() {
return this.blogs.filter(blog => {
return blog.title.match(this.blogSearch);
});
},
},
methods: {
onSubmit() {
this.blogSearch = this.search;
},
},
};
Notice that blogSearch will only be set once the form has been submitted (e.g. enter pressed inside the input).
Other notes:
You'll probably want to trim your search value
You should add a label to your input.
You could skip using v-model and instead add a keyup event handler with the .enter modifier that sets the search data property
<input type="text" :value="search" placeholder="Find Car"
#keyup.enter="search = $event.target.value" />
Demo...
new Vue({
el: '#app',
data: () => ({ search: '' })
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<div id="app">
<input type="text" :value="search" placeholder="Find Car"
#keyup.enter="search = $event.target.value" />
<pre>search = {{ search }}</pre>
</div>

Vue.js: #input for <input> not working with v-for

I am creating my own custom <input> Vue component. What I am doing is that the user can never enter the wrong type of input. For that I am using regex.test() at each input.
This is my code for my Vue component for taking an integer element or an integer array:
<template>
<div>
<label>{{ label }}
<template v-if="isArray">
<input
v-model="arr[i - 1]"
#input="filterInput"
:disabled="disableWhen"
v-for="i in arraySize"
:key="i">
</input>
</template>
<template v-else>
<input
v-model="num"
#input="filterInput"
:disabled="disableWhen">
</input>
</template>
</label>
<el-button
type="success"
icon="el-icon-check"
circle
#click="confirm"
:disabled="disableWhen">
</el-button>
</div>
</template>
<script>
export default {
props: {
label: String,
nonNegative: Boolean,
disableWhen: Boolean,
isArray: Boolean,
arraySize: Number
},
data() {
return {
num: '',
arr: []
}
},
methods: {
filterInput() {
if (this.nonNegative) {
if (!/^[0-9]*$/.test(this.num)) {
this.num = '';
}
} else if (!/^(-)?[0-9]*$/.test(this.num)) {
this.num = '';
}
},
confirm() {
if (this.isArray) {
let validArrayInput = true;
for (let i = 0; i < this.arraySize; i++) {
if (!this.validInput(this.arr[i])) {
validArrayInput = false;
}
}
if (validArrayInput) {
this.$emit('confirm', this.arr);
}
} else if (this.validInput(this.num)) {
this.$emit('confirm', this.num);
}
},
validInput(x) {
return (x !== '' && x !== '-' && typeof x !== "undefined");
}
}
}
</script>
The code is working correctly when isArray = false, that is, for integer elements. But the method filterInput is never being called when isArray = true, and there is no restriction for the wrong input. What is the problem?
filterInput is being called fine for both types of input but it only attempts to manipulate num, it doesn't change arr.
Here's my attempt at implementing this:
const MyInput = {
template: `
<div>
<label>{{ label }}
<template v-if="isArray">
<input
v-for="i in arraySize"
v-model="arr[i - 1]"
:disabled="disableWhen"
:key="i"
#input="filterInput"
>
</template>
<template v-else>
<input
v-model="num"
:disabled="disableWhen"
#input="filterInput"
>
</template>
</label>
</div>
`,
props: {
label: String,
nonNegative: Boolean,
disableWhen: Boolean,
isArray: Boolean,
arraySize: Number
},
data() {
return {
arr: []
}
},
computed: {
num: {
get () {
return this.arr[0]
},
set (num) {
this.arr[0] = num
}
}
},
methods: {
filterInput() {
const arr = this.arr
const re = this.nonNegative ? /^\d*$/ : /^-?\d*$/
for (let index = 0; index < arr.length; ++index) {
if (!re.test(arr[index])) {
this.$set(arr, index, '')
}
}
}
}
}
new Vue({
el: '#app',
components: {
MyInput
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<my-input label="Single"></my-input>
<br>
<my-input label="Multiple" is-array :array-size="3"></my-input>
</div>
A few notes:
I've changed num to be a computed property backed by arr[0]. This simplifies the filtering logic as it only has to consider arr for both types of input. It could be simplified further, e.g. the template doesn't really need to handle two cases, it could treat single-valued just the same as multi-valued but with array-size of 1. Only the value that's emitted (not included in my code) really needs to have different behaviour for the single-valued case. With a little refactoring num could probably be removed altogether.
The implementation is painfully stateful. You're going to run into difficulties if you ever want to pass in values from the outside.
Rather than setting the values to '' I would suggest just stripping out the disallowed characters using replace. I have not made this change in my code, I wanted to retain the behaviour from the original example.
Closing </input> tags are invalid and I have removed them.
There was a lot of duplication in your filterInput method that I've tried to remove. It now checks all the entries in the arr array. There didn't seem to be any need to target the specific input that had changed.
this.$set is used as it's updating an array by index, which otherwise would not be detected by the reactivity system (the standard caveat for manipulating arrays).