How to Bind Multiple Choice Quiz Answer in VueJS - javascript

I am creating a form in VueJS that allows a user to create a quiz using various question types. One of those question types is Multiple Choice. Currently, this is my data structure for the new quiz...
newQuiz: {
title: '',
questions: [
],
}
And this is the structure for Multiple Choice questions...
this.newQuiz.questions.push({
id: count+'mc',
type: 'mc',
question: 'Example',
correct: 0,
answers: [
'First answer',
'Second answer',
'Third answer',
'Fourth answer'
]
});
Currently, I have a v-for running on a div that creates each question, giving the radio buttons their proper labels, name, etc. Each radio button and it's respective label has an #click that runs a function as such...
this.newQuiz.questions[question].correct = answer;
I was curious if I could bind the radio buttons to the correct value, so that no updating function is necessary, or so that the default shows selected properly?
For context, here's the v-for as well.
<div v-for="(question, qIndex) in this.newQuiz.questions" :key="question.id">
<div v-if="question.type='mc'" class="mb-8">
<label class="block">{{ question.question }}</label>
<div v-for="(answer, aIndex) in question.answers" :key="answer">
<input type="radio" :id="question.id+'-'+aIndex" :name="question.id" #click="updateMCAnswer(qIndex, aIndex)">
<label :for="question.id+'-'+aIndex" #click="updateMCAnswer(qIndex, aIndex)">{{ answer }}</label>
</div>
</div>
</div>

If you want to bind the answer directly to the correct field of the corresponding question, try the following code
<div v-for="(question, qIndex) in this.newQuiz.questions" :key="question.id">
<div v-if="question.type='mc'" class="mb-8">
<label class="block">{{ question.question }}</label>
<div v-for="(answer, aIndex) in question.answers" :key="answer">
<input type="radio" :id="question.id+'-'+aIndex" :name="question.id" v-model="question.correct"
:value="answer">
<label :for="question.id+'-'+aIndex" #click="updateMCAnswer(qIndex, aIndex)">{{ answer }}</label>
</div>
</div>
</div>
You can remove the click event and directly bind the value by adding v-model="question.correct" and :value="answer" to the input field.

Related

How to use the "ngFor" without repeating the element you've applied it on?

Sorry, I can't post all the code here. But, if you could give me some hint as to what needs to be done, I'd appreciate it.
Let me try to explain my predicament.
So, this is the code snippet I've been trying to use ngFor and ngIf on:
<li class="list-group-item" *ngFor="let option of question.options">
<div class="form-check lead">
<input
class="form-check-input"
type="checkbox"
value=""
id="{{ option.id }}"
[(ngModel)]="option.selected"
(change)="onSelect(question, option)"
disabled="disabled"
/>
<label
class="form-check-label d-block fw-normal"
[attr.for]="option.id"
>
{{ option.name }}
<ng-container *ngFor="let obj of response"
><fa-icon
*ngIf="
obj.qId == option.questionId && obj.correct.includes(option.id)
"
[icon]="faCheck"
></fa-icon
></ng-container>
</label>
<div>
<img
src="{{ option.image }}"
alt=""
onerror="this.onerror=null; this.remove();"
width="55"
height="55"
/>
</div>
</div>
</li>
Before this code there is this element,
<div class="d-flex flex-column bg-white px-5" *ngFor="let question of filteredQuestions" style="white-space: pre-line">
filteredQuestions() loads the array of objects in which there are questions and inside of a question, there is array of options.
Now, what I want is to display the check icon right next to the correct option.
This is the response array:
response = [
{ qId: '60e57c069107a038085ae3a1', correct: [1001, 1002] },
{ qId: '60e57cc09107a038085ae3a2', correct: [1002] },
{ qId: '60e57d289107a038085ae3a3', correct: [1003] },
{ qId: '60e57d9e9107a038085ae3a4', correct: [1001, 1002, 1003] },
{ qId: '60e57e7c9107a038085ae3a5', correct: [1004] }];
This is the overall structure for the options:
[{id: number;
questionId: number;
name: string;
image: string;
selected: boolean;}]
I've tried to use the ngFor on different tags but with no luck. Right now, there are no check icons based on the if condition on the fa-icon tag.
And when I remove the ngIf from the fa-icon tag in this case, the checks just print multiple times. Where can I apply the tag so that the element won't repeat and I can get my desired output?
The problem is that your option.id is not number, so this obj.correct.includes(option.id) was not satisfied and does not meet your *ngIf condition in below line of code:
<fa-icon *ngIf="obj.qId == option.questionId && obj.correct.includes(+option.id)" [icon]="faCheck"></fa-icon>
So easy way to solve the problem is that put + next to the option.id:
obj.correct.includes(+option.id)
<ng-container *ngFor="let obj of response">
<fa-icon *ngIf="obj.qId == option.questionId && obj.correct.includes(+option.id)" [icon]="faCheck"></fa-icon>
</ng-container>
Here is working sample
And the result:

