Conditional rendering after function - VueJS [duplicate] - vue.js

This question already has answers here:
Use arrow function in vue computed does not work
(6 answers)
Closed 1 year ago.
I am currently writing a template for sending an email and after the function I want to conditionally render a success or error block. For some reason it is not working. The function itself is working, however neither success or error block is rendered. Please find my code below.
Template:
<form v-if="success==null" #submit.prevent="sendEmail" >
... //form code
<input name="submit" type="submit" class="btn" value="send" />
</form>
<b-alert variant="success" v-if="success">success</b-alert>
<b-alert variant="error" v-if="!success">error</b-alert>
Function:
data() {
return {
success: null
}
},
methods: {
sendEmail: (e) => {
... // request code
.then((result) => {
this.success=true
console.log('success')
}, (error) => {
this.success=false
console.log('error')
})
}
}

I think you just need to use strict comparing for error block
like:
success === false
There can be problems with !success because it's true for both null and false.
Also you can use string instead of boolean|null
var app = new Vue({
el: '#app',
data: {
type: 'form',
formData: {
email: '',
}
},
methods: {
sendEmail() {
if (this.formData.email) {
this.type = 'success';
} else {
this.type = 'error';
}
setTimeout(() => this.type = 'form', 10000);
}
}
})
.success {
color: green;
}
.error {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form v-if="type === 'form'" #submit.prevent="sendEmail">
<input type="email" v-model="formData.email" />
<button type="submit">Submit</button>
</form>
<div v-if="type === 'success'" class="success">Success!</div>
<div v-if="type === 'error'" class="error">Error!</div>
</div>

Are you sure your success/error handler are called ?/
In your method sendEmail you should define define it without an arrow function
sendEmail (e) {
... // request code
.then((result) => {
this.success=true
console.log('success')
}, (error) => {
this.success=false
console.log('error')
})
}

Try this
<b-alert :variant="success ? 'success' : 'error'" v-if="success!=null" :key="success">
<span v-if="success">success</span>
<span v-else>error</span>
</b-alert>

Related

input field value keeps getting reset #input?

I have created an custom reusable autocomplete component.The issue i am facing is whenever i start to type anything into the fields(#input) the value in the input field gets reset. I feel it has something to do within the code written in the debounce function but i am not sure.Plz help?
main.js
Vue.component('AutoComplete', {
props: ['list','value','title'],
data() {
return {
input: '',
}
},
template: `<template>
<div class="autocomplete">
<input style="font-size: 12pt; height: 36px; width:1800px; " type="text" v-model="input" #input="handleInput"/>
<ul v-if="input" >
<li v-for="(item, i) in list" :key="i" #click="setInput(item)" >
<template v-if="title!='manager'">
<div class="container">
<p>
<b>ID:</b>
{{item.id}}
</p>
<p>
<b>Description:</b>
{{item.description}}
</p>
</div>
</template>
<template v-else>
<div class="container">
<p>
<b>ID:</b>
{{item.id}}
</p>
<p>
<b>First Name:</b>
{{item.firstName}}
</p>
<p>
<b>Last Name:</b>
{{item.lastName}}
</p>
</div>
</template>
</li>
</ul>
</div>
</template>`,
methods: {
handleInput(e) {
console.log('inside handleInput')
this.$emit('input', e.target.value)
},
setInput(value) {
console.log('inside setInput')
this.input = value
this.$emit('click', value)
}
},
watch: {
$props: {
immediate: true,
deep: true,
handler(newValue, oldValue) {
console.log('new value is'+newValue)
console.log('old value is'+oldValue)
console.log('value inside handler'+this.value)
console.log('list inside handler'+this.list)
console.log('title inside handler'+this.title)
this.input=this.value
}
}
}
})
Currently i have called this component from JobDetail.vue page like this-
JobDetail.vue
<template>
<b-field label="Custom Action">
<AutoComplete v-on:input="getAsyncDataAction" v-on:click="(option) => {updateRowValue('records', props.index, 'action', option.id); props.row.action = option.id}" :value="props.row.action" :list="dataAction" title='action' >
</AutoComplete>
</b-field>
</template>
<script>
import { viewMixin } from "../viewMixin.js";
import debounce from "lodash/debounce";
import api from "../store/api";
const ViewName = "JobDetail";
export default {
name: "JobDetail",
mixins: [viewMixin(ViewName)],
data() {
return {
dataAction: [],
isFetching: false
};
},
methods: {
getAsyncDataAction: debounce(function(name) {
if (!name.length) {
this.dataAction = [];
return;
}
this.isFetching = true;
api
.getSearchData(`/action/?query=${name}`)
.then(response => {
this.dataAction = [];
response.forEach(item => {
this.dataAction.push(item);
});
})
.catch(error => {
this.dataAction = [];
throw error;
})
.finally(() => {
this.isFetching = false;
});
}, 500)
}
};
</script>
viewmixin.js
computed: {
viewData() {
return this.$store.getters.getViewData(viewName)
},
objectData() {
return this.$store.getters.getApiData(this.viewData.api_id).data
},
sessionData() {
return this.$store.getters.getSessionData()
},
isLoading() {
return this.$store.getters.getApiData(this.viewData.api_id).isLoading
},
newRecord() {
return this.$route.params.id === null;
}
},
I don't understand why the input fields value keeps resetting #input. Please help and also let me know if this is the correct approach?

How to add to array from method in vue.js?

I'm having a weird proplem that I can't understand
I have a registration form, and on any input I'm executing the method on blur;
<input class='form-control' placeholder='Username' #blur="watchVal" v-model="username">
Method
watchVal : function(){
if(this.username == ""){
this.errors['username'].push('Username is empty');
}
}
Data:
data: function(){
return {
username: "",
errors: {
'username' : []
}
}
}
When I blur without writing any value, nothing is added to this.errors['username'], unless I type a letter in any field.
I've also tried to make validation on submit, but found same problem that no error is added to the array unless I type in any input,
Can anyone explain to me what I am doing wrong??
I faced similar issue. How I solved this.
your data:
data: function(){
return {
username: "",
errors: {}
}
}
your Method
watchVal (key) {
let errors = this.errors
if (this[key] === '') {
errors[key].push('Emai empty')
}
this.errors = errors
}
your HTML
<input class='form-control' placeholder='Username' #blur="watchVal('username')" v-model="username">
<p>{{errors['username']}}</p>
You must display error variable in your HTML template then it will be solved.
Your source is working. See in full mode if you cannot see error messages.
new Vue({
el: "#app",
data: function() {
return {
username: '',
errors: {
username: []
}
}
},
methods: {
watchVal : function(){
if(this.username == ""){
this.errors['username'].push('Username is empty');
}
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input class='form-control' placeholder='Username' #blur="watchVal" v-model="username">
<p class="text-sm text-danger" v-for="(error, index) in errors.username" :key="index">
{{ error }}
</p>
</div>

Making a value from a fetch call available immediately

I have a Go program written that has a form which checks for the existence of a file by calling a fetch on a route inside the Go code. If the file exists or not, a boolean is return inside of a JSON as fileExists. I'm having trouble with the fetch call's JSON updating this.found boolean immediately.
What happens is that when I press enter or click the buttons, the form is submitted via call to onSubmit where the checkFile() is called which does the fetch. Somehow, I have to press enter twice to see the value returned by the fetch as it is not updating the this.found immediately. I am probably thinking about this the wrong way, but I figure it wouldn't to ask. Here's the code, if anyone can help so that clicking or submitting will be based on the correct value returned by the checkFile call:
<div class="jumbotron">
<div class="container">
<div class="alert alert-dark" role="alert">
</div>
<h1 class="display-3">Title</h1>
<div id="app">
<form ref="myForm" method="POST" v-on:submit.prevent="onSubmit" action="/push" class="needs-validation" id="myForm" novalidate="true">
<div class="form-group">
Canned List:
<input v-model="cannedlist" ref="cannedlist" type="text" class="form-control" name="fileListFile" id="filelist"
aria-describedby="fileListFileHelp"
autocomplete="off" :disabled="!!individuallist" v-on:submit.prevent="onSubmit" />
</div>
<div class="form-group">
Path List:
<textarea v-model="individuallist" ref="cannedlist" :disabled="!!cannedlist" class="form-control" rows=10 name="fileListRaw" id="files" autocomplete="off"></textarea>
</div>
<div class="form-group">
<button v-on:submit.prevent="onSubmit" type="submit" name="button" value="submit" id="submitButton" class="btn btn-primary" :disabled="isDisabled">Submit</button>
<button v-on:submit.prevent="onSubmit" type="submit" name="button" value="checkOnly" id="checkOnlyButton" class="btn btn-primary" :disabled="isDisabled">Submit 2</button>
</div>
</form>
</div>
</div>
</div>
<script src="/static/js/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
// cannedlist: "filelist.txt",
individuallist: "",
found: false,
},
computed: {
isDisabled: function() {
//found = !found;
return (this.cannedlist.length <= 0 && this.individuallist.length <= 0);
},
},
methods: {
isDisabledNew: function() {
alert((this.cannedlist.length <= 0 && this.individuallist.length <= 0));
// return (this.cannedlist.length <= 0 && this.individuallist.length <= 0);
return false;
},
isFieldDisabled: function(e) {
//console.log(app.$refs.individuallist.disabled);
return false;
},
onSubmit: function() {
if (this.cannedlist.length > 0) {
this.checkFile();
if (this.found == true) {
this.$refs.myForm.submit();
return;
}
} else if (this.individuallist.length > 0) {
this.$refs.myForm.submit();
return;
}
},
checkFile: function() {
var url = 'http://localhost:9000/CheckIfFileExists?name=' + this.cannedlist;
return fetch(url)
.then(response => {
if (response.ok) {
var v = response.json().then( response => { this.found = response.fileExists; } );
return this.found;
}
return response.json().then(error => ({ error }));
});
return this.found;
},
}
});
</script>
Your onSubmit function calls checkFile and expects found to be updated:
onSubmit: function() {
if (this.cannedlist.length > 0) {
this.checkFile();
if (this.found == true) {
this.$refs.myForm.submit();
return;
}
} else if (this.individuallist.length > 0) {
this.$refs.myForm.submit();
return;
}
},
But checkFile returns a Promise. The Promise resolves by updating found. So you need to put your found checking inside a then block:
onSubmit: function() {
if (this.cannedlist.length > 0) {
this.checkFile()
.then(() => {
if (this.found == true) {
this.$refs.myForm.submit();
}
});
return;
} else if (this.individuallist.length > 0) {
this.$refs.myForm.submit();
return;
}
},
Here're the changes I made:
methods: {
onSubmit: function(event) {
if (this.cannedlist.length > 0) {
this.checkFile()
// This promise capture is the key I was missing
.then( (data) => {
this.found = data.fileExists;
if (this.found == true) {
this.$refs.myForm.submit();
} else {
alert("File not found: " + this.cannedlist);
}
});
} else if (this.individuallist.length > 0) {
this.$refs.myForm.submit();
}
},
checkFile: function() {
var url = 'http://localhost:9000/CheckIfFileExists?name=' + this.cannedlist;
return fetch(url).then((response) => response.json());
}

export default issue with VueJS ES5

I am developing a website, partially in Vue using ES5.
I need to get data from a Vue Child component to be sent back up to the parent, the way explained to me was via using export default {} However I am constantly recieving syntax errors and I am not sure why because from what i see, I am following Mozillas recomendations.
My question component:
var question = Vue.component('question', {
props: {
scenarios: Array,
scenario: Object,
post: Boolean
},
data: function () {
return ({
choice: 0,
counter: 0,
finished: false
});
},
export default {
methods: {
onClickButton: function (event) {
this.$emit('clicked', 'someValue')
},
postResponse: function () {
var formData = new FormData();
formData.append(this.choice);
// POST /someUrl
this.$http.post('Study/PostScenarioChoice', formData).then(response => {
// success callback
}, response => {
// error callback
});
},
activatePost: function () {
if (this.counter < this.scenarios.length) {
this.counter++;
}
else {
this.finished = true;
}
}
}
},
template:
`<div>
<div v-if="this.finished === false" class="row justify-content-center">
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioLeftImg" />
</button>
</div>
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioRightImg" />
</button>
</div>
</div>
<finished v-else></finished>
</div >
});
I get the error in the browser console Expected ':' which is on the line export default {
Any assistance will be greatly appreciated.
You have written completely wrong syntax of JavaScript. What you are trying to do here is to put "export default" in the place where object key should go. I will provide correct code here, but I strongly suggest you go and learn the fundamentals of JavaScript including arrays and objects in order to be able to read and write correct and valid JavaScript. Here is some good learning material for beginners:
http://eloquentjavascript.net/
https://maximdenisov.gitbooks.io/you-don-t-know-js/content/
And here is the fixed Vue component:
export default Vue.component("question", {
props: {
scenarios: Array,
scenario: Object,
post: Boolean
},
data: function () {
return ({
choice: 0,
counter: 0,
finished: false
});
},
methods: {
onClickButton: function (event) {
this.$emit("clicked", "someValue");
},
postResponse: function () {
var formData = new FormData();
formData.append(this.choice);
// POST /someUrl
this.$http.post("Study/PostScenarioChoice", formData).then(response => {
// success callback
}, response => {
// error callback
});
},
activatePost: function () {
if (this.counter < this.scenarios.length) {
this.counter++;
}
else {
this.finished = true;
}
}
},
template:
`<div>
<div v-if="this.finished === false" class="row justify-content-center">
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioLeftImg" />
</button>
</div>
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioRightImg" />
</button>
</div>
</div>
<finished v-else></finished>
</div>`
});
The actual answer was a simple misunderstanding of the information presented here. The use of export default was irrelevant in my implementation however this was not easily visible until I started noticing the emit posting the parent element later on.
The actual implementation was as follows:
var question = Vue.component('question', {
props: {
scenarios: Array,
scenario: Object,
post: Boolean,
counter: Number
},
data: function () {
return ({
choice: 0,
finished: false
});
},
methods: {
onClickButton: function (event) {
this.$emit('clicked', 'someValue');
},
postResponse: function () {
var formData = new FormData();
formData.append(this.choice);
// POST /someUrl
this.$http.post('Study/PostScenarioChoice', formData).then(response => {
// success callback
}, response => {
// error callback
});
}
},
template:
`<div>
<div v-if="this.finished === false" class="row justify-content-center">
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioLeftImg" />
</button>
</div>
<div class="col-lg-6">
<button class="btn btn-light" href="#" v-on:click="this.onClickButton">
<img class="img-fluid" v-bind:src="this.scenarios[this.counter].scenarioRightImg" />
</button>
</div>
</div>
<finished v-else></finished>
</div >`
});
The receiving method in the parent element is as follows:
onClickChild: function (value) {
console.log(value) // someValue
this.showTimer = true;
this.countdownTimer();
if (this.counter < this.scenarios.length) {
this.counter++;
}
else {
this.finished = true;
}
}

Vuejs reactive v-if template component

I'm struggling to understand how to make my component reactive. At the moment the button is rendered correctly but once the create/delete event happens, the template does not change. Any tips on how to update the component after the event has taken place?
new Vue({
el: '#app'
});
Vue.component('favourite-button', {
props: ['id', 'favourites'],
template: '<input class="hidden" type="input" name="_method" value="{{ id }}" v-model="form.listings_id"></input><button v-if="isFavourite == true" class="favourited" #click="delete(favourite)" :disabled="form.busy"><i class="fa fa-heart" aria-hidden="true"></i><button class="not-favourited" v-else #click="create(favourite)" :disabled="form.busy"><i class="fa fa-heart" aria-hidden="true"></i></button><pre>{{ isFavourite == true }}</pre>',
data: function() {
return {
form: new SparkForm({
listings_id: ''
}),
};
},
created() {
this.getFavourites();
},
computed: {
isFavourite: function() {
for (var i = 0; this.favourites.length; i++)
{
if (this.favourites[i].listings_id == this.id) {
return true;
}
}
},
},
methods: {
getFavourites() {
this.$http.get('/api/favourites')
.then(response => {
this.favourites = response.data;
});
},
create() {
Spark.post('/api/favourite', this.form)
.then(favourite => {
this.favourite.push(favourite);
this.form.id = '';
});
},
delete(favourite) {
this.$http.delete('/api/favourite/' + this.id);
this.form.id = '';
}
}
});
Vue.component('listings', {
template: '#listing-template',
data: function() {
return {
listings: [], favourites: [],
};
},
created() {
this.getListings();
},
methods: {
getListings() {
this.$http.get('/api/listings')
.then(response => {
this.listings = response.data;
});
}
}
});
Vue expects HTML template markups to be perfect. Otherwise you will run into multiple issues.
I just inspected your template and found an issue - the first <button> element does not close.
Here is the updated version of your code:
Vue.component('favourite-button', {
props: ['id', 'favourites'],
template: `
<input class="hidden" type="input" name="_method" value="{{ id }}" v-model="form.listings_id"></input>
<button v-if="isFavourite == true" class="favourited" #click="delete(favourite)" :disabled="form.busy">
<i class="fa fa-heart" aria-hidden="true"></i>
</button> <!-- This is missing in your version -->
<button class="not-favourited" v-else #click="create(favourite)" :disabled="form.busy">
<i class="fa fa-heart" aria-hidden="true"></i>
</button>
<pre>{{ isFavourite == true }}</pre>
`,
...
Note the comment on 7th line above, the closing </button> tag is not present in your template.
As a side note, if you do not want to type back-slash at the end of every line to make multi-line template strings, you can use back-ticks as shown in my code example above. This will help you avoid markup errors leading to Vue component issues and many hours of debugging.
Another reference: Check out "Multi-line Strings" in this page: https://developers.google.com/web/updates/2015/01/ES6-Template-Strings
Relevant lines (copied from above page):
Any whitespace inside of the backtick syntax will also be considered part of the string.
console.log(`string text line 1
string text line 2`);
EDIT: Found a possible bug in code
Here is another issue in your create method of favourite-button component:
methods: {
// ...
create() {
Spark.post('/api/favourite', this.form)
.then(favourite => {
this.favourite.push(favourite); // Note: This is the problem area
this.form.id = '';
});
},
//...
}
Your success handler refers to this.favourite.push(...). You do not have this.favourite in data or props of your component. Shouldn't it be this.favourites?