Adding new objects in Vue.js - javascript

I'm training in Vue.js by doing a quiz. I did a submit system and deleting a questions. But i want to add a Question adder. I tried this:
addQuestion()
{
if (this.question != "") {
this.questions.push(this.question);
this.question = "";
}
else
{
alert("You didnt write a question")
}
}
but that didnt works. Any help? Full code:
HTML:
<body>
<div id="app">
<div class="question" v-for="(question, index) in questions">
<h2>{{ question.question }}</h2><button #click="deleteQuestion(index)" class="doprava"><img src="criss.png"/></button>
<label v-for="answer in question.answers" class="answer" :class="{ 'answer-correct':answer.correct, 'answer-false':answer.false }">
<input type="checkbox" :value="answer.id" v-model="question.selected"> {{ answer.answer }}
</label>
</div>
<hr>
<button #click="onSubmit()">Submit</button>
<button #click="addQuestion()">Add Question</button><br><br>
<label >Question: </label>
<input v-model="question" type="text"><br><br>
<label v-model="answer-correct">Correct Answer: </label>
<input type="text"><br><br>
<label v-model="answer-false">Answer: </label>
<input type="text"><br><br>
<label v-model="answer-false">Answer: </label>
<input type="text">
</div>
<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="questions.js"></script>
<script src="script.js"></script>
</body>
JS:
var vm = new Vue({
el: "#app",
data: {
questions: questions,
result: 0
},
methods: {
onSubmit() {
this.result = 0
this.questions.forEach(question => {
question.answers.forEach(answer => {
answer.correct = question.correct.includes(answer.id);
answer.false = question.false.includes(answer.id);
});
});
},
deleteQuestion(index)
{
this.questions.splice(index, 1);
},
addQuestion()
{
if (this.question != "") {
this.questions.push(this.question);
this.question = "";
}
else
{
alert("You didnt write a question")
}
}
}
});
Questions in JS:
var questions = [
{
question: "1+1 is",
answers: [
{ id: 0, answer: "1", correct: false },
{ id: 1, answer: "0", correct: false },
{ id: 2, answer: "2", correct: false }
],
correct: [2],
selected: [],
false: [0, 1]
},
{
question: "Is Donald Trump egoistic?",
answers: [
{ id: 0, answer: "Yes", correct: false },
{ id: 1, answer: "No", correct: false }
],
correct: [0],
selected: [],
false: [1]
}
];

Any properties which you use in a v-model attribute for an input field should first be declared in data to make them reactive.
Here you are using question, answer-correct and answer-false in v-model. These should all be declared in data. Also, you are using answer-false twice, meaning both of these inputs will resolve to the same value. If you want to have multiple values here, you may want to create answer-false-a and answer-false-b or something like that.
You can add these to data like this:
data: {
questions: questions,
result: 0,
question: '',
answer-correct: '',
answer-false-a: '',
answer-false-b: ''
},

