AngularJS checkboxes for quiz - angularjs

i'm new to angular, stuck with, what i hope is a rather easy problem.
I Have a question and several answers. There might be 1-3 correct answers.
Answers can be selected by checkbox. In the value field i save if the answers is correct (true) or wrong (false). I was hopping that i can simply get things work by getting ng-valid and ng-invalid.
E.g. The answers is wrong, when clicked on the checkbox for this answer the anwswer text should be red (or sth.). When deselecting it should go back to normal. Same with correct answers.
That's how i currently try to do it:
<label ng-repeat="a in q.answers"><input type="checkbox" value="{{ a.correct }}" ng-pattern="true" ng-model="field.value"> {{ a.title }}</label>
So if the value is true it should match the pattern 'true'. Does not working, ng-pattern does not seem to have any effect.
Anyone know how to solve this.
If it's more sophisticated than i thought i might be doing the check with jquery. But i want to try pure angular first.
PS: i tried to find a answer to this, but i couldn't find anything that suits my problem.

Adding onto the class suggestion from Jared Reeves, here is one way that you might implement the checkbox answers:
<label ng-repeat="answer in question.answers" ng-class="{'invalid': answer.checked && !answer.correct, 'valid': answer.checked && answer.correct}">
<input type="checkbox" ng-change="select(field, answer)" ng-model="answer.checked"> {{answer.title}}
</label>
Selected answer: {{field.value}}
// inside the controller
$scope.field = {
value: null
};
$scope.question = {
answers: [
{title: "A (incorrect)", correct: false},
{title: "B (incorrect)", correct: false},
{title: "C (correct)", correct: true},
{title: "D (incorrect)", correct: false}
]
};
$scope.select = function(field, answer) {
if (answer.checked) {
field.value = answer;
} else {
field.value = null;
}
};
Here's a working example: http://plnkr.co/edit/Qa2VrukMD61y8Jv4iKjx?p=preview

Related

Using angularjs how can I get checked and unchecked values

