Scope a single input in Vue built from JSON - vue.js

I am building a simple quiz with vuejs. I've been working in https://codepen.io/jasonflaherty/pen/NBaJLO on this. I am able to get the correct responses to the checked input, however, it is not scoped to this.input and cascades to all radio buttons. I tried to use bind class with:
<span class="" v-bind:class="{ 'badge badge-success' : isCorrect, 'badge badge-danger' : isWrong, 'showthis' : showIt }">{{response.correct}}</span>
and am currently using:
v-if="isCorrect"
v-if="isWrong"
However, it is the same issue with the scope bound to the same input vs all of them.
What am I missing in vue to make this distinction?

You need to track at the question level rather than the global level.
Here are a couple simple changes you can make. Notice the input changes for value and v-model and also the conditions for the correct/wrong spans. This creates a property, selection on each question to track the currently selected answer for this specific question.
<li v-for="response in question.responses">
<label class="form-check-label">
<input class="form-check-input" type="radio"
:key="index"
:name="question.group"
:value="response.text"
v-model="question.selection">
{{response.text}}
<span v-if="question.selection === response.text && response.correct ==='Correct!' ">
Correct
</span>
<span v-else-if="question.selection === response.text && response.correct !=='Correct!' ">
Wrong
</span>
</label>
</li>
You could clean it up some more by altering your data model. There is no reason to save the correct text in the data. You can simply have the answer as a property and determine the textual response based on the selection.
var quizquestions = {
questions: [
{
text: "Subtract 219 from 500.",
group: "qone",
responses: [281, 719, 218, -219],
answer: 281, // correct answer
selection: null, // for v-model to track selected
},
]
};

Related

Vue/Vuelidate: Dynamically show required star (*) based on requiredIf

I'm creating a dynamic re-usable vue component that wraps a text input and includes the vuelidate validation stylings, etc.:
<template>
<div class="form-group" :class="{'form-group--error': validator.$error}">
<label>{{ label }}<span v-if="validator.$params.required">*</span></label>
<input type="text v-model="validator.$model" />
<div class="error" v-if="validator.$error && !validator.required">* This field is required</div>
</div>
<template>
<script>
export default {
name: "FormTextField",
props: ["validator", "label"]
}
</script>
(validator prop is $v from parent)
The problem I have is trying to show the <span>*</span> dynamically based on if the field is required. This currently works (v-if="validator.$params.required") as long as I only specify the required validator:
fieldName: {
required: required
}
Now, I need to instead declare my validation like this:
fieldName: {
required: requiredIf( ... )
}
The question is how to access the result of the requiredIf function? validator.$params.required will always be true since it's just checking if the param is there. And validator.required is the status of the validation, not the result of the requiredIf call to see whether it SHOULD be required or not.
Any suggestions on how I can show the required star dynamically based on vuelidate state?
Instead of validator.$params.required, check validator.required, which is only defined when there's a required or requiredIf validator rule applied. The value of validator.required is true when the field is missing, or false otherwise.
When the field is optional (has no required/requiredIf rule), the validator.required property does not exist, so we can't use v-if="!validator.required". Instead, explicitly compare validator.require to false:
<label>{{ label }}<span v-if="validator.required === false">*</span></label>
<div class="error" v-if="validator.$error && validator.required === false">* This field is required</div>
demo

vue v-model does not seem to be working in modal

I am pretty new to vue, and am trying to use it in a bootstrap modal. The relevant div in the modal is as follows.
<div class="form-group row">
<label for="priceQCField" class="col-sm-2 col-form-label">Price<span class="red"> *</span></label>
<input type="number" step="0.01" class="form-control col-sm-4" id="priceQCField" name="priceQCField" min="0" v-model="job.price">
</div>
I read some other questions about vue returning strings rather than numbers, so I have converted the job.price to a number inside my method to call the modal
showPriceJob: function (job) {
this.job = job;
this.job.price = parseFloat(this.job.price);
$('#mdlPriceJob').modal('show');
},
However, job.price refuses to appear in the input field either as a string or a number. I know it is available to the modal as I can see it using <span>{{job.price}}</span>.
Can anyone advise me please?
Additional - I think it is a display issue - if I change the input field, the entry in the <span> changes
2nd update - initial table
<tr class="light-grey" v-for="job in jobs" v-on:click="viewJob(job)">
<td>{{job.id}}</td>
<td>{{job.customerName}}</td>
<td>{{job.description}}</td>
<td v-bind:class="job.dueDate | dateColour">{{job.dueDate | dateOnly}}</td>
<td>£{{job.price}} {{job.isEstimate | priceEstimated}}</td>
<td>{{job.delivery}}</td>
</tr>
Upd.
According to your comments to my answer you are using v-for and you can't use this.job within your method. You should give us more code to see the whole picture.
Upd.2
You have showed more code but I didn't see any v-for so I am confused. You can try to use something like this if job is a property of appData.jobs:
showPriceJob: function (job) {
this.appData.jobs.job = Object.assign({}, job);
this.appData.jobs.job = parseFloat(this.appData.jobs.job.price);
$('#mdlPriceJob').modal('show');
},
But I'm not sure about this because I don't see where job is declared.
Upd.3
Oh! Wait! You have this code:
data: appData.jobs, but data should be in this format:
data: function(){
return {
appData: {
jobs: [],
},
}
},
Or show me what is your appData.jobs variable is.