Replace your js code with this
var vm = new Vue({
el: "#app",
data: {
questions: questions,
result: 0,
question: {
question: "",
answers: [],
correct: [],
selected: [],
false: []
}
},
methods: {
onSubmit() {
this.result = 0
this.questions.forEach(question => {
question.answers.forEach(answer => {
answer.correct = question.correct.includes(answer.id);
answer.false = question.false.includes(answer.id);
});
});
},
deleteQuestion(index)
{
this.questions.splice(index, 1);
},
addQuestion()
{
if (this.question != "") {
this.questions.push(Object.assign({},this.question)); // this is to avoid pushing an object with the same reference address. A better way is to use the cloneDeep option from lodash library which can clone an object with multiple nested elements.
this.question = "";
}
else
{
alert("You didnt write a question")
}
}
}

Related

Combine the same attribute from different arrays in href (vue.js)

I’m new to vue.js. I’m building a wizard, but one step I can’t figure out.
I’ve got a checkbox list that outputs the list a user has choosen. So far so good.
In the same array there is an urlAnchor that needs to be combined in the final url.
So for instance, if the user selects extra1 and extra2, the list will be:
List view
Product: Extra 1
Price: 129
URL Anchor: /iii
Product: Extra 2
Price: 49
URL Anchor: /jjj
URL
URL needs to be google.com/iii/jjj
But I don’t know how to combine the 2 url anchors in 1 url.
Can someone help me with this please?
My code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<!-- wizard -->
<div id="app">
<div v-for="(extraItem, index) in extra" v-bind:id="'extraItem-'+extraItem.id" :class="'w-1/2 overflow-hidden my-1 px-1 item-'+index" >
<input type="checkbox" class="extraCheckboxes" :id="extraItem.name" :value="extraItem.name" v-model="checkedExtras" #click="saveExtra(index)">
<label class="form-check-label" :for="extraItem.id">{{extraItem.name}}</label>
</div>
<h1>Output:</h1>
<h2> List view </h2>
<div v-for="extra in extraOutput">
<strong>Product:</strong> {{extra.name}} <br />
<strong>Price:</strong> <span class="items" :data-value="extra.price">{{extra.price}}</span><br />
URL Anchor: {{extra.urlAnchor}}
<p> </p>
</div>
<h2> URL </h2>
<button><a v-for="extra in extraOutput" :href="'https://google.be'+extra.urlAnchor">Button text</a></button>
</div>
<!-- scripts -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js'></script>
<script type="text/javascript">
function extras() {
return [
{ id: 1, name: "Extra 1", price: 129, urlAnchor:"/iii" , selected: false },
{ id: 2, name: "Extra 2", price: 49, urlAnchor:"/jjj" , selected: false },
{ id: 3, name: "Extra 3", price: 59, urlAnchor:"/ggg" , selected: false },
{ id: 4, name: "Extra 4", price: 69, urlAnchor:"/hhh" , selected: false }];
}
new Vue({
el: "#app",
data() {
return {
extra: extras(),
checkedExtras: [],
data: [],
};
},
methods: {
saveExtra: function (index) {
this.extra[index].selected = !this.extra[index].selected;
}
},
computed: {
extraOutput: function () {
let extra = this.extra.filter(function (item) {
return item.selected === true;
});
return extra;
}
}
});
</script>
</body>
</html>
You've already done most of the work with by writing the extraOutput computed.
computed: {
extraOutput() {
const anchorsOfSelectedExtras = this.extra
.filter(extra => extra.selected) //get array of only selected
.map(extra => extra.urlAnchor); //turn array of objects into array of strings
return anchorsOfSelectedExtras.join(''); //turn array of strings into one joined string
}
}
You can also go even shorter:
computed: {
extraOutput() {
return this.extra.reduce((accumulator, extra) => extra.selected ? [...accumulator, extra.urlAnchor] : accumulator, []).join('');
}
}

Vue Survey not indexing questions

I have to build a quiz/survey app in vue.js, I'm pretty new to vue and still trying to learn it. I have a quiz/survey that asks different questions depending on what the user answers in the initial question.
so if the user picks yes it will display question 2 if the user picks no it will display question 3 etc.
I'm not sure what the best way of going around it but so far I have this.
Is there anyway i can use the value of my answer as the questionIndex after a person clicks next?
JS file:
"use strict";
let quiz = {
title: "Asbestos Quiz",
questions: [
{
text: 'Do you need help with an Asbestos Survey?',
answers: [
{
text: "Yes",
value: '2'`enter code here`
},
{
text: "No",
value: '3'
},
]
},
{
text: 'Was your property constructed pre 2000',
answers: [
{
text: "Yes",
value: '4'
},
{
text: "No",
value: '5'
},
]
},
{
text: 'Do you need an Asbestos Management plan?',
answers: [
{
text: "Yes",
value: '6'
},
{
text: "No",
value: '7'
},
]
}
]
};
var app = new Vue({
el: "#app",
data: {
quiz: quiz,
questionIndex: 0,
responses: [],
errors: [],
error: ''
},
methods: {
prev: function() {
this.questionIndex--;
},
next: function() {
if (this.responses[this.questionIndex] === undefined) {
this.errors[this.questionIndex] = 1;
this.error = 'Please select your answer';
}
else {
this.errors[this.questionIndex] = 0;
this.questionIndex++;
}
},
score: function() {
},
playAgain: function() {
this.questionIndex = 0;
}
}
});
HTML:
<html lang="en">
<head>
<title>Vue quiz/survey</title>
<meta name="viewport" content="width=device-width"/>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- <link rel="stylesheet" href="index.css"> -->
</head>
<body>
<div id="app">
<div class="container">
<div class="jumbotron mt-3">
<h1 class="mb-5">{{ quiz.title }}</h1>
<hr>
<p v-if="errors[questionIndex]" class="alert alert-danger">
{{ error }}
</p>
<div v-for="(question, index) in quiz.questions">
<div v-show="index === questionIndex">
<h4 class="mt-5 mb-3">{{ question.text }}</h4>
<div v-for="answer in question.answers" class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="radio"
:value="answer.value"
:name="index"
v-model="responses[index]">
{{answer.text}}
</label>
</div>
<div class="mt-5">
<button
class="btn btn-primary"
v-if="questionIndex > 0"
#click="prev">
prev
</button>
<button class="btn btn-secondary" #click="next">
next
</button>
</div>
</div>
</div>
<div v-show="questionIndex === quiz.questions.length">
<h3>Your Results</h3>
<p>
You are: {{ score() }}
</p>
<button class="btn btn-success" #click="playAgain">
Play Again!
</button>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="index.js"></script>
</body>
</html>
I thought that this sounded like a potentially interesting exercise, so I spent some time creating an implementation in a Vue CLI sandbox app that I built and use for trying out various ideas.
I learned a few things, and hopefully you will get something out of it. I left the 'Previous' functionality as a TODO if you decide you like it and want implement that yourself.
QuizQuestions.vue
<template>
<div class="quiz-questions">
<div class="jumbotron mt-3">
<h1 class="mb-5">{{ quiz.title }}</h1>
<hr>
<question v-if="!showResults" :question="currentQuestion" #answer-selected="processAnswer" />
<div class="mt-5">
<button class="btn btn-primary" v-if="currentQuestionId > 1 && !showResults" #click="getPreviousQuestion">
prev
</button>
<button v-if="!showResults" class="btn btn-secondary" #click="getNextQuestion">
{{ nextButtonLabel }}
</button>
</div>
<div v-if="showResults">
<h3>Your Results</h3>
<table class="table table-bordered">
<thead>
<tr>
<th>QUESTION</th>
<th>ANSWER</th>
</tr>
</thead>
<tbody>
<tr v-for="(response, index) in responses" :key="index">
<td>{{ getQuestionText(response.questionId) }}</td>
<td>{{ getAnswerText(response.answerId) }}</td>
</tr>
</tbody>
</table>
<button class="btn btn-success" #click="playAgain">
Play Again!
</button>
</div>
</div>
</div>
</template>
<script>
import quiz from './quiz.js';
import Question from '#/components/stackoverflow/Question'
export default {
components: {
Question
},
data() {
return {
quiz: quiz,
currentQuestionId: 1,
currentAnswerId: 1,
previousQuestionId: 0,
responses: [],
showResults: false,
errors: [],
error: ''
}
},
computed: {
currentQuestion() {
return this.quiz.questions.find( question => {
return question.id === this.currentQuestionId;
})
},
nextQuestionId() {
let retVal = 0;
if (this.currentAnswerId > 0) {
let tempAnswer = this.currentQuestion.answers.find( answer => {
return answer.id === this.currentAnswerId;
});
retVal = tempAnswer.nextQuestionId;
}
return retVal;
},
lastQuestion() {
return this.currentQuestion.answers[0].nextQuestionId === 0;
},
nextButtonLabel() {
return this.lastQuestion ? 'Finish' : 'Next';
}
},
methods: {
getPreviousQuestion() {
this.currentQuestionId = this.previousQuestionId;
},
getNextQuestion() {
// TODO: Look for existing response for this question in case the 'Previous' button was pressed
// If found, update answer
// Store current question id and answer id in responses
let response = { questionId: this.currentQuestionId, answerId: this.currentAnswerId };
this.responses.push(response);
if (this.lastQuestion) {
this.showResults = true;
return;
}
this.previousQuestionId = this.currentQuestionId;
this.currentQuestionId = this.nextQuestionId;
//console.log(this.responses);
},
getQuestionText(id) {
let result = this.quiz.questions.find( question => {
return question.id === id;
});
return result.text;
},
getAnswerText(id) {
// NOTE: Since answers are currently limited to '1 = Yes' and '2 = No',
// this method does not need to involve any look up
return id === 1 ? 'Yes' : 'No';
},
processAnswer(selectedAnswerId) {
this.currentAnswerId = selectedAnswerId;
},
score() {
return 'TODO'
},
playAgain() {
this.currentQuestionId = 1;
this.showResults = false;
this.responses = [];
}
}
}
</script>
Question.vue
<template>
<div class="question">
<h4 class="mt-5 mb-3">{{ question.text }}</h4>
<div class="form-check" v-for="(answer, idx) in question.answers" :key="idx">
<input class="form-check-input" type="radio"
:value="answer.id" v-model="answerId" #change="answerSelected">
<label class="form-check-label">
{{answer.text}}
</label>
</div>
</div>
</template>
<script>
export default {
props: {
question: {
type: Object,
required: true
}
},
data() {
return {
answerId: 1
}
},
watch:{
question() {
// Reset on new question
this.answerId = 1;
}
},
methods: {
answerSelected() {
this.$emit('answer-selected', this.answerId);
}
}
}
</script>
I also modified your test data by adding various ID properties to help with tracking, as well as created a few more placeholder questions.
quiz.js
const quiz = {
title: "Asbestos Quiz",
questions: [
{
id: 1,
text: 'Do you need help with an Asbestos Survey?',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 2
},
{
id: 2,
text: "No",
nextQuestionId: 3
},
]
},
{
id: 2,
text: 'Was your property constructed pre 2000',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 4
},
{
id: 2,
text: "No",
nextQuestionId: 5
},
]
},
{
id: 3,
text: 'Do you need an Asbestos Management plan?',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 6
},
{
id: 2,
text: "No",
nextQuestionId: 7
},
]
},
{
id: 4,
text: 'Question 4',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
},
{
id: 5,
text: 'Question 5',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
},
{
id: 6,
text: 'Question 6',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
},
{
id: 7,
text: 'Question 7',
answers: [
{
id: 1,
text: "Yes",
nextQuestionId: 0
},
{
id: 2,
text: "No",
nextQuestionId: 0
},
]
}
]
};
export default quiz;