Here I am binding values to ng-click as
<input type="checkbox" ng-model="a.CheckAssign"ng-click="myFunctionnew(a.ENQUIRY_CODE,a.CheckAssign)" />
enquiryArr=[]
$scope.myFunctionnew = function(enqcode,checkassign)
{
enquiryArr.push(enqcode + '&' + checkassign)
var uniqueNames = [];
$.each(enquiryArr, function (i, el) {
if ($.inArray(el, uniqueNames) === -1) {
uniqueNames.push(el);
console.log(uniqueNames)
}
else
{
console.log(uniqueNames);
}
}
Here when I check and uncheck the checkbox the value is storing multiple
Please help me how can I store Enquiry code and T/F values.
If I understand your issue correctly, you're running into duplicate entries in your enquiryArr. You're running into this because you're trying to manually keep track of the array in an odd fashion. While there are ways to correct the way you're storing it, it's even easier to have angular track what is checked and what is not.
Using the following DataSet:
$scope.enquiries = [
{ENQUIRY_CODE: 'ENQUIRY_1', ENQUIRY_NAME: 'First Enquiry', selected: false},
{ENQUIRY_CODE: 'ENQUIRY_2', ENQUIRY_NAME: 'Second Enquiry', selected: false},
{ENQUIRY_CODE: 'ENQUIRY_3', ENQUIRY_NAME: 'Third Enquiry', selected: false},
{ENQUIRY_CODE: 'ENQUIRY_4', ENQUIRY_NAME: 'Fourth Enquiry', selected: false}
];
And binding the selected field to your checkboxes ng-model
<div ng-repeat="e in enquiries">
<input type="checkbox" ng-model="e.selected" />
{{e.ENQUIRY_NAME}}
</div>
You can determine which one is checked simply by looking at the $scope.enquiries array and looking for selected: true (or false if you want to know the ones not checked).
In javascript you can use a simple Array Filter to get the selected values as such:
$scope.enquiries.filter(e => e.selected);
Or if you want to get them in your template, you can also use the filter pipe as such:
{{(enquiries | filter:{selected:true})}}
Here is an interactive plunkr that you can view this.
Misc Note: If you do want to run something when the checkbox is clicked, you can still use an ng-click and run your custom code, however, I would recommend using ng-change as there are other ways to change the selected value of a checkbox other than clicking.
I have test your code and its working fine. your problem is not so much clear so you can check following test code.
<div ng-controller="MyController">
<div ng-repeat="dt in data">
<input type="checkbox" ng-model="CheckAssign" ng-click="myFunctionnew(dt.name,dt.volume)" />
</div>
</div>
Javascript
$scope.myFunctionnew = function(enqcode,checkassign)
{
enquiryArr.push(enqcode + '&' + checkassign)
var uniqueNames = [];
for(i=0; i <enquiryArr.length; i++) {
if ($.inArray(enquiryArr[i], uniqueNames) == -1) {
uniqueNames.push(enquiryArr[i]);
console.log(uniqueNames)
}
else
{
console.log(uniqueNames);
}
}
}
JSFiddle

Vue JS for updating checkboxes based on radio selection

I basically have the same question as this guy, but using Vue JS instead of jQuery.
I have a list of N groups bound to my array ensemble_groups and represented by radio buttons. Selected value is mapped to selected_group.
I have a list of actors bound to my array cast with the variables actor_id, actor_name and groups. Each actor is pre-assigned to any number of groups. They're represented by checkboxes and mapped to an array visible_actors (when checked).
Here's my Vue JS powering the above data (I imagine the method is all jacked up, and I probably need a computed property of some sort):
new Vue({
el: '#schedule-builder',
data: {
selected_group: 'Entire Cast',
visible_actors: [],
ensemble_groups: [
"Entire Cast",
"Leads",
"Dancers",
"Children",
"Deselect All",
],
cast: [
{
actor_id: "123",
actor_name: "Carl",
groups: ["Entire Cast","Leads",],
},
{
actor_id: "234",
actor_name: "Max",
groups: ["Entire Cast","Leads","Children",],
},
{
actor_id: "345",
actor_name: "Sheryl",
groups: ["Entire Cast","Dancers",],
},
{
actor_id: "456",
actor_name: "Chip",
groups: ["Entire Cast","Children",],
},
],
},
methods: {
selectGroup: function() {
// uncheck all
visible_actors=[];
// add people in this group to visible_actors
for person in cast {
if (person.groups.indexOf(selected_group) > -1) {
visible_actors.push(person.actor_id);
}
}
}
})
When a user clicks on a radio button for a group, I want to select only the actors' checkboxes who are in that group. So if the user selects "Children", only the actors in the "Children" group should be checked (Max and Chip, per my example).
And, obviously, checking an actor (rather than a group) shouldn't affect the rest of the selections. I mention this because I got it partially working at one point, where selecting a group also selected the correct people, but when I clicked on a person suddenly everyone else was deselected. User can click either a group OR a person, and the expected behavior is that
Here's my HTML template:
<div id="schedule-builder">
<div class="select-groups">
<h3>Ensemble Groups</h3>
<template v-for="group in ensemble_groups">
<input name="select_group[]" id="group_#{{ $index }}"
v-model="selected_group"
:value="group"
#click="selectGroup"
type="radio">
<label for="group_#{{ $index }}">#{{ group }}</label>
</template>
</div>
<div class="select-individuals">
<h3>Cast Members</h3>
<template v-for="person in cast">
<input name="actors[]" id="actor-#{{ $index }}"
v-model="visible_actors"
:value="person.actor_id"
:checked="visible_actors.indexOf(person.actor_id) > -1"
type="checkbox">
<label for="actor-#{{ $index }}">
#{{ person.actor_name }}
</label>
</template>
</div>
</div>
Any help is appreciated... I've been banging my head on it for a couple days already.
This is a tough question to answer well but I'll try.
I would not rely on a computed property for the checked state. Let v-model handle that for you. You can do
<input
type="checkbox"
name="actors[]"
v-model="selected_actors"
:value="actor">
and that will manage the array of selected_actors for you as their values change.
I'm at work and plan on elaborating on this answer a little later but here's a fiddle of how I'd approach the situation: https://jsfiddle.net/crswll/806shzzg/7/

What is the best way to update an attribute with integer values in a angularJS form?

I am trying to create a form using angularJs, I have some attributes which is of integer type.
for example, I have variable called admin_events, it has 3 values, 0, 1, 2 to indicate the user's right/permisson to access different files. The number 0 means no right, 1 means view only, 2 means full right.
Now I want to create a form for editing this attributes for the administrator.
How should I go about doing this?
I am thinking of using a angularJs to make 3 radio buttons/checkboxes of 1,2,and 3, so that the administrator can just click on the option and it will update the attributes.
Anyone has any suggestion on what is the best way to do this?
This is one possible approach. See inline comments for explaination.
app.controller('MainCtrl', function($scope, $filter) {
// set up your model
$scope.allPermissions = [
{ "id" : 3, "name" : "ADMIN", selected: false },
{ "id" : 2, "name" : "READ", selected: false },
{ "id" : 1, "name" : "WRITE", selected: false } ];
// Use $watch to detect changes in the model.
// But you might as well want to call this from an event or whatever...
$scope.$watch('allPermissions', function(newval, oldval){
if (oldval != newval)
{
// only return the checked values using $filter
var selectedPermission = $filter('filter')($scope.allPermissions, {selected: true});
// 'selectedPermission' will return a list of all selected permissions.
// Use 'selectedPermission[0].id' to return the first value.
$scope.admin_events = selectedPermission[0].id;
}
},true);
});
And in your HTML
<body ng-controller="MainCtrl">
<p ng-repeat="permission in allPermissions">
<input type="checkbox" ng-model="permission.selected"/>
{{permission.name}}
</p>
admin_events: {{admin_events}}
</body>
In the end, I decided to use a range type input to achieve the selection. It is much easier to implement.
<input ng-model= "user.activities.events" name="activities.events" type="range" max="2" min="0" class="form-control">
But i think #sjokkogutten's answer is not bad too. But the checkboxes are not mutally exclusive. Thanks anyway #sjokkogutten.

Angular - how to switch between expressions without losing user input

I'm new to Angular so please ELI5 if possible :)
I have a multiple-choice questionnaire with the setup for each question as below:
Question
Choice A
Choice B
Choice C
My controllers look like this:
scope.questionnaire = [
{
questionEN: "Thing?",
choice1EN: "Yes",
choice2EN: "No",
choice3EN: "Maybe",
questionPR: "Treasure?",
choice1PR: "Yarrr",
choice2PR: "Arrrrr",
choice3PR: "Parrrrley"
}
]
My current questionnaire looks like this:
<div ng-bind="questionnaire.questionEN"></div>
<input type="radio" name="{{questionnaire.questionEN}}" ng-model="questionnaire.choice" ng-value="questionnaire.choice1EN">{{questionnaire.choice1EN}}</input> <br />
<input type="radio" name="{{questionnaire.questionEN}}" ng-model="questionnaire.choice" ng-value="questionnaire.choice2EN">{{questionnaire.choice2EN}}</input> <br />
<input type="radio" name="{{questionnaire.questionEN}}" ng-model="questionnaire.choice" ng-value="questionnaire.choice3EN">{{questionnaire.choice3EN}}</input>
What I want to do is that at the start of the questionnaire, there are two buttons that will let the user select between languages.
[English] [Pirate]
So I have two questions, actually:
How do I do the language swapping without losing the user input (for example: they have answered questions 1-5 in [English], and while reading #6, scroll back up and hit the button to swap to [Pirate]. Their selected answers for 1-5 should remain, but all questions+choices have been "translated").
Is there a better way of arranging my controllers?
The answer to this really varies on your particular set of needs. Start with developing a logical model of the problem you are trying to solve.
Things to ask yourself:
Are the questions dynamic (loaded from a database) or static?
How do I need to store the answer? Can I store a choice ID, key, etc. or do I have to use the human readable value?
Does the model that captures the answers have to be different than the model that presents the question?
Do you need to store the language they selected along with the answers?
As you think about it more, other questions will follow.
Anyways, to give you something close to an answer here is a suggestion. If the questions are dynamic, you'll want to create a better structure for defining questions and the choices that are related to them. I'd also give them a key or ID so they can be referred to as concrete concepts. Not only does this help tie the model to database entries, making storing and retrieving easy, it makes it easier for you to handle the language issue.
Here's an example of such a design. You may wish to track the answers separately if your design requires, in my case I just kept it simple and kept the questions and answers in one model.
The question model is an array of objects that look like this:
{
key: 'q1', // unique key for question
text: { // a dictionary of language codes and the text of the question in that language
'en': 'What is the answer the question 1?',
'pr': 'Ahoy 1?'
},
choices: [{
key: 'q1a1', // a unique key for the choice
text: { // a dictionary of language codes and the text of the choice in that language
'en': 'Answer 1',
'pr': 'Arr 1'
}
}, {
key: 'q1a2',
text: {
'en': 'Answer 2',
'pr': 'Arr 2'
}
}],
value: null // the field that stores the user's selected choice
}
There is a helper function to get the language-specific text of the selected choice:
$scope.getAnswer = function(question) {
var answer = $filter('filter')(question.choices, {key: question.value});
if (answer.length == 1) return answer[0].text[$scope.selectedLanguage];
return '(no answer)';
};
The meat of your HTML template that displays the questions looks like this:
<div ng-repeat="question in model">
<label>{{question.text[selectedLanguage]}}</label>
<div>
<label ng-repeat="choice in question.choices">
<input type="radio" name="{{question.key}}" ng-value="choice.key" ng-model="question.value" />{{choice.text[selectedLanguage]}}
</label>
</div>
</div>
Alternative
For the heck of it, here's yet another example of how you might do it. This method is useful if you do not have dynamic question/choice data, but with some effort could still be used for dynamic data. It involves keeping localized UI strings in separate tables, assigning questions and choices unique keys, and then using a translation service to retrieve the correct string. In this example, I use the angular-translate module. I load it with tables of localization strings during .config of my module, specify a default language, and just use the translate filter when displaying questions and choices. This lets me greatly simplify my question model and get rid of some extra work in the views and controller.
My questions simply become:
{
key: 'q1',
choices: [
'q1a1',
'q1a2'
],
value: null
}
I add a watch on the language dropdown to change the current language:
$scope.$watch('selectedLanguage', function(value) {
if (value === null) return;
$translate.use(value);
});
And clean up the template:
<label>{{question.key | translate }}</label>
<div>
<label ng-repeat="choice in question.choices">
<input type="radio" name="{{question.key}}" ng-value="choice" ng-model="question.value" />{{ choice | translate }}
</label>
</div>
You could add a function using ng-click then set your $scope to a new questionnaire.
So you would have something like this:
var english = {
question: "Thing?",
answers: {
choice1: {text: "Yes", val: 1},
choice2: {text: "No", val: 2},
choice3: {text: "Maybe", val: 3}
}
}
var pirate= {
question: "Treasure?",
answers: {
choice1: {text: "Yarrr", val: 1},
choice2: {text: "Arrrrr", val: 2},
choice3: {text: "Parrrrley", val: 3}
}
}
// default
$scope.questionnaire = english;
$scope.choice;
// change language
$scope.changeLanguage = function (lang) {
switch (lang) {
case 0:
$scope.questionnaire = english;
break;
case 1:
$scope.questionnaire = pirate;
break;
}
}
Then for your buttons
<button ng-click="changeLanguage(0)">English</button>
<button ng-click="changeLanguage(1)">Pirate</button>
and finally, I would suggest using ng-repeat in your questionnaire
<div ng-bind="questionnaire.question"></div>
<label ng-repeat="answer in questionnaire.answers" ><input type="radio" name="{{questionnaire.question}}" ng-model="choice" ng-value="answer.val" />{{answer.text}} <br /></label>

unable to display radio button group in angular js dynamic form

what is the simplest way to display a radio button and dropdown list using templating in angular js? I have a plunker where i'd like to display the radio button group(quest#4) options as dynamic values - i am not sure how to do so. I am having the same issue with the selects(quest#5) as well. Any help is much appreciated.
http://run.plnkr.co/eHxbQn2lm1uTE2JI/
I couldn't see your attempt at using a <select> in your code, so here's an abstracted example, if you had the choices like so:
.controller('AppControl', ($scope)->
$scope.answers =
q1: null
q2: null
$scope.options = {
"option 1": "val1",
"option 2": "val2"
}
)
Then you could make a radio button list would look like this:
<span ng-repeat="(key, value) in options">
<input type="radio" ng-model="answers.q1" ng-value="value"/> {{key}}
</span>
And a select would look like this:
<select ng-model="answers.q2"
ng-options="value as key for (key, value) in options">
</select>
EDIT
To answer the second part of your question below: "how do I use ng-show in this case?":
To do this, you need to add the possibility of having a "show if" type condition in your questions. If there is a condition, then you only want to show the question if that condition is met. Your HTML for each question would change to look like this:
<div ng-repeat="question in questionnaire"
ng-include="'questionnaire'"
ng-show="(question.condition == null) || {{question.condition}}"></div>
Note that you check whether a condition has been specified, and if it hasn't, then the ng-show expression would evaluate to true. If it has, we interpolate that condition so that we can use the same syntax as we would for other ng-shows.
You then just need to adjust your question to include a condition, for example:
{
number: "5",
question: "Which of the following sweets do you like?",
type: "DD",
condition: 'questionnaire[$index-1].answer != "blue"',
values :[
{ name: "hardcandy" },
{ name: "chocolate" },
{ name: "other" },
]
}
questionnaire[$index-1] refers to the previous question ($index is from ng-repeat, check the docs), and .answer only works because I have used ng-model="question.answer" when I answered your previous query. I would personally add this ng-model attribute to all of your questions.
You can see it working here: Plunker

Resources