Need to make v-model dynamic

I have a questionnaire that has MANY questions. The questions are either an input or a true|false|n/a response. The following allows me to load the questions from the database BUT the v-model isn't working so the responses aren't showing
<div v-for="(q, idx) in questions" :key="idx">
<div v-if="q.type === 'input'">
<question-input :question="q.question" :name="q.name"
v-model="comp.vaf.vendor_info.disassembly['q.name']"> . </question-input>
</div>
<div v-if="q.type === 'boolean'">
<question-boolean :question="q.question" :name="q.name"
v-model="comp.vaf.vendor_info.disassembly['q.name']"> . </question-boolean>
</div>
Your q.name is a variable hence it should not be in single quote as it would be considered as an property directly rather than considering it as an object value.
Try as below =>
<question-boolean :question="q.question" :name="q.name"
v-model="comp.vaf.vendor_info.disassembly[q.name]">

Vue - How to scope variables within template

Can I scope data somehow within a component's template?
For example, if I have the following code:
data() {
a: {test: 'Test A'},
b: {test: 'Test B'}
}
Currently in the template I have to do
<div class="a">{{ a.test }}</div>
<div class="b">{{ b.test }}</div>
Is there any way I can scope data per element? For example, something like:
<div :scope="a">{{ test }}</div><!-- This prints 'Test A' -->
<div :scope="b">{{ test }}</div><!-- This prints 'Test B' -->
I do know that I can extract each item to a component, however, I was wondering if there is a way to do that within the same template? As it does not have own logic etc. so I don't want to extract it to a separate component just to scope the variable. However, it can get tedious repeating the same variable name many times.
For example, I have a form to create a new item, which has a number of inputs. I keep them under a variable (for example) newItem, which looks like
newItem: {
input1: "",
input2: "",
input3: null,
input4: false,
// etc...
}
And in the template I would like to do
<div :scope="newItem">
<input v-model="input1"/>
<!-- etc.. --->
</div>
Instead of
<input v-model="newItem.input1"/>
<!--- etc... --->
NO.
There's no such a way to do. And also v-model needs to be specified to the particular data else it will not work. Otherwise, we can think of v-for.

Vue - altering model arrays

I have a simple Vue template consisting of an object with two arrays (dummy/placeholders vs actual data).
data() {
var tableColumns = new Array();
tableColumns.push({"dummyValues": ["date 1", "date 2"], "csvValues": []});
var variables = {
"tableColumns": tableColumns
};
return variables;
}
<td v-for="(item, key, index) in tableColumns">
<span v-if="item.csvValues.length == 0" v-for="dummyValue in item.dummyValues">
{{dummyValue}} <br />
</span>
<span v-else v-for="value in item.csvValues">
{{value}} <br />
</span>
</td>
At first, I only have dummy values. Something happens along the way (I parse a CSV file) and I need to exchange the dummy data I've first rendered with actual values.
I thus append my real data to the real data array (csvValues) and hope the v-else will take care of it.
This doesn't work - why, is my approach wrong?
Apparently, Vue can't switch between two data sources (dummyValues and csvValues).
I had to use a third one, which now holds/cycles elements from either of the first two. So, instead of building my view from one or the other arrays, I'm popping/pushing stuff to my third array (renderingValues) and always iterating that one.
Want to render dummyValues?
- pop everything (previously csvValues) from renderingValues
- push everything from dummyValues to renderingValues
- iterate renderingValues