VueJS How to filter array data when using dropdown select

In my vue-app I have an array of job-postings, which have different states, such as "active", "rejected", "draft", "not_active" etc. Now I have a TabMenu: All Jobs, Drafts and To Be Approved. Each of those MenuTabs, have their own Dropdown menu, where you are supposed to filter the jobpostings. I've realized that this feature is more complex than expected, or maybe I have spend too much time with the issues, but for some reason, I cannot manage, to show "all" for the individual MenuTab. For example, when I click on the "To Be Approved" MenuTab, I want to see all the jobpostings, with the status "Not approved" and "Rejected" (See data below in the code).
So my question is, how to solve this properly? Does the job-posting data object need to have a category too?
Any help is most welcome!
So, here is my component:
<template>
<div>
<div class="tabs">
<ul>
<li v-for="(tab, index) in menuTabs” :key="tab.id" :class="{ 'active': activeTab === index }"
#click="toggleList(tab, index)” >
<span>{{tab.label}}</span>
</li>
</ul>
</div>
<div class="dropdown has-prepend col-8" :class="{ active: isOpen }">
<div :class="{ active: isOpen }" class="dropdown-select" #click="toggle">
{{ selectedOption }}
<i class="icon-chevron_down" />
</div>
<div class="dropdown-options" v-show="isOpen">
<div class="option" v-for="tab in dropDownTabs" #click="select(tab)" :key="tab.id">
<span>{{ tab.status }}</span>
</div>
</div>
</div>
<div class="block">
<DataTable :data="filteredData" :columns="tableColumns" :filter="search" />
</div>
</div>
</template>
import DataTable from '../../snippets/DataTable';
export default {
components: { DataTable },
data() {
return {
isOpen: false,
search: "",
tableData: [
{
id: 1,
title: "Salesperson",
publish_date: "2019-07-10",
status: "active",
applicants: 23,
expiration_date: "2020-02-21"
},
{
id: 2,
title: "Developer",
publish_date: "2019-11-12",
status: "not_active",
applicants: 111,
expiration_date: "2020-02-21"
},
{
id: 3,
title: "Freelanceer",
publish_date: "2019-06-10",
status: "need_approval",
applicants: 222,
expiration_date: "2020-01-10"
},
{
id: 4,
title: "Construction worker",
publish_date: "2019-12-06",
status: "active",
applicants: 76,
expiration_date: "2020-03-15"
},
{
id: 5,
title: "IT support”
publish_date: "2019-11-20",
status: "draft",
applicants: 103,
expiration_date: "2020-04-31"
},
],
menuTabs: [
{
label: "All jobs",
options: [
{
status: "all",
},
{
status: "active",
},
{
status: "not_active"
}
]
},
{
label: "Drafts",
options: [
{
status: "all"
},
{
status: "draft"
}
]
},
{
label: "To Be Approved",
options: [
{
status: "all",
},
{
status: "need_approval",
},
{
status: "rejected"
}
]
},
],
dropDownTabs: [],
selectedOption: "",
selectedTabCategory: "",
category: "",
activeTab: "",
tableColumns: [
"id",
"title",
"publish_date",
"status",
"applicants",
"expiration_date"
]
}
},
computed: {
filteredData() {
let status = this.selectedOption;
let category = this.category;
let filtered = this.tableData.filter(data => {
if (status == "all") {
return data;
}
return data.status === status;
});
return filtered;
}
},
methods: {
toggleList(tab, index) {
this.category = tab.options[0].category;
this.selectedTabCategory = tab;
let currentTabOptions = this.selectedTabCategory.options;
this.clearDropDown();
this.selectedOption = currentTabOptions[0].status;
currentTabOptions.forEach(option => {
this.dropDownTabs.push(option);
});
this.activeTab = index;
},
toggle() {
this.isOpen = !this.isOpen;
},
select(tab) {
this.selectedOption = tab.status;
let category = tab.category;
let filtered = this.tableData.filter(data => {
return data.status === this.selectedOption;
});
this.isOpen = false;
return filtered;
},
clearDropDown() {
this.dropDownTabs = [];
}
},
created() {},
mounted() {
this.selectedOption = this.menuTabs[0].options[0].status;
this.selectedTabCategory = this.menuTabs[0].label;
this.category = this.menuTabs[0].options[0].category;
let defaultOptions = this.menuTabs[0].options;
defaultOptions.forEach(option => {
this.dropDownTabs.push(option);
});
this.activeTab = 0;
}
};
I am not sure if it will help you at all. However I will try anyway.
You should store the selected tab when you click on it. Then filter the this.tableData based on the selected tab options. Also you will need map the tab option options to array of strings, so you can check if the posting status is in there.
methods: {
toggleList (tab, index) {
this.selectedTabObject = tab
// rest of your code...
}
},
computed: {
filteredData () {
return this.tableData.filter(data => {
const states = this.selectedTabObject.options.map(opt => opt.status)
return states.includes(data.status)
})
}
}
Also I have created simple fiddle to mimic your problem.
https://jsfiddle.net/3hqnp7u2/7/