Vue.js change model attached to a form upon clicking a list

I have an array of objects. These objects are loaded into a list in vue.js.
Aside from this list, I have a form that displays data from one of these objects. I want to, when clicking one of the list's elements, it will bind this specific object to the form and show its data.
How can do this in Vue.js?
My list code is:
<div id="app-7">
<ul id="food-list" v-cloak>
<food-item v-for="item in foodList" v-bind:food="item" v-bind:key="item.id" inline-template>
<li class="food">
<div class="food-header">
<img :src="'img/' + food.slug +'.png'">
<div class="food-title">
<p>{{food.name}} |
<b>{{food.slug}}</b>
</p>
<p>quantity: {{food.quantity}}</p>
</div>
<div class="food-load"> // load into form upon clicking this
</div>
</div>
</li>
</food-item>
</ul>
</div>
Since I do not have the code for the form, this is my best guess without clarification.
You can add a click handler to the item you want to be clicked. It will pass the value of the food item into the method.
<div class="food-load" #click="setFoodItem(item)">
</div>
And when that method is called, it can assign the clicked item to a data property. I'm not sure where your form is, and if it is in a different component. If it is in a child component, you would have to pass it in as a prop, or emit an event to pass it to a parent component.
data() {
return {
//create a reactive field to store the current object for the form.
foodItemForm: null
};
},
methods: {
//method for setting the current item for the form.
setFoodItem(item) {
this.foodItemForm = item;
}
}
Missing quite a bit of info in your sample code, your script is very important to see to make sense of what you would like to accomplish and where things might be going wrong.
Here's a quick list of the issue I came across with your code:
v-for refers to an individual food item as 'item', inside the loop you're trying to access properties as 'food'
You don't wrap your code in a component unless you're importing the component
When binding a value to 'v-bind:src' (or shorthand ':src') only pass the url, you should be specifying this in your script not inline.
You're better off using a button and the 'v-on:click' (or shorthand '#click') to load your selected food item into your form
You should also include your Javascript
Regardless, here's how I would handle this (took the liberty in filling in some blanks):
<template>
<div id="app">
<ul id="food-list">
<!--<food-item v-for="item in foodList" v-bind:food="item" v-bind:key="item.id" inline-template>-->
<li v-for="item in foodList" class="food">
<div class="food-header">
<img :src="item.slug" v-bind:alt="item.slug" width="250px" height="auto">
<div class="food-title">
<p>{{item.name}} | <b>{{item.slug}}</b></p>
<p>quantity: {{item.quantity}}</p>
</div>
<button class="food-load" #click="loadFoodItem(item.id)">Load Food Item</button>
</div>
</li>
<!--</food-item>-->
</ul>
<form v-if="activeFoodId != null" id="foodItemForm" action="#">
<h3>Food Form</h3>
<label for="food-id">Id:</label>
<input id="food-id" type="number" v-bind:value="foodList[activeFoodId].id"><br/>
<label for="food-slug">Slug:</label>
<input id="food-slug" type="text" v-bind:value="foodList[activeFoodId].slug"><br/>
<label for="food-name">Name:</label>
<input id="food-name" type="text" v-bind:value="foodList[activeFoodId].name"><br/>
<label for="food-quantity">Quantity:</label>
<input id="food-quantity" type="number" v-bind:value="foodList[activeFoodId].quantity">
</form>
</div>
</template>
<script>
export default {
name: 'app',
data: function () {
return {
activeFoodId: null,
foodList: [
{
id: 1,
slug: 'http://3.bp.blogspot.com/-QiJCtE3yeOA/TWHfElpIbkI/AAAAAAAAADE/Xv6osICLe6E/s320/tomato.jpeg',
name: 'tomatoes',
quantity: 4
}, {
id: 2,
slug: 'https://img.purch.com/rc/300x200/aHR0cDovL3d3dy5saXZlc2NpZW5jZS5jb20vaW1hZ2VzL2kvMDAwLzA2NS8xNDkvb3JpZ2luYWwvYmFuYW5hcy5qcGc=',
name: 'bananas',
quantity: 12
}, {
id: 3,
slug: 'https://media.gettyimages.com/photos/red-apples-picture-id186823339?b=1&k=6&m=186823339&s=612x612&w=0&h=HwKqE1MrsWrofYe7FvaevMnSB89FKbMjT-G1E_1HpEw=',
name: 'apples',
quantity: 7
}
]
}
},
methods: {
loadFoodItem: function (foodItemId) {
console.log(foodItemId)
this.activeFoodId = foodItemId
}
}
}
</script>
<style>
/# Irrelevant #/
</style>
Hope it helps!

