I wonder if someone could answer this question for me, maybe I'm using Vue in the incorrect way here.
I'm using vue-router to link to a .vue file with a template. So I'm referencing the below code using Submit Artwork
I'm developing a form, and want to use the toFormData() function which I believe is on the Vue() instance. I cannot access the vue instance however.
At the moment my call to app.toFormData(var) or this.toFormdata(var) will produce an error "404 cannot POST" since it's failing invalid function.
How do I access this function so I can use it to parse the input form data?
<template>
<div class="submitArtwork">
<div class="container">
<hr class="bg-info">
<form id="paintingForm" action="#" method="post">
<div class="form-group">
<v-text-field
name="aa_name"
label="Name of Painting"
id="aa_name"
v-model="newPainting.aa_name"
></v-text-field>
<v-autocomplete
label="Medium"
:items="mediums"
v-on:mouseover="activateHelper('mediumHelper');"
v-on:mouseleave="activateHelper('');"
v-model="newPainting.aa_medium"
></v-autocomplete>
<v-autocomplete
label="Country (optional)"
:items="country_list"
v-on:mouseover="activateHelper('countryHelper');"
v-on:mouseleave="activateHelper('');"
v-model="newPainting.aa_country"
></v-autocomplete>
<v-autocomplete
label="Tags (optional)"
:items="tags"
multiple
v-on:mouseover="activateHelper('tagHelper');"
v-on:mouseleave="activateHelper('');"
v-model="newPainting.aa_tags"
></v-autocomplete>
<v-file-input :rules="rules" prepend-icon="attachment" accept="image/*" multiple label="File input" #change="onFilePicker"></v-file-input>
<v-switch
v-model="consentCheck"
:label="`Consent: ${consentCheck.toString()}`"
></v-switch>
</div>
<div class="form-group">
<button class="btn btn-info btn-block btn-lg" #click="showAddModal=false; addPainting();">Submit Painting</button>
</div>
</form>
...
</template>
<script>
export default {
name: 'submitArtwork',
data: function() {
return {
countryHelper: false,
tagHelper: false,
mediumHelper: false,
newPainting: {aa_fbid: "", aa_a_firstname: "", aa_a_surname: "", aa_a_email: "", aa_country: "", aa_name: "", aa_tags: "", aa_medium: ""},
country_list: [""],
tags: [,
],
mediums: [
],
errorMsg: false,
successMsg: "",
showEditModal: false,
showAddModal: true,
consentCheck: true,
user: {
picture: { data: { url: '' } }
},
rules: [
value => !value || value.size > 2000000 || 'Photo size should be less than 2 MB!',
],
}
},
mounted() {
showAddModal=true;
},
methods: {
activateHelper(helper) {
this.countryHelper=false
this.tagHelper=false
this.mediumHelper=false
if(helper == "countryHelper") {
this.countryHelper=true
}
else if(helper == "tagHelper") {
this.tagHelper=true
}
else if(helper == "mediumHelper") {
this.mediumHelper=true
}
},
addPainting() {
// ERRORS OUT HERE // ------------------------------------>
var formData = toFormData(this.newPainting)
axios.post("http://localhost/process.php?action=create", formData).then(function(response) {
app.newUser = {name: "",email: "",phone: ""};
if(response.data.error) {
app.errorMsg = response.data.message;
}
else{
app.succcessMsg = response.data.message;
app.getAllUsers();
}
});
*/
},
}
}
</script>
<style>
...
</style>
Thanks
The problem is that toFormData is not a method on your instance (Vue does not come with any predefined instance methods, except for lifecycle hooks). You would also have to access instance methods through this.method().
Could you mean the HTML FormData API instead?
https://developer.mozilla.org/en-US/docs/Web/API/FormData
You can create a computed property and inside it prepare your form data for request. Iterate over JSON properties and add each data to form data. Or you can add ref=“form” to form and use like this:
var formData = new FormData(this.$refs.form)
Related
TL;DR
When using the 'required'-validator, then it doesn't update, when I insert content into a text-field.
CodeSandbox: https://codesandbox.io/s/confident-benz-x1lq8?file=/src/App.vue
Further details
I'm experiencing a wierd bug - and I've been sitting with it all day! I'm afraid that I'm missing something obvious, but I'm starting to get the feeling, that it could be a bug in the Vuelidate-library.
I have a simple form like this:
<div class="field__container">
<div>
<label for="emailToInvite">Email to invite</label> <br />
<input
type="email"
id="emailToInvite"
name="emailToInvite"
v-model="$v.emailToInvite.$model"
/>
<div class="errors" v-if="showErrors">
<p v-if="!$v.emailToInvite.required">Email to invite is required.</p>
<p v-if="!$v.emailToInvite">The written email is not valid.</p>
</div>
</div>
</div>
<button type="button" #click="submitForm()">Submit</button>
and the logic:
import { required, email } from "vuelidate/lib/validators";
export default {
data() {
return {
number: 1,
showAll: false,
submitAttempts: 0,
};
},
computed: {
showErrors() {
if (this.submitAttempts > 0) {
if (this.$v.$invalid) {
return true;
}
}
return false;
},
},
methods: {
submitForm() {
this.submitAttempts++;
},
resetAll() {
this.submitAttempts = 0;
this.$v.emailToInvite.$model = "";
},
},
validations: {
emailToInvite: {
required,
email,
},
},
}
And when I insert some text in the emailToInvite-field, then the 'required'-validator stays 'false'.
Another wierd thing is that the 'email'-validator is true, when the input is just an empty string.
All this can be seen in this CodeSandbox: https://codesandbox.io/s/confident-benz-x1lq8?file=/src/App.vue
What am I doing wrong?
I noticed a few things you should change -
First, you must add emailToInvite: '' to data:
data() {
return {
number: 1,
showAll: false,
submitAttempts: 0,
emailToInvite: ''
};
},
I'm not sure how Vuelidate works, but it seems it doesn't create the property for you. So it can't track the change properly.
Second, the showErrors computed property is set to 'false' until you press on 'Submit'. So you won't see the error message before you click on 'Submit'. Set it to 'true' if you want to see the error message (or hide it) while you type.
I have a defined a file upload field but the issue here is I am able to submit the form even if I have not selected a file. Please help me figure out how to through an error that no file is selected on clicking on the submit button if no file is selected. I am using vuetify version 1.0.
<template>
<v-form :model='agency' ref='AgencyForm'>
<div class="vue-file-wrapper">
<input
type="file"
ref="file"
name="vue-file-input"
required
:rules='uploadDocument'
#change="onFileSelected"
>
</div>
<v-btn #click.prevent='submit'>Save</v-btn>
</v-form>
</template>
<script>
export default {
props: ['agency'],
data: function () {
return {
filename: '',
uploadDocument: [
value => !!value || 'Please upload document'
],
}
}
methods: {
onFileSelected(event) {
var files = event.target.files || event.dataTransfer.files;
if (!files.length) {
return;
}
this.createImage(files[0]);
},
createImage(file) {
var fileReader = new FileReader(),
that = this;
fileReader.onload = function(event) {
that.agency.document = event.target.result;
that.agency.filename = file.name;
that.filename = file.name;
};
fileReader.readAsDataURL(file);
},
submit() {
if (this.$refs.AgencyForm.validate()) {
this.$axios.put('/agency.json', { agency: this.agency })
}
</script>
I can see some issues with your current implementation. Firstly, you are directly mutating a prop agency, which isn't a good practice. Also, you aren't waiting for your axios request to complete on submission.
However, for your current situation of not having an error thrown when no file is selected on clicking on the submit button, I think the issue is a syntax problem.
You currently have
<div class="vue-file-wrapper">
<input
type="file"
ref="file"
name="vue-file-input"
required
:rules='uploadDocument'
#change="onFileSelected"
>
According to the documentation, it should be
<v-file-input
:rules="uploadDocument"
#change="onFileSelected"
>
</v-file-input>
You can then leave the data property as it was
data: function () {
return {
filename: '',
uploadDocument: [
value => !!value || 'Please upload document'
],
}
}
EDIT -- since in Vuetify 1.0.5, there's no support for v-file-input, from this Github issue, you can do this
<template>
<div>
<v-text-field prepend-icon="attach_file" single-line
v-model="filename" :label="label" :required="required"
#click.native="onFocus"
:disabled="disabled" ref="fileTextField"></v-text-field>
<input type="file" :accept="accept" :multiple="false"
ref="file" #change="onFileSelected">
</div>
</template>
Your data property now becomes
data: function () {
return {
filename: '',
uploadDocument: [
value => !!value || 'Please upload document'
],
errors: {
file: ''
}
}
}
You can then style the text field using SCSS/CSS to be below the file input field or something.
One thing is for sure, the rules prop will not work on a input element because it's reserved for vuetify specific elements.
It won't be triggered by this.$refs.AgencyForm.validate() for that very reason. You will have to write custom validation
Maybe something along the lines of
methods: {
validateFile(file) {
if (!file.name) {
errors.file = 'Please select a file';
} else {
errors.file = '';
}
}
atLeastOneErrorExists(errors) {
return Object.values(errors).some(error => error.length > 0)
}
onFileSelected(event) {
var files = event.target.files || event.dataTransfer.files;
if (!files.length) {
return;
}
var file = files[0];
this.filename = file.name;
this.createImage(files[0]);
},
submit() {
this.validateFile(this.filename);
if (this.atLeastOneErrorExists(this.errors)) {
this.$axios.put('/agency.json', { agency: this.agency })
}
}
}
In your template, you can simulate the error message of Vuetify by styling a p tag that looks similar to Vuetify error messages
Something like
<div class="vue-file-wrapper">
...
<input
...
>
<p class="custom-error-class">{{errors.file}}</p> // custom error message
</div>
I am creating a form dynamically with the data that I get from the backend:
{
"title": "Contact Me",
"fields": [
{
"label": "Name",
"type": "textbox",
"required": "1"
},
{
"label": "Email",
"type": "email",
"required": "1"
},
{
"label": "Message",
"type": "textarea",
"required": "1"
},
{
"label": "Submit",
"type": "submit",
"required": null
}
]
}
In Vue the component where I am making this form looks like this:
<form #submit.prevent="submit()" class="form">
<template v-for="input in ninjaForm.fields">
<div v-if="input.type != 'submit' && input.type != 'textarea'" class="control">
<input
v-bind:value="form[input.label]"
class="input is-large"
:type="input.type"
:name="input.label.toLowerCase()"
:required="input.required == 1">
<label>{{ input.label }}</label>
</div>
<div v-if="input.type == 'textarea'" class="control">
<textarea
v-bind:value="form[input.label]"
class="textarea"
:name="input.label.toLowerCase()">
</textarea>
<label>{{ input.label }}</label>
</div>
<div v-if="input.type == 'submit'">
<button class="button is-primary">{{ input.label }}</button>
</div>
</template>
</form>
I would like to submit this data back to the backend, but I am not sure how to do that, I have tried with something like this:
data() {
return {
form: {},
};
},
methods: {
submit() {
let payload = {
headers: {
'Content-Type': 'application/json'
},
params: JSON.parse(JSON.stringify(this.form))
};
console.log(payload);
this.$backend.post('submit', null, payload)
.then(_ => {
this.response = 'Meldingen ble sendt!';
}, err => {
console.warn(err);
this.response = 'En feil oppstod under innsending av skjemaet, prøv igjen senere.';
});
}
}
But when I am doing console.log(this.form) I get an observer object, and if I do console.log(payload) I get an empty params property. What am I doing wrong and how should I fix this so that I can send form data as a params object?
I have tried with setting the form properties on created method, like this:
created() {
this.ninjaForm.fields.forEach(field => this.form[field.label.toLowerCase()] = '');
},
Which has made an object with properties that looks like this:
form: {
email:"",
message:"",
name:"",
submit:""
}
But, when I was submitting the form, the values of this properties where still empty:
v-bind:value="form[input.label.toLowerCase()]"
I have also tried with changing v-bind:value to v-model, but then I have got an error:
v-model does not support dynamic input types. Use v-if branches
instead.
Please check this thread:
https://github.com/vuejs/vue/issues/3915#issuecomment-294707727
Seems like it works with v-model. However, this doesn't work when you use a v-bind on the input type. The only workaround is to create a v-if for every possible form type. Really annoying, but there seems to be no apparent other solution.
I get an multi dimensional array named response from back-end and what I'm trying to do is to create several text-fields depending on the number of element I have in response (each response element has some inner elements like response[0][0] and response[0][1] that each of them is a object contains caption,name,etc for each text-field. for example response[0][0].name get name of response[0][0] element).
What I want is to bind these text-fields to an other two dimensional array named data so I can get value of them and use them as I want.
Here's the code:
<v-layout row wrap v-for="(row,i) in response" :key = "i">
<v-layout v-for="(col,j) in row" :key = "j">
<v-text-field
:name = "col.name"
:label = "col.caption"
v-model="data[i][j]"//I think somehow i should create data[i][j] element
first,like data[i] =[]
>
</v-text-field>
</v-layout>
</v-layout>
And script is :
data () {
return {
data: [],
response: []
}
},
mounted: function () {
//get response from back-end
}
I'm new to Vue and javascript, any help would be appreciate...
please comment If it's not clear.
should work... can you clarify what the issue is? is it matter of generating the response array?
new Vue({
el: '#app',
data: {
data: [
[{
name: 'name00',
caption: 'caption00'
},
{
name: 'name01',
caption: 'caption01'
}
],
[{
name: 'name10',
caption: 'caption10'
},
{
name: 'name11',
caption: 'caption11'
}
]
],
response: [
['',''],
['','']
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.5/vue.min.js"></script>
<div id="app">
<div v-for="(i, ii) in data" :key="ii">
<div v-for="(j, jj) in i" :key="jj">
{{j.name}}
<input v-model="response[ii][jj]" />
</div>
</div>
<pre>{{response}}</pre>
</div>
I am a newbie to JavaScript. I am trying to figure out what is the best way to write the control logic for my application. I have a list of checkboxes that hide and show different elements depending on certain options that are checked.
For example, I have the following HTML:
<label>
<input type="checkbox" name="productType" value="magazines" v-model="selectedProductType"> Magazines
</label>
<label>
<input type="checkbox" name="productType" value="books" v-model="selectedProductType"> Books
</label>
<label>
<input type="checkbox" name="productType" value="comics" v-model="selectedProductType"> Comics
</label>
<label>
<input type="checkbox" name="productType" value="videos" v-model="selectedProductType"> Videos
</label>
...snip...
And then I am hiding/showing things based on the checkmarked items
above, like so (this is just one example of some of the conditions I need to check):
<section v-if="(selectedOffice.jira) && (selectedProductType == 'comics') || (selectedProductType == 'videos')" id="booksInfo">Some info here</section>
...snip...
The issue is that I have to check different values in the data/model that looks like this:
//selectedOffice: '',
selectedProductType: [],
officeList: [
{
code: 'Blue Office',
jira: true
},
{
code: 'Red Office',
jira: false
}
...snip...
],
productList: [
{
type: 'comics',
url: 'www.comicsurl.com'
},
{
type: 'videos',
url: 'www.videosurl.com'
}
....snip...
]
Does anyone have any advice on the best way to approach the logic for my application? Better flexibility? My plan is to use an API for the data (Wordpress JSON REST API) and I will be able to customize the key/value properties on my own, but need help with the conditional stuff.
Thanks for any help!
Instead of using v-model, use #click or v-on:click to handle the click event instead. I like to keep most logic inside Vue instead of in the HTML so I recommend making use of methods and computed to determine what data to show.
const app = new Vue({
el: '#app',
data: {
selectedProductTypes: {
magazines: false,
books: false,
comics: false,
videos: false
},
productList: [],
typeList: [
{
type: 'magazines',
name: 'Magazines',
text: 'Check out new magazines'
},
{
type: 'books',
name: 'Books',
text: 'Check out new books'
},
{
type: 'comics',
name: 'Comics',
text: 'Check out new comics'
},
{
type: 'videos',
name: 'Videos',
text: 'Check out new videos'
}
]
},
computed: {
dataToBeShown() {
return this.productList
.filter(product => this.selectedProductTypes[product.type] === true) || [];
}
},
created() {
// get json data here
// assignning data here for demo
this.productList = [
{
type: 'magazines',
url: 'www.magazineurl.com'
},
{
type: 'books',
url: 'www.booksurl.com'
},
{
type: 'comics',
url: 'www.comicsurl.com'
},
{
type: 'videos',
url: 'www.videosurl.com'
}
]
},
methods: {
selectProductType(input) {
this.selectedProductTypes[input] = !this.selectedProductTypes[input];
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div class="control">
<div v-for="item in typeList">
<label>
<input type="checkbox" #click="selectProductType(item.type)"> <b>{{item.name}}</b> <span>{{item.text}}</span>
</label>
</div>
</div>
<hr/>
<section v-for="item in dataToBeShown">
<div>Type: {{item.type}}</div>
<div>Url: {{item.url}}</div>
</section>
</div>