How to properly add custom validation to array in vuelidate

I have an array of objects with the following structure
varientSections: [
{
type: "",
values: [
{
varientId: 0,
individualValue: ""
}
]
}
]
I created a custom validation called isDuplicate, which checks for duplicate value for the property "type". For example
varientSections: [
{
type: "Basket",
values: [
{
varientId: 0,
individualValue: ""
}
]
},
{
type: "Basket", // ERROR: Duplicate with the "above" object
values: [
{
varientId: 1,
individualValue: ""
}
]
}
],
I was able to get my custom validation to work. However, the $invalid property will be false for all the objects present in the array. Hence, all the objects in the array will be highlighted in red
Below is my validation code:
validations: {
varientSections: {
$each: {
type: {
required,
isDuplicate(type, varient) {
console.log(varient);
const varientIndex = this.varientSections.findIndex(
v => v.type === type
);
var isWrong = true;
this.varientSections.forEach((varObject, index) => {
if (index !== varientIndex) {
if (varObject.type === varient.type) {
isWrong = false;
}
}
});
return isWrong;
}
},
values: {
$each: {
individualValue: {
required
}
}
}
}
}
},
Should be something like this.
<div v-for="(vs, index) in varientSections" :key="index">
<input :class="{'is-error': $v.varientSections.$each[index].type.$error}" type="text" v-model="vs.type">
<input :class="{'is-error': $v.varientSections.$each[index].value.$error}" type="text" v-model="vs.value>
</div>
Change the error class to fit your need.
I had the exact same need and found that the solution was quite simple once you wrap your head around what you're trying to do. Your validator needs to trigger only if the current item is a duplicate of any previous items.
Something like this:
validations: {
varientSections: {
$each: {
isUnique(currItem, itemArr) {
// Find the index of the first item in the array that has the same type
var firstIdx = itemArr.findIndex((item /*, index, arr*/) => currItem.type === item.type );
// If it's the same as the current item then it is not a duplicte
if(currItem === itemArr[firstIdx])
return true;
// All others are considered duplicates
return false;
},
type: { required }
}
}
}
this is worked for me
<b-row v-for="(field,index) in fields" :key="index">
<b-colxx lg="6">
<b-form-group :label="$t('target.name')">
<b-form-input v-model="field.name" :state="!$v.fields.$each[index].name.$error"/>
<b-form-invalid-feedback v-if="!$v.fields.$each[index].name.required">name is required</b-form-invalid-feedback>
</b-form-group>
</b-colxx>
<b-colxx lg="6">
<b-form-group :label="$t('target.value')">
<b-form-input v-model="field.value" :state="!$v.fields.$each[index].value.$error"/>
<b-form-invalid-feedback v-if="!$v.fields.$each[index].value.required">value is required</b-form-invalid-feedback>
</b-form-group>
</b-colxx>
</b-row>
.
data() {
return {
fields: [
{name: null, value: null},
{name: null, value: null} ]
}
},
.
validations: {
fields: {
$each: {
name: {
required
},
value: {
required
}
}
},
},