How can we achieve product filter page using angularjs with multi selection of checkboxes?

I have created a tabular page in which i need to filter the table with filters using a left side facets boxes of different category but with multi-selection options and i need to use Angularjs for this requirement.
I need to check/uncheck using the clear filter selection .
Any helping library can help to achieve the same or we need to do some logic around the checkboxes to achieve this.
My checkbox code looks like this:
<div class="col-sm-2" style="padding-top: 10px; padding-bottom: 20px;">
<div class="facetBx sltBx" ng-show="tabFilters.length > 0">
<p class="facetBxTitle"><i class="fa fa-filter"></i> Filter Selection
<a class="clrSlt" ng-click="clearAllFilters();">Clear</a>
</p>
<div class="facetBxChld" id="uRslctn">
<ul>
<li ng-repeat="item in tabFilters">
<div class="crop">
<strong title="{{item}}">{{item}}</strong>
</div>
<i class="fa fa-remove rmvThs" style="font-size: 14px;color:#000;float: right;" ng-click="checkItem(item, item,false);"></i>
</li>
</ul>
</div>
</div>
<div class="facetBx" ng-repeat="item in filters">
<p class="facetBxTitle bomtype">{{item.label}}</p>
<div class="facetBxChld" id="bomFacet">
<ul class="multiselect" style="max-height: 140px;overflow-y: auto;">
<li ng-repeat="(k,v) in item.values">
<input type="checkbox" ng-model='isSelected' ng-click='checkItem(item.name, k, isSelected)'>
<span> {{k}} ({{v}})</span>
</li>
<li ng-show="value.length == 0">
No Data Available.
</li>
</ul>
</div>
</div>
</div>
Below website are the reference of the code which i am trying to build:
www.jabong.com
The UI(HTML) is done but i am facing the trouble in the maintaining the checking and un-checking of the checkboxes which are not clearing off.
I believe i need to code something in the ng-model of checkbox to achieve it but i am not able to be successfull so need help on the same.
Sample Plunkur for the same:
enter link description here
Thanks in advance.
Basically you need to keep track of ng-model of checkboxes with some property in your $scope. I did this by modifying your $scope.filters and adding selected property inside it, like below.
var filters = [{
label: 'Brand',
name: 'brand',
values: {
Blackberrys: 503,
Arrow: 175,
ParkAvenue: 358
}
}, {
label: 'Color',
name: 'color',
values: {
Black: 100,
Green: 200,
Red: 300
}
}]
function loadFilters() {
$scope.filters = filters.map(function(filter) {
var filter = angular.copy(filter);
for (var key in filter.values) {
filter.values[key] = {
selected: false,
count: filter.values[key]
}
}
return filter;
})
}
loadFilters();
Then you can call loadFilters() any time you want to clear all filters. Please see POC attached below.
https://plnkr.co/edit/SADPoUpftnJkg1rMuSB9?p=preview
You should try 'ng-change' on checkboxes to trigger a method which changes the values according to checked and unchecked checkboxes.
For Ex.
<input type="checkbox" ng-model='isSelected' ng-click='checkItem(key, k, isSelected)' ng-change="yourMethodHere()">
in JS:
$scope.yourMethodHere = function() {
if (isSelected) {
// filter the values here
} else {
// non filtered values
}
}
By doing this you do not need to even maintain the values of exiting checked/unchecked checkbox.

