I am using VueJS and Vuetify to create a modal that can accept some strings in the text field. Now what i want to do is to push the input string inside an array on click. So let's say if i input something and click create the resultant array is ['inputValue1'] but if i add another value by separating with a comma, the resultant array should be ['inputValue1', 'inputValue2'] instead i am getting it as ['inputValue1', 'inputValue1' 'inputValue2']. So the new value should be pushed to the new index instead of adding it with the last value.
Here is a demo
new Vue({
el: "#app",
data() {
return {
dialog: false,
inputValue: "",
stringArray: []
};
},
methods: {
createArray() {
if (this.inputValue !== "") {
this.stringArray.push(this.inputValue);
console.log(this.stringArray);
}
},
closeDialog() {
this.dialog = false;
this.inputValue = "";
this.stringArray = [];
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.14/dist/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-layout justify-center>
<v-flex>
<v-btn color="primary" #click="dialog=!dialog"> Click Me </v-btn>
</v-flex>
</v-layout>
<v-dialog v-model="dialog" width=350>
<v-card>
<v-card-title primary-title>
Create Array
</v-card-title>
<v-card-text>
<span class="title">How to create Array of Strings </span>
<v-layout justify-center>
<v-flex xs11>
<v-text-field v-model="inputValue"></v-text-field>
</v-flex>
</v-layout>
</v-card-text>
<v-card-actions class="mt-5">
<v-spacer></v-spacer>
<v-btn #click="closeDialog">CLOSE</v-btn>
<v-btn #click="createArray" :disabled="this.inputValue === ''" color="primary">CREATE</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-app>
</div>
Also on Cancel how can i set the input value and the array to an empty string and an empty array respectively. Thank You. I asked it yesterday but had to delete since i wasn't able to figure out the exact issue.
Your `createArray' method is not attached to any click event. Other than that the code is correct. :)
You should clear the inputValue after the value is pushed to the array like this:
methods: {
createArray() {
if (this.inputValue !== "") {
this.stringArray.push(this.inputValue);
this.inputValue = '';
console.log(this.stringArray);
} else {
console.log('The inputValue is empty')
}
},
closeDialog() {
this.dialog = false;
this.inputValue = "";
this.stringArray = []
}
}
});
Related
I know that a similar question has already been dealt with on stackoverflow. But I could not put together a solution from the proposed one. I am very ashamed.
The essence is this: I have a component and another one inside it.
The child component-VClipboardTextField is a ready-made configured text-area. I couldn't get the input out of there and I don't get an error when I try to enter it.
Avoid mutating a prop directly since the value will be overwritten
whenever the parent component re-renders. Instead, use a data or
computed property based on the prop's value. Prop being mutated:
"message"
code tabs-item.vue
<template>
<v-container fluid>
<v-row align="center">
<v-col cols="9">
<v-card flat>
<v-card-text>
<h1>Request</h1>
<v-container>
<v-textarea v-model="message"
placeholder="Placeholder"
label="Request"
auto-grow
clear-icon="mdi-close-circle-outline"
clearable
rows="10"
row-height="5"
#click:clear="clearMessage"
></v-textarea>
<v-textarea v-model="response"
placeholder="Placeholder"
label="Request2"
auto-grow
counter
rows="10"
row-height="5"
color="success"
></v-textarea>
<VClipboardTextField ></VClipboardTextField>
<VClipboardTextField isReadOnly></VClipboardTextField>
</v-container>
<v-row>
<v-btn
dark
color="primary"
elevation="12"
justify="end"
float-right
#click="sendRequest"
>
Send Request
</v-btn>
</v-row>
</v-card-text>
</v-card>
</v-col>
<v-col cols="3">
<schema-selector #changeSchema="onChangeSchema"></schema-selector>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: 'tabs-item',
props: ['response'],
data() {
return {
schema: String,
message: '',
}
},
methods: {
sendRequest() {
const message = {
message: this.message,
method: this.schema.state
}
this.$emit('sendRequest', message)
},
clearMessage() {
this.message = ''
},
onChangeSchema(selectS) {
console.log("get schema: ", selectS.state)
this.schema = selectS
}
},
}
</script>
and child VClipboardTextField.vue
<template>
<v-container>
<v-tooltip bottom
v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
#click:append-outer="copyToBuffer"
#click:clear="clearMessage"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: 'VClipboardTextField',
props: {
isReadOnly: Boolean,
message : { type :String, default: "msg"}
},
data() {
return {
show: false,
// messageLocal: 'Response!',
iconIndex: 0,
}
},
methods: {
copyToBuffer() {
console.log("this: ", this)
navigator.clipboard.writeText(this.message);
this.toolTipChange()
setTimeout(() => this.toolTipChange(), 1000)
},
clearMessage() {
this.message = ''
},
toolTipChange() {
if (this.show)
this.show = false
}
}
}
</script>
I will be glad to see an example of the code that will explain how to correctly solve this problem without crutches!
Thanks.
you cannot modify props in components, if the initial value of message is needed as placeholder (meaning the input might not be empty at the begining), you can store the data in message prop to another data variable and use that as v-model to the textarea.
if there is no initial value for message, just use another data variable for textarea and emit an update for message in a watcher.
in code tabs-item.vue
<VClipboardTextField :message.sync="message"></VClipboardTextField>
and child VClipboardTextField.vue
<template>
<v-container>
<v-tooltip bottom
v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message_slug"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
#click:append-outer="copyToBuffer"
#click:clear="clearMessage"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: 'VClipboardTextField',
props: {
isReadOnly: Boolean,
message : { type :String, default: "msg"}
},
data() {
return {
show: false,
// messageLocal: 'Response!',
iconIndex: 0,
message_slug: '',
}
},
watch: {
message_slug(x) {
this.$emit('update:message', x)
}
},
}
</script>
It will bind the value to message_slug and update the message on parent component when it's value changes.
Instead of watching for change every time, you can only emit when there are changes.
In your tabs-item.vue
<VClipboardTextField v-model="message"></VClipboardTextField>
In your VClipboardTextField.vue component, you can receive the v-model input prop as a "VALUE" prop and assign it to a local data property. That way u can manipulate the local property and emit only when in it is changed!
<template>
<v-container>
<v-tooltip bottom v-model="show">
<template v-slot:activator="{ on, attrs }">
<v-textarea
v-model="message"
:append-outer-icon="'mdi-content-copy'"
:readonly="isReadOnly"
v-on="on"
v-bind="attrs"
auto-grow
filled
counter
clear-icon="mdi-close-circle-outline"
clearable
label="Response message"
type="text"
#click:append-outer="copyToBuffer"
#click:clear="clearMessage"
#change="$emit('input', v)"
></v-textarea>
</template>
<span>Tooltip</span>
</v-tooltip>
</v-container>
</template>
<script>
export default {
name: "VClipboardTextField",
props: {
isReadOnly: { type: Boolean },
value: {
type: String,
},
},
data() {
return {
show: false,
message: this.value,
};
},
};
</script>
I am new to Vue and trying to make a blog project.
In above image when i click on reply button a dialog box opens and i submit a reply to the comment.
But the problem here is that After submitting the form and on dialog=false the new submitted reply doesn't show in the list.
my code is
<v-btn
#click="handleReply(comment.id)"
>Reply</v-btn>
<v-dialog v-model="dialog" max-width="400">
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-form ref="replyForm" v-model="isFormValid" lazy-validation>
<v-text-field v-model="editedReply.reply" label="reply" required></v-text-field>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" text #click="close">Cancel</v-btn>
<v-btn
color="primary"
#click.prevent="handleSubmit"
:disabled="!isFormValid || isProcessing"
:loading="isProcessing"
>Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
and
handleReply(id) {
this.dialog = true;
this.editedReply.comment_id = id;
},
handleSubmit() {
this.isProcessing = true;
axios
.post(`/replies`, { reply: this.editedReply })
.then(res => {
this.isProcessing = false;
const newEntry = res.data.reply;
this.replies.push(newEntry);
this.dialog = false;
},
Here this.editedReply presents the state of all params which are
editedReply: {
reply: null,
comment_id: null,
name: JSON.parse(localStorage.getItem("user")).name,
email: JSON.parse(localStorage.getItem("user")).email
}
we are getting comment id from handleReply method.
thanks:)
I try to realise a upload page as part of a project. It's working fine but I wonder if there is a better way to reset my v-form for my inputs.
So here is what I do:
<v-container v-if="!success">
<v-flex>
<v-card>
<v-form ref="form" class="pa-5">
<v-select
...
>
</v-select>
<v-select
...
>
</v-select>
<v-text-field
...
/>
<form enctype="multipart/form-data">
<v-file-input
...
></v-file-input>
</form>
</v-form>
<v-card-actions>
<v-btn #click="addDocument">Add document</v-btn>
</v-card-actions>
</v-card>
</v-flex>
</v-container>
<v-container v-if="success">
<v-flex>
<v-card>
<v-alert v-if="notification.show" :type="notification.type" class="pa-3">
{{notification.text}}
</v-alert>
<v-card-actions>
<v-btn #click="resetPage">Okay</v-btn>
</v-card-actions>
</v-card>
</v-flex>
</v-container>
Two v-containers which will either be rendered if success is true or not. If success is false all the required inputs will be rendered and if its true (for me this means the upload was successfull) the second v-container should be rendered.
The second v-container shows just a message about the success and a "Okay" button which calls the resetPage function.
This function should reset all validations and clear all inputs.
What I've tried:
At first I tried this:
resetPage() {
this.success = false;
this.$refs.form.reset()
}
which obviously doesn't work because $refs.from is not rendered because its part of the first v-container.
So I thought I have to call the reset after it's been rendered.
So I tried this:
methods: {
resetPage() {
this.success = false;
}
},
updated() {
this.$refs.form.reset();
}
But quickly I learned that updated() is called if a input field changes or in my case one of the selects. I should have thought about this.
So I added allowReset and ended up with this:
methods: {
resetPage() {
this.success = false;
this.allowReset = true;
}
},
updated() {
if(this.allowReset) {
this.$refs.form.reset();
this.allowReset = false;
}
}
Its working like I need it to. But as I said updated() gets called everytime I select something or choose a file etc.
Is there maybe a better way to reach my goal?
Thank you in advance for all tips and tricks!
Add a v-model to your v-form. Whenever you reset the page by using this.success = false, the form should reset by itself.
When adding a v-model to a form it checks all of the rules set for the elements inside of that form. If they all pass the checks, the formValid will be true, otherwise false
simple example:
<div v-if="!success">
<v-form v-model="formValid">
<v-text-field v-model="a" :rules="[required]"></v-text-field>
</v-form>
<!-- Button will stay disabled as long as the form is invalid -->
<v-btn :disabled="!formValid">Save</v-btn>
</div>
<div v-else>
Success
</div>
And in your data:
formValid: false,
required: (v) => !!v || "Required" // Will print required if textfield is empty
I want to add data (from two v-text-fields) in my product component to my data that I pass to the server. So that if the user adds the values '444' and '230' to the v-text-fields, the entry would be:
{
"444": 230
}
So far, I have hard coded the '444' and managed to get the value '230' passed. But how do i pass both '444' and '230' according to user inputs from a v-text-field?
product.vue
<v-content>
<v-container>
<code>formData:</code> {{ formData }}<br />
<v-btn color="primary" #click="create">Save</v-btn>
(Check console)<br />
<v-row>
<v-col>
<v-text-field v-for="(value, key) in formData" :key="key"
label="Card Type" v-model="formData[key]"
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-content>
data() {
return {
dialog: false,
product: null,
error: null,
formData: {
444: null,
}
}
},
methods: {
async create() {
try {
await ProductService.create(this.formData);
} catch (error) {
this.error = error.response.data.message
}
}
}
What changes would i need to make in my component so that formData is based on user input from another v-text-field?
I remember your previous question. To add a new item to that basic formData hash, you would do something like this:
<div>
<v-text-field label="Product #" v-model="newKey"></v-text-field>
<v-text-field label="Card Type" v-model="newValue"></v-text-field>
<v-btn #click="$set(formData, newKey, newValue)">+</v-btn>
</div>
This uses $set to add new properties to formData.
I'm creating a button to filter those data(games) that a user bought or have though in my code it only filters data with no boolean attribute set upon them.
Edited: added some more details on the code with firebase data to ref
<!-- This is the button I'm having trouble with -->
<v-tooltip right>
<v-btn small flat color="grey" #click="toggleHave(true)" slot="activator">
<v-icon left small>title</v-icon>
<span class="caption text-lowercase">by All titles bought</span>
</v-btn>
<span>Sort by Game's Title that I have</span>
</v-tooltip>
</v-flex>
<!-- filterGames is for the search method -->
<v-layout row wrap>
<v-card v-for="game in filterGames" :key="game.id" class="ma-2" width="240px">
<router-link :to="{ name: 'view-game', params: { game_id: game.game_id }}">
<v-img :src="game.cover" :alt="game.name" />
</router-link>
<v-card-title>
<div>
<span class="subheading">{{ game.title }}</span><br>
<span class="caption grey--text">{{ game.platform }}</span><br>
</div>
</v-card-title>
</v-card>
</v-layout>
export default {
data() {
return {
games: [], //this is connected to firebase
search: '',
}
},
toggleHave(bought) {
console.log(bought)
this.games = this.games.filter(game => {
return game.have === bought.have
})
},
computed: {
filterGames() {
return this.games.filter((game) => {
return game.title.toLowerCase().match(this.search)
})
}
}
}
// with true
game_id: "006"
title: "Far Cry 4"
have: true // this is the boolean
// with false
game_id: "051"
title: "Assassin's Creed Unity"
have: false // this is the boolean
You're calling toggleHave(true), but in your toggleHave function you use argument as it was an object:
game.have === bought.have
That means you compare game.have with undefined, which explains current behaviour.
Replace bought.have with bought and it should work.
Your question is more of a JavaScript question then a vue question. The filter function returns an array of the elements that meet your condition see here
If you would like to receive Boolean on filtering try using the includes method like here
see this Filter list with Vue.js
just change
return this.games.filter(game => {
return game.have == bought
})