TypeError: e is undefined in vue.js

Now I'm getting frustrated :( This is my very first attempt at using vue.js which after jQuery, is the second JS framework I'm learning since I came to this planet. I have the following HTML:
var main = new Vue({
el: ".main-content",
data: {
heading: "First Vue Page",
usdamount: 0,
currencies: [{
label: "GBP",
rate: 0.7214,
value: 0
},
{
label: "EUR",
rate: 0.80829,
value: 0
},
{
label: "CAD",
rate: 1.2948,
value: 0
}
]
},
computed: {
updateCurrencies: function() {
console.log(this.usdamount);
var usd = parseFloat(this.usdamount);
for (var i = this.currencies.length - 1; i >= 0; i--) {
this.currencies[i].value = this.currencies[i].rate * usd;
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<section class="main-content">
<h1>{{ heading }}</h1>
<input type="number" v-on:change="updateCurrencies" v-model="usdamount">
<p class="cur-value" v-for="cur in currencies">
<strong>{{ cur.label }}</strong>: {{ cur.value }}
</p>
</section>
When I load the page everything works fine and I get a zero logged on the console. If I try to change the input I get:
TypeError: e is undefined
Stack trace:
we#https://cdn.jsdelivr.net/npm/vue:6:26571
X#https://cdn.jsdelivr.net/npm/vue:6:7441
...
I went to the part that is complaining and got even more lost. It's this function:
function we(t,e,n,r){(r||si).removeEventListener(t,e._withTask||e,n)}
I have no idea what's causing the error even after several attempts to change things and isolate the problem.
computed is for automatic recalculation whenever an involved data property from your VM changes. To attach a method to an event handler, use methods block:
var main = new Vue({
el: ".main-content",
data: {
heading: "First Vue Page",
usdamount: 0,
currencies: [{
label: "GBP",
rate: 0.7214,
value: 0
},
{
label: "EUR",
rate: 0.80829,
value: 0
},
{
label: "CAD",
rate: 1.2948,
value: 0
}
]
},
methods: {
updateCurrencies: function() {
console.log(this.usdamount);
var usd = parseFloat(this.usdamount);
for (var i = this.currencies.length - 1; i >= 0; i--) {
this.currencies[i].value = this.currencies[i].rate * usd;
}
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<section class="main-content">
<h1>{{ heading }}</h1>
<input type="number" v-on:change="updateCurrencies" v-model="usdamount">
<p class="cur-value" v-for="cur in currencies">
<strong>{{ cur.label }}</strong>: {{ cur.value }}
</p>
</section>
Since you indeed have the case that data depends on usdamount and should be adjusted whenever that value changes, making currencies a computed property would be an even better approach:
var main = new Vue({
el: ".main-content",
data: {
heading: "First Vue Page",
usdamount: 0,
},
computed: {
currencies() {
let cur = [{
label: "GBP",
rate: 0.7214,
value: 0
},
{
label: "EUR",
rate: 0.80829,
value: 0
},
{
label: "CAD",
rate: 1.2948,
value: 0
}
];
for (var i = cur.length - 1; i >= 0; i--) {
cur[i].value = cur[i].rate * parseFloat(this.usdamount);
}
return cur;
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<section class="main-content">
<h1>{{ heading }}</h1>
<input type="number" v-model="usdamount">
<p class="cur-value" v-for="cur in currencies">
<strong>{{ cur.label }}</strong>: {{ cur.value }}
</p>
</section>
This way you don't have to implement a listener yourself and instead use Vue's mechanisms to update your data and DOM.
As pointed out in my comment, you should be using the methods property for your v-on:change callback instead.
In a nutshell this means you have to change computed to methods
To understand the difference between the computed and methods property have a look at the vuejs documentation on both.
Computed Properties and Watchers
Events
Here is a working demo
var main = new Vue({
el: ".main-content",
data: {
heading: "First Vue Page",
usdamount: 0,
currencies: [{
label: "GBP",
rate: 0.7214,
value: 0
},
{
label: "EUR",
rate: 0.80829,
value: 0
},
{
label: "CAD",
rate: 1.2948,
value: 0
}
]
},
methods: {
updateCurrencies: function() {
console.log(this.usdamount);
var usd = parseFloat(this.usdamount);
for (var i = this.currencies.length - 1; i >= 0; i--) {
this.currencies[i].value = this.currencies[i].rate * usd;
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<section class="main-content">
<h1>{{ heading }}</h1>
<input type="number" v-on:change="updateCurrencies" v-model="usdamount">
<p class="cur-value" v-for="cur in currencies">
<strong>{{ cur.label }}</strong>: {{ cur.value }}
</p>
</section>

Categories

Resources