There is a component that contains input[type=file].
Also, this field has an uploadFile handler, which calls the validateMessage method, which attempts to change the error. As you can see, after changing this.error it shows that everything is correct. But in div.error it is not displayed and if you look in vueDevtool, then there is also empty.
data in vueDevTools
data() {
return {
error: ''
}
},
methods: {
validateFile(file) {
if (! file.type.includes('video/')) {
this.error = 'wrong format';
console.log(this.error); // wrong format
}
},
uploadFile(e) {
const file = e.target.files[0];
this.validateFile(file);
},
}
<input type="file"
id="im_video"
name="im_video"
#change="uploadFile"
class="hidden">
<div class="error">
{{ error }}
</div>
Here is working example.
new Vue({
el:'#app',
data() {
return {
error: ''
}
},
methods: {
validateFile(file) {
console.log(file.type);
if (! file.type.includes('video/')) {
this.error = 'wrong format';
//console.log(this.error); // wrong format
}
},
uploadFile(e) {
this.error = '';
const file = e.target.files[0];
this.validateFile(file);
},
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<input type="file"
id="im_video"
name="im_video"
#change="uploadFile"
class="hidden">
<div class="error">
{{ error }}
</div>
</div>
If you are using component this would help more to share data from child to parent in your case setting error from child component to parent
Vue.component('upload-file',{
template:`<div><input type="file"
id="im_video"
name="im_video"
#change="uploadFile"
class="hidden"></div>`,
data() {
return {
error: ''
}
},
methods: {
validateFile(file) {
//
if (! file.type.includes('video/')) {
vm.$emit('filerror', 'wrong format');
}
},
uploadFile(e) {
vm.$emit('filerror', '');
const file = e.target.files[0];
this.validateFile(file);
},
}
});
const vm = new Vue({
el:'#app',
mounted(){
this.$on('filerror', function (msg) {
this.error = msg;
})
},
data:{
error:''
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<upload-file></upload-file>
<div class="error">
{{ error }}
</div>
</div>
Related
I'm trying to build a filtered list in Vue with equations rendered with MathJax, and it seems to be having some problems, since the equations renders on the first load, but when I search for terms, some equations render and some don't, I can't understand why.
Basically on the first load, if I type a character in my search bar, everything renders correctly, but when I search more, or erase it and do it again, it doesn't, as you can see in these images:
my Vue code is as follows:
var analisiMatematica = new Vue({
el: '#searcher',
data: {
searchQuery: '',
isResult: false,
results: [],
//json: 'json/analisimatematica.json',
error: ''
},
mounted: function() {
axios.get('./json/analisimatematica.json')
.then(function(response) {
this.results = response.data.Domande;
console.log(this.results);
}.bind(this))
.catch(function(error) {
this.error = error.data;
console.log(error.data);
}.bind(this));
},
methods: {
removeSearchQuery: function() {
this.searchQuery = '';
this.isResult = false;
},
submitSearch: function() {
this.isResult = true;
}
},
computed: {
filteredObj: function() {
var list = [];
this.results.forEach(function(el) {
if(el.domanda.toLowerCase().indexOf(this.searchQuery.toLowerCase()) > -1) {
list.push(el);
}
}.bind(this))
return list;
}
}
});
MathJax is loaded in my html file's <head> like this:
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
}
});
</script>
<script type="text/javascript" async
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/latest.js?config=TeX-MML-AM_CHTML">
</script>
While the vue app section is like this:
<div id="searcher">
<p v-show="error" v-html="error"></p>
<form class="searchForm" v-on:submit.prevent="submitSearch">
<input type="text" name="queryInput" v-model="searchQuery" placeholder="Che domanda cerchi?" #keyup="submitSearch">
<span v-show="searchQuery" class="removeInput" #click="removeSearchQuery">+</span>
</form>
<div class="results" v-show="isResult">
<ul>
<li v-for="result in filteredObj">
<p id="domanda">{{ result.domanda }}</p>
<p id="risposta">{{ result.risposta }}</p>
</li>
</ul>
</div>
</div>
All you need is to trigger MathJax to render again when filteredObj is changed. Watch filteredObj:
watch: {
filteredObj: function () {
if ('MathJax' in window) {
this.$nextTick(function() {
MathJax.Hub.Queue(["Typeset",MathJax.Hub, document.body])
});
}
}
}
var analisiMatematica = new Vue({
el: '#searcher',
data: {
searchQuery: '',
isResult: false,
results: [],
//json: 'json/analisimatematica.json',
error: ''
},
mounted: function() {
this.results = [{domanda: '$a+b=c$', risposta: '$a+b=c$'}]
},
methods: {
removeSearchQuery: function() {
this.searchQuery = '';
this.isResult = false;
},
submitSearch: function() {
this.isResult = true;
}
},
computed: {
filteredObj: function() {
var list = [];
this.results.forEach(function(el) {
if(el.domanda.toLowerCase().indexOf(this.searchQuery.toLowerCase()) > -1) {
list.push(el);
}
}.bind(this))
return list;
}
},
watch: {
filteredObj: function () {
if ('MathJax' in window) {
this.$nextTick(function() {
MathJax.Hub.Queue(["Typeset",MathJax.Hub, document.body])
});
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
}
});
</script>
<script type="text/javascript" async
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/latest.js?config=TeX-MML-AM_CHTML">
</script>
<div id="searcher">
<p v-show="error" v-html="error"></p>
<form class="searchForm" v-on:submit.prevent="submitSearch">
<input type="text" name="queryInput" v-model="searchQuery" placeholder="Che domanda cerchi?" #keyup="submitSearch">
<span v-show="searchQuery" class="removeInput" #click="removeSearchQuery">+</span>
</form>
<div class="results" v-show="isResult">
<ul>
<li v-for="result in filteredObj">
<p id="domanda">{{ result.domanda }}</p>
<p id="risposta">{{ result.risposta }}</p>
</li>
</ul>
</div>
</div>
I have created a settings page where users can update their email addresses. Everything worked fine but suddenly the validation is not updating anymore. Only the first change of the input field triggers validateState().
Any further changes will not trigger this function so the status of that field stays as it is.
I have compared the code with other components that use the same code and they still work fine.
I am using bootstrap-vue components for the form.
<template>
<div class="row">
<div class="col-md-12">
<b-form #submit="onSubmit">
<b-form-group :label="$t('general.email')"
label-for="settingsEmail"
:invalid-feedback="errors.first('email')">
<b-form-input id="settingsEmail"
type="text"
v-model="form.email"
:disabled="saving"
name="email"
:state="validateState('email')"
v-validate="{required: true, email: true}">
</b-form-input>
</b-form-group>
<b-button type="submit" variant="primary" :disabled="saving || !hasChanged() || errors.any()"><i class="fa fa-refresh fa-spin fa-fw" v-if="saving"></i> {{$t('general.save')}}</b-button>
</b-form>
</div>
</div>
</template>
<script>
import {UPDATE_USER} from '../config/actions'
export default {
name: 'settingsAccount',
data() {
return {
form: {},
saving: false
}
},
computed: {
user: function() {
return this.$store.getters.getUser;
}
},
created() {
this.init();
},
methods: {
init() {
this.form.email = this.user.email;
},
hasChanged() {
if(this.form.email !== this.user.email) {
return true;
}
return false;
},
onSubmit(event) {
event.preventDefault();
this.saving = true;
this.$validator.validateAll().then((result) => {
if (result) {
let data = {};
if(this.form.email !== this.user.email) {
data.email = this.form.email;
}
this.$store.dispatch(UPDATE_USER, data).then(() => {
this.saving = false;
this.$validator.reset();
}).catch(() => {
this.saving = false;
});
} else {
this.saving = false;
}
});
},
validateState(ref) {
if (this.veeFields[ref] && (this.veeFields[ref].dirty || this.veeFields[ref].validated)) {
return !this.errors.has(ref)
}
return null
},
}
}
</script>
The problem you're having is that the form data element is an empty object, so it will only trigger reactivity when the whole object changes. Either you need to change your data to be this:
data() {
return {
form: {email:''},
saving: false
}
},
Or in your init function, explicitly add the email property as reactive:
methods: {
init() {
this.$set(form,'email',this.user.email)
},
//...
If you're not clear on why, you can read the details here: https://v2.vuejs.org/v2/guide/reactivity.html
A working example (minus vuex) here: https://codesandbox.io/s/x4kp93w3o
PS, when writing questions about vue, it's very helpful to boil it down to a simpler example. Get rid of vuex, remove your translation stuff. Sometimes the answer will jump out at you once you have it as simple as possible.
I need to display only name from request in my form, can't figure out how to do it. I'm just starting with js, need help.
I have tried this {{ request.name }} but doesn't work. {{request}} shows me full data.
const app = new Vue({
el:'#valuation-request',
data() {
return {
step:1,
request:{
name:null,
industry:'{{ $company->industry }}',
valuation_date:null,
similar_comp:null,
total_raised:null,
sales_transactions:null
}
}
},
methods:{
prev() {
this.step--;
},
next() {
this.step++;
}
}
});
If name has a value, it should display as you wrote it. If it's null, nothing will be displayed.
const app = new Vue({
el:'#valuation-request',
data() {
return {
step:1,
request:{
name: null,
industry:'{{ $company->industry }}',
valuation_date:null,
similar_comp:null,
total_raised:null,
sales_transactions:null
}
}
},
methods:{
prev() {
this.step--;
},
next() {
this.step++;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="valuation-request">
{{request.name}}
<hr>
Name: <input type="text" class="uk-input" name="name" v-model="request.name" id="name" placeholder="e.g. John Doe" required>
</div>
all how i can watch changes in my component in data?
I need watch when user choose car brand to take from server models for that brand
this is my code
Templete
<template>
<div class="category-info">
<div v-for="input in inputs.text">
<label >{{ input.placeholder}}</label>
<input type="text" id="location" :name="input.name" v-model="input.value" #click="console">
</div>
<div class="select" v-for="select in inputs.select">
<label >{{ select.placeholder }}</label>
<my-select :data="select" v-model="select.value"></my-select>
</div>
<button #click="console">click</button>
</div>
Script
<script>
export default {
name: "profile-add-inputs",
props: ['category'],
data() {
return {
inputs: {
text : {},
select: {}
},
}
},
methods: {
getCategories(){
axios.get('/profile/inputs', {
params: {
category: JSON.stringify(this.category.href)
}
})
.then((response) => {
this.inputs.text = response.data.text;
this.inputs.select = response.data.select;
for(let key in this.inputs.text){
this.inputs.text[key].value = '';
}
for(let key in this.inputs.select){
this.inputs.select[key].value = '';
if(this.category.href.sub == 'car' && this.inputs.select[key].name == 'brand'){
console.log('CAR BREND');
this.$watch.inputs.select[key].value = function () {
console.log(this.inputs.select[key].value);
}
}
}
},this)
.catch(function (error) {
console.log(error);
});
},
console(){
console.log(this.inputs.select);
}
},
watch: {
category : function () {
this.getCategories();
console.log('categoty');
},
inputs : {
handler() {
console.log('watch inputs');
}
}
}
}
So, i tried to use watch and $watch but its not working, plz give me a reason why that not work, or maybe some another way to resolve this problem
this.$watch can i create dynamiclly watchers with this stement?
The correct syntax is
watch : {
inputs : function(val, oldVal){
//val : New value
//oldVal : Previous value.
}
}
I am having trouble understanding how vue js's reactivity work. I am trying to make a component for all kinds of ajax call, the problem is when I change the props to be passed on to the component it doesn't get the changes in the data.
https://jsfiddle.net/8t4vx44r/34/
Vue.component('api-call-template', {
template: '#api-call-templates',
props: {
apiCallParams: Object
},
data () {
return {
result: [],
loading: false
}
},
created() {
console.log("entered?")
console.log(this.apiCallParams);
var vm = this;
vm.loading = true;
axios({
method: this.apiCallParams.method,
url: this.apiCallParams.url,
data: this.apiCallParams.params
}).then(function(response) {
vm.result = response.data;
vm.loading = false;
vm.$emit('get-response', response);
});
console.log(vm.loading)
}
});
var vm = new Vue({
el: '#app',
data: {
apiCallParams: {
url: '',
method: '',
params: {}
},
author: 'aa'
},
created() {
this.apiCallParams.url = 'https://reqres.in/api/users/1';
this.apiCallParams.method = 'get';
console.log("passing this");
console.log(this.apiCallParams);
},
methods: {
search() {
console.log("searching")
this.url = 'https://reqres.in/api/users/2';
this.apiCallParams.method = 'get';
},
getResponse(response) {
console.log("back to parent");
console.log(response);
}
}
});
<script src="https://unpkg.com/axios#0.18.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/vue#2.5.13/dist/vue.js"></script>
<div id="app">
try {{title}}
<post :title="title" :author="author" :content="content">
</post>
</div>
<template id="post-template">
<h1>{{ title }}</h1>
<h4>{{ author }}</h4>
<p>{{ content }}</p>
</template>
If you click the "next user button" it updates the data in the parent component but it doesn't call my api-call-template.
You are only calling one time because your logic is at created(), which is called only once (when the component is created).
Considering your component is:
<api-call-template :api-call-params="apiCallParams" #get-response="getResponse">...
And you want to request every time api-call-params changes. What you could do is:
Move your logic from created() to a method, say performRequest()
Call this.performRequest() on created()
Add a watcher to apiCallParams prop
Call this.performRequest() on apiCallParams watcher
See demo below.
Vue.component('api-call-template', {
template: '#api-call-templates',
props: {
apiCallParams: Object
},
data() {
return {
result: [],
loading: false
}
},
methods: {
performRequest() {
console.log("entered?")
console.log(this.apiCallParams);
var vm = this;
vm.loading = true;
axios({
method: this.apiCallParams.method,
url: this.apiCallParams.url,
data: this.apiCallParams.params
}).then(function(response) {
vm.result = response.data;
vm.loading = false;
vm.$emit('get-response', response);
});
console.log(vm.loading)
}
},
created() {
this.performRequest();
},
watch: {
apiCallParams() {
this.performRequest();
}
}
});
var vm = new Vue({
el: '#app',
data: {
apiCallParams: {
url: '',
method: '',
params: {}
}
},
created() {
this.apiCallParams = {url: 'https://reqres.in/api/users/1', method: 'get'}
console.log("passing this");
console.log(this.apiCallParams);
},
methods: {
search() {
console.log("searching")
this.apiCallParams = {url: 'https://reqres.in/api/users/2', method: 'get'};
},
getResponse(response) {
console.log("back to parent");
console.log(response);
}
}
});
<script src="https://unpkg.com/axios#0.18.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/vue#2.5.13/dist/vue.js"></script>
<div id="app">
<button #click="search">next user</button>
<api-call-template :api-call-params="apiCallParams" #get-response="getResponse"></api-call-template>
</div>
<template id="api-call-templates">
<div>
<main>
<div v-if="loading">loading</div>
<div class="wrapper">
<div class="row">
<div v-for="res in result" :key="res.id">
<div class="col-md-4 cards">
<div>
results
<h3>{{ res.first_name }}</h3>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</template>
There is only one caveat: to trigger the watch, change apiCallParams at once, like:
this.apiCallParams = {url: 'https://reqres.in/api/users/2', method: 'get'};
Not property by property, like:
this.apiCallParams.url = 'https://reqres.in/api/users/2'; // don't do like this
this.apiCallParams.method = 'get'; // don't do like this
As Vue won't pick up changes if you do it prop by prop.