VueJS: Passing data to methods using vuejs - vue.js

This is my code for my html in App.vue file.
<b-form #submit="onSubmit">
<b-form-group>
**nested v-model binding**
<div v-for="(question, index) in questionsList" :key="question.question">
<h4>{{question.question}}</h4>
<div v-for="options in question.responses" :key="options.options">
<input
type="radio"
:name="'question'+index"
:value="options.options"
v-model="responses['question'+index]"
/>
{{options.options}}
<br />
</div>
</div>
<b-button variant="outline-primary" type="submit">Submit</b-button>
</b-form-group>
</b-form>
*** script ***
export default {
data() {
return {
responses: {},
questionsList: [
{
id: "1",
question: "What is the full form of HTTP?",
responses: [
{ options: "Hyper text transfer package" },
{ options: "Hyper text transfer protocol" }
],
},
{
id: "2",
question: "HTML document start and end with which tag pairs?",
responses: [
{ options: "HTML" },
{ options: "WEB" }
],
},
answersheet: {
q1: "Hyper text transfer protocol",
q2: "HTML"
},
methods: {
onSubmit(event) {
event.preventDefault();
var score = 0;
for (var key of Object.keys(this.responses)) {
console.log(key + " -> " + this.responses[key]);
//displays the answers of the users
**trying to compare the answers from my answersheet but keeps saying undefined**
if (this.responses[key] == this.answersheet[key]) {
score = score + 1;
}
}
displays score to console
console.log(score);
}
}
What I'm trying to do here is to calculate the number of correct answers and then send the result to an api, but my code won't work.
I'm still a noob with vuejs and this is my first project for my class.

I've made some points and made some changes on your code
You needed to make sure opening and closing brackets written
Check indexes and make sure you are comparing right values
Read documentation carefully and try to follow getting started instructions. It gives you quite good orientation.
Have fun
new Vue({
el: '#app',
data() {
return {
isSubmitted: false,
score: 0,
responses: {},
questionsList: [
{
id: "1",
question: "What is the full form of HTTP?",
responses: [
{ options: "Hyper text transfer package" },
{ options: "Hyper text transfer protocol" }
],
},
{
id: "2",
question: "HTML document start and end with which tag pairs?",
responses: [
{ options: "HTML" },
{ options: "WEB" }
],
},
],
answersheet: {
question0: "Hyper text transfer protocol",
question1: "HTML"
},
};
},
methods: {
tryAgain() {
this.responses = {};
this.score = 0;
this.isSubmitted = !this.isSubmitted;
},
onSubmit() {
for (var key of Object.keys(this.responses)) {
console.log(key + " -> " + this.responses[key]);
if (this.responses[key] == this.answersheet[key]) {
this.score++;
}
}
this.isSubmitted = !this.isSubmitted
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link href="https://unpkg.com/bootstrap#4.5.2/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue#2.16.0/dist/bootstrap-vue.css" rel="stylesheet" />
<script src="https://unpkg.com/bootstrap-vue#2.16.0/dist/bootstrap-vue.js"></script>
<div id="app">
<b-form #submit.prevent="onSubmit">
<b-form-group v-if="!isSubmitted">
<!-- nested v-model binding** -->
<div v-for="(question, index) in questionsList" :key="question.question">
<h4>{{question.question}}</h4>
<div v-for="options in question.responses" :key="options.options">
<input
type="radio"
:name="'question'+index"
:value="options.options"
v-model="responses['question'+index]"
/>
{{options.options}}
<br />
</div>
</div>
<b-button variant="outline-primary" type="submit">Submit</b-button>
</b-form-group>
<div v-else>
Your score: {{ score}}
<br>
<b-button variant="outline-primary" #click="tryAgain">Try Again</b-button>
</div>
</b-form>
</div>

Related

show/hide elements inside of for loop

I have the following:
<div v-for="num in [1,2,3,4,5]" :key="num ">
<customelement0 :num ="num" />
<span #click="show = !show" > Details: </span>
<customelement1 v-if="show" :num ="num" />
<hr />
</div>
and:
export default {
data() {
return {
show: false
};
},
};
However, in this implementation Whenever show changes it affects all of the customelement1s and will show/hide all of them.
How would one solve this problem, so that whenever a user clicks on the span it only shows/hides one element in the loop?
P.S.: in reality, the length of the loop is much longer, than what's indicated above
The problem here is essentially that your show is a variable for the entire component, and isn't linked to one of your array elements.
Generally, we don't really tend to hard-code an array into the html, but rather in the data, as shown in the other answers.
The other answers show it with num being coded into the object but you could also do something like this. Note that other field is optional and not required. The main advantage of this method is that you don't need to code every number.
<div v-for="(item, i) in items" :key="i">
<!-- Note I do i+1 so that it matches the 1,2,3 in your example -->
<!-- if you're okay with having 0,1,2 you can omit the +1 -->
<customelement0 :num="i+1" />
<span #click="item.show = !item.show" >Details: </span>
<customelement1 v-if="item.show" :num="i+1" />
<hr />
</div>
export default {
data() {
return {
items: [
{show: false, other: "foo"},
{show: false, other: "bar"},
//...
],
};
},
};
You can change number to object and toggle property:
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="todo in todos">
<label>
<input type="checkbox"
v-on:change="toggle(todo)"
v-bind:checked="todo.done">
<del v-if="todo.done">
{{ todo.text }}
</del>
<span v-else>
{{ todo.text }}
</span>
</label>
</li>
</ol>
</div>
new Vue({
el: "#app",
data: {
todos: [
{ text: "1", done: false },
{ text: "2", done: false },
{ text: "3", done: true },
{ text: "4", done: true },
{ text: "5", done: true },
]
},
methods: {
toggle: function(todo){
todo.done = !todo.done
}
}
})
You can to save the value isShow for each element in the array. So you need to use an array of objects in you data and do something like this:
export default {
data() {
return {
numList: [
{
num: 1,
isShow: true
},
{
num: 2,
isShow: true
}
]
};
},
};
In your template:
<div v-for="item in numList" :key="item.num">
<customelement0 :num="item.num" />
<span #click="item.isShow = !item.isShow" > Details: </span>
<customelement1 v-if="item.isShow" :num="item.num" />
<hr />
</div>

Cannot read property 'name' of undefined" Vue JS

[tex]It shows me on the console the following error message " Cannot read property 'name' of undefined". I cant reach out to the name in my Data even that is structured same as in my validation function.**emphasized text*
<template>
<component v-bind:validationsRule="validations" v-bind:dataFields="dataFields" v-bind:is="currentStep.details"></component>
<button class="btn" v-on:click="backStep" id="back">Back</button>
<div v-show="$v.dataFields.name.$error">this has an error</div>
<button class="btn" v-on:click="nextStep" id="next">Next</button>
</div>
</template>
<script>
import DetailsForm from './DetailsForm'
import DetailsForm1 from './DetailsForm1'
import { required } from 'vuelidate/lib/validators'
export default {
name: 'ProductDetails',
props: {
step: {
type: Array
}
},
data: function () {
return {
items: [
{ stepTitle: 'Completed step', details: DetailsForm },
{ stepTitle: 'Step Two', details: DetailsForm1 },
{ stepTitle: 'Step Three', details: DetailsForm },
{ stepTitle: 'Step Four', details: DetailsForm }
],
activeNumber: 0,
dataFields: {
id: null,
hasDescription: false,
name: '',
description: ''
},
validations: function () {
if (!this.dataFields.hasDescription) {
return {
name: {
required
}
}
} else {
return {
name: {
required
},
description: {
required
}
}
}
}
}
},
<--- DetailsForm.vue --->
Here is my other part of Code from the other file that I am using as component on this file
<template>
<div>
<div class="form-group" :class="{ 'form-group--error': $v.dataFields.name.$error}">
<label class="form__label">Name</label>
<input class="form__input" v-model.trim="$v.dataFields.name.$model"/>
<div v-show="$v.dataFields.name.$error">This has an error</div>
</div>
<div class="form-group">
<label class="form__label" for="hasDesc">Has description?</label>
<div class="toggle">
<input id="hasDesc" type="checkbox" v-model="hasDescription"/>
<label for="hasDesc">
<div class="toggle__switch"></div>
</label>
</div>
</div>
<div class="form-group" v-if="hasDescription" :class="{ 'form-group--error': $v.dataFields.description.$error}">
<label class="form__label">Description</label>
<input class="form__input" v-model.trim="$v.dataFields.description.$model"/>
</div>
<tree-view :data="$v" :options="{rootObjectKey: '$v', maxDepth: 2}"></tree-view>
</div>
</template>
<script>
export default {
name: 'DetailsForm',
data () {
return {
}
},
props: {
validationsRule: {
type: Function,
default: () => {
}
},
dataFields: {
type: Object
}
},
validations () {
return this.validationsRule()
}
}
</script>
Your validation rules do not contain a property dataFields, but you're calling $v.dataFields.name in your template. Since dataFields is not defined, the error Cannot read property 'name' of undefined makes sense.
Untested, but if you changed your validations function to return something like this, it should work:
validations: function () {
var validations = {
dataFields: {
name: {
required
}
}
};
if (this.dataFields.hasDescription)
validations.dataFields.description = { required };
return validations;
}

Vue-MultiSelect Checkbox binding

The data properties of the multi-select component does not update on change. Check-boxes doesn't update on the front-end.
Expected Behavior: The check-boxes should get ticked, when clicked.
Link to code: https://jsfiddle.net/bzqd19nt/3/
<div id="app">
<multiselect
select-Label=""
selected-Label=""
deselect-Label=""
v-model="value"
:options="options"
:multiple="true"
track-by="library"
:custom-label="customLabel"
:close-on-select="false"
#select=onSelect($event)
#remove=onRemove($event)
>
<span class="checkbox-label" slot="option" slot-scope="scope" #click.self="select(scope.option)">
{{ scope.option.library }}
<input class="test" type="checkbox" v-model="scope.option.checked" #focus.prevent/>
</span>
</multiselect>
<pre>{{ value }}</pre>
</div>
new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
data: {
value: [],
options: [
{ language: 'JavaScript', library: 'Vue.js', checked: false },
{ language: 'JavaScript', library: 'Vue-Multiselect', checked: false },
{ language: 'JavaScript', library: 'Vuelidate', checked: false }
]
},
methods: {
customLabel(option) {
return `${option.library} - ${option.language}`;
},
onSelect(option) {
console.log('Added');
option.checked = true;
console.log(`${option.library} Clicked!! ${option.checked}`);
},
onRemove(option) {
console.log('Removed');
option.checked = false;
console.log(`${option.library} Removed!! ${option.checked}`);
}
}
}).$mount('#app');
In your code you call onSelect and try to change the option argument of this function inside the function:
option.checked = true;
This affects only the local variable option (the function argument). And doesn't affect objects in options array in the data of the Vue instance, the objects bound with checkboxes. That's why nothing happens when you click on an option in the list.
To fix it find the appropriate element in options array and change it:
let index = this.options.findIndex(item => item.library==option.library);
this.options[index].checked = true;
Here is the code snippet with fix:
new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
data: {
value: [],
options: [
{ language: 'JavaScript', library: 'Vue.js', checked: false },
{ language: 'JavaScript', library: 'Vue-Multiselect', checked: false },
{ language: 'JavaScript', library: 'Vuelidate', checked: false }
]
},
methods: {
customLabel (option) {
return `${option.library} - ${option.language}`
},
onSelect (option) {
console.log("Added");
let index = this.options.findIndex(item => item.library==option.library);
this.options[index].checked = true;
console.log(option.library + " Clicked!! " + option.checked);
},
onRemove (option) {
console.log("Removed");
let index = this.options.findIndex(item => item.library==option.library);
this.options[index].checked = false;
console.log(option.library + " Removed!! " + option.checked);
}
}
}).$mount('#app')
* {
font-family: 'Lato', 'Avenir', sans-serif;
}
.checkbox-label {
display: block;
}
.test {
position: absolute;
right: 1vw;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link href="https://unpkg.com/vue-multiselect#2.0.2/dist/vue-multiselect.min.css" rel="stylesheet"/>
<script src="https://unpkg.com/vue-multiselect#2.0.3/dist/vue-multiselect.min.js"></script>
<div id="app">
<multiselect
select-Label=""
selected-Label=""
deselect-Label=""
v-model="value"
:options="options"
:multiple="true"
track-by="library"
:custom-label="customLabel"
:close-on-select="false"
#select=onSelect($event)
#remove=onRemove($event)
>
<span class="checkbox-label" slot="option" slot-scope="scope" #click.self="select(scope.option)">
{{ scope.option.library }}
<input class="test" type="checkbox" v-model="scope.option.checked" #focus.prevent/>
</span>
</multiselect>
<pre>{{ value }}</pre>
</div>

How to use query parameter in Vue search box?

I have a page with a search box on it using Vue. What I want to do is this: when a user comes from another page with a parameter in the URL (e.g., myurl.com/?windows), I capture the parameter and populate the search field to run the search on that string when the page loads. If there's no parameter, nothing happens.
I'm capturing the string from the URL with JavaScript, but don't see how to get it in the input to run the search.... I created a method but don't see how to apply it.
<div id="app">
<input type="text" v-model="search" placeholder="Search Articles" />
<div v-for="article in filteredArticles" v-bind:key="article.id" class="container-fluid to-edges section1">
<div class="row">
<div class="col-md-4 col-sm-12 section0">
<div class="section0">
<a v-bind:href="article.url" v-bind:title="toUppercase(article.title)">
<img class="resp-img expand section0"
v-bind:src="article.src"
v-bind:alt="article.alt"/>
</a>
</div>
<div>
<h3 class="title-sec">{{ article.title }}</h3>
<p>{{ article.description }}</p>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var pgURL = window.location.href;
var newURL = pgURL.split("?")[1];
console.log(newURL);
</script>
// Filters
Vue.filter('to-uppercase', function(value){
return value.toUpperCase();
});
new Vue({
el: "#app",
data: {
articles: [
{ id: 1, title: 'Trend Alert: Black Windows', category: 'Windows', description: 'Timeless, elegant, and universally flattering, black is an excellent color to add to any wardrobe – or any window. Get in the black with this chic design trend.', src: 'http://i1.adis.ws/i/stock/Trending_Polaroid_Black_Windows_2018_1?$trending-mobile$', url: '/{StorefrontContextRoot}/s/trending/trend-alert-black-windows', alt: 'Pantone Colors image' },
{ id: 2, title: 'Benefits of a Pass-Through Window', category: 'Windows', description: 'Whether you’re adding a pass-through window in order to enjoy an al fresco aperitif or for easier access to appetizers in the kitchen, we’re big fans of bringing the outdoors in.', src: 'http://i1.adis.ws/i/stock/polaroid_benefitsofapassthroughwindow655x536?$trending-mobile$', url: '/{StorefrontContextRoot}/s/trending/kitchen-pass-through-bar-window', alt: 'Benefits of a Pass-Through Window image' }, etc....
],
search: ''
},
methods: {
toUppercase: function(title){
return title.toUpperCase();
},
urlSearch: function(newURL) {
if (newURL) {
return this.search = newURL;
}
}
},
computed: {
filteredArticles: function() {
// returning updated array based on search term
return this.articles.filter((article) => {
return article.category.match(new RegExp(this.search, "i"));
});
}
}
})
You can call the urlSearch method during the mounted hook:
mounted() {
this.urlSearch(newURL)
},
methods: {
urlSearch(url) {
return this.search = url
}
},

Vue do not save clicked element style

I am generating small list of items. After click on every single item it should change style. Only one item can be selected. If you click on another item first item reverse to default value.
I have follow code:
<div class="LngList">
<div class="lng" v-for="item in languages">
<button :class="[ isLangSelected ? 'ui inverted basic button' : 'ui inverted red basic button' ]" #click=LangSelect(item.lang)>{{item.lang}}</button>
</div>
</div>
My method:
data: function (){
return {
isLangSelected: false,
mycode: "",
languages: [
{lang:'D'},
{lang:'C#'},
{lang:'Python'}
],
selectedLanguage: ""
}
},
methods: {
LangSelect(lang)
{
this.selectedLanguage = lang;
if(this.selectedLanguage.length != "")
{
this.isLangSelected = !this.isLangSelected;
}
}
}
But when I am clicking outside the button I am losing selected style.
I did small gif to show the problem:
This is possible of course with buttons, but why don't you use a radio input instead? Having only one item selected, that's what they are done for.
new Vue({
el: '#app',
data() {
return {
languages: [{
lang: 'D'
},
{
lang: 'C#'
},
{
lang: 'Python'
}
],
selectedLanguage: ''
};
},
computed: {
isLangSelected() {
return this.selectedLanguage !== '';
}
}
});
input[type=radio] {
visibility: hidden;
width: 0;
}
input[type=radio]:checked + span {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
<label v-for="lang in languages">
<input type="radio" name="languages"
v-model="selectedLanguage" :value="lang.lang">
<span>{{lang.lang}}</span>
</label>
<div v-if="isLangSelected">
Selected language is: {{ selectedLanguage }}
</div>
</div>