KO radio works but does not initialise as checked

Here's a snippet from a knockout based answer editor
<!-- ko foreach: Answers -->
<div class="qa-box" data-bindx="event: { mousedown: mouseDown, dragend: dragEnd, dragstart: dragStart }">
<div class="qa-body">
<div class="radio">
<label>
<input type="radio" data-bind="attr: { name: 'Q' + $parentContext.$index(), value: $index }, checked: $parent.CorrectAnswer" /><span></span>
Tick the correct answer
<span data-bind="text:$parent.CorrectAnswer"></span>
</label>
<a href="#" data-bind="click: $parent.remove.bind($parent)">
<i class="fa fa-times"></i>
Remove this answer
</a>
<div class="form-control" contenteditable="true" data-bind="ckeditor: $data, attr: { id: 'Q' + $parentContext.$index() + 'A' + $index() }"></div>
</div>
</div>
</div>
<!-- /ko -->
<div>CorrectAnswer: <span data-bind="text: CorrectAnswer"></span></div>
You'll notice I put a bound span on the end of the radio button label so I can see what happens to the CorrectAnswer observable when I interact with the UI. This is how I know it's correctly bound to the view model. Clicking a radio button or its label changes the value of CorrectAnswer exactly as intended.
This also allows me to know that CorrectAnswer contains the value I expected.
Let's take a closer look at the binding in case it isn't obvious.
attr: { name: 'Q'+$parentContext.$index(), value: $index }, checked: $parent.CorrectAnswer
All the answers for a given question get the same name Qx and a value provided by the item's list position. When an item is clicked its list position is written to CorrectAnswer. This does happen, as evidence by the new value showing up in the telltale div.
So, what could be preventing the UI from initialising as checked when everything else is fine?
It isn't an initialisation problem, it's a type compatibility problem. The value of a radio input is of type string. The value provided by my view model is of type number. Knockout does a strong comparison and does not recognise a match.
See also Radio buttons Knockoutjs

Bind list to multiple target divs in WinJS

I have list declared in js file like this (full list contain 6 items, but can be more or less than that)
var dataArray = [
{
type: "item", title: "Cliff",
picture: "../../images/slike_etnologija/srednji_vek/01.jpg",
text: "some description"
},
{
type: "item", title: "Grapes",
picture: "../../images/slike_etnologija/srednji_vek/02.jpg",
text: "another description"
},
two templates declared in html file
<div id="galleryTemplate" data-win-control="WinJS.Binding.Template">
<div class="overlaidItemTemplate">
<img class="image img-responsive" src="#" data-win-bind="src: picture; alt: title" />
<div class="overlay">
<h2 class="ItemTitle" data-win-bind="innerText: title"></h2>
</div>
</div>
</div>
<div id="textTemplate" data-win-control="WinJS.Binding.Template">
<div>
<p data-win-bind="innerText: text"></p>
</div>
</div>
and two controls where i need to show data from list
<div class="col-md-12" id="basicFlipView"
data-win-control="WinJS.UI.FlipView"
data-win-options="{ itemDataSource : EtnologijaGallery.itemList.dataSource, itemTemplate: galleryTemplate }">
</div>
<p data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource : EtnologijaGallery.itemList.dataSource, itemTemplate: textTemplate }">
</p>
I'm trying to show image gallery in flipbox and text description associated to each image in listview next to it. Due to design i can't put both things in same template.
My flipbox works fine, and shows all images, but listview don't work. It shows, first, only 3 descriptions from list, and those 3 descriptions are shown in control with scroll bar, instead changing when user change image in flipbox.
Can someone help me solve this?
As mentioned on http://www.buildwinjs.com/tutorial/2WinJS_Binding/bindingInit/, WinJS Binding is one-time binding and you bound the same array to two separate controls.
I think you should check FlipView's onpageselected event and when that event occurs, update the div with the proper text. I guess the ListView is not needed to be used in this case at all.

Categories

Resources