I'm making a website where you can make quizzes and answer them. My issue is that when I try to answer my quiz, to see whether the answer is correct or incorrect, the result I get is not exactly what I want.
I'm creating my html page by loading a json and assigning a radio button to every answer. The structure is that a quiz can have many questions, and questions can have many answers.
HTML:
<table class="pageTable" align="center">
<!-- Quiz title -->
<div *ngFor="let quiz of quizToDisplay"><br/>
Quiz title: {{quiz.title}} <br/> by {{quiz.owner}}<br/><br/>
<!-- Quiz questions -->
<div *ngFor="let quizQuestions of quiz.questions" align="center">
<div class="Question-panel-title" style="padding: 10px; word-wrap: break-word;">
Question: {{quizQuestions.questionText}}
</div>
<!-- Quiz answers -->
<div class="Question-panel-content" style="padding: 5px; word-wrap: break-word;">
<div *ngIf="quizQuestions.types == 'Multiple-choice'" >
<div *ngFor="let quizAnswers of quizQuestions.answers; let i=index">
<input type="radio" id="rawr" name="{{quizQuestions.questionText}}" value="{{quizAnswers.correctAnswer}}" [disabled]="submitted">
{{i + 1}}: {{quizAnswers.answerText}}
</div>
<div *ngIf="submitted == true">
Your answer is {{correctAnswerMultipleChoice}}
</div>
</div>
<div *ngIf="quizQuestions.types == 'Checkboxes'">
<div *ngFor="let quizAnswers of quizQuestions.answers; let i=index">
<input type="checkbox" name="checkboxGroup" value="{{quizAnswers.correctAnswer}}" [disabled]="submitted" (click)="handleCheckboxAnswer(i, quizAnswers.correctAnswer)">
{{i + 1}}: {{quizAnswers.answerText}}
</div>
<div *ngIf="submitted == true">
Your answer is {{correctAnswerCheckbox}}
</div>
</div>
</div><br/>
</div>
<input type="submit" value="Submit" (click)="submitAnswer()">
<input type="submit" value="View Statistics">
<div *ngIf="quiz.owner == currentUser">
<input type="submit" value="Delete Quiz" (click)="deleteQuiz(quiz.id)">
</div>
</div>
Code:
export class QuizComponent implements OnInit, OnDestroy {
service: any;
data: any;
quizToDisplay: any;
currentUser: string;
correctAnswerMultipleChoice: string = 'Incorrect';
CheckboxesValues: string[] = [];
correctAnswerCheckbox: string = 'Incorrect';
submitted: boolean = false;
answersArray: any[] = [];
constructor(private router:Router, private quizObserverService:QuizObserverService, private socketService:SocketService, private elementRef:ElementRef){
this.currentUser = localStorage.getItem('user');
}
ngOnInit() {
this.service = this.quizObserverService.getQuiz(this.router.url).subscribe(data => { //only gets JSON upon page load
this.quizToDisplay = data;
})
}
ngOnDestroy() {
this.service.unsubscribe();
}
deleteQuiz(id: string){
this.socketService.socket.emit('deleteQuiz', JSON.stringify(id));
this.router.navigateByUrl('/home');
}
handleMultiplechoiceAnswer(){
let rawr = this.elementRef.nativeElement.querySelectorAll('#rawr');
for(let i = 0; i < this.quizToDisplay[0].questions.length; i++){
if(this.quizToDisplay[0].questions[i].types == "Multiple-choice"){
for(let j = 0; j < this.quizToDisplay[0].questions[i].answers.length; j++){
this.answersArray.push(this.quizToDisplay[0].questions[i].answers[j]);
}
}
}
if(this.handleMultiplechoiceAnswerCorrect(rawr)){
return this.handleMultiplechoiceAnswerCorrect(rawr);
}
else
{
return this.handleMultiplechoiceAnswerIncorrect(rawr);
}
}
handleMultiplechoiceAnswerCorrect(rawr: any){
for(let k = 0; k < rawr.length; k++){
if(this.answersArray[k].correctAnswer == rawr[k].value && rawr[k].value == "Correct" && rawr[k].checked == true){
return "Correct";
}
}
}
handleMultiplechoiceAnswerIncorrect(rawr: any){
return "Incorrect";
}
handleCheckboxAnswer(index: number, correct: string) {
this.CheckboxesValues[index] = correct;
}
submitAnswer(){
this.submitted = true;
this.correctAnswerMultipleChoice = this.handleMultiplechoiceAnswer();
}
When I try to answer my quiz, this is what the result looks like:
Image
As you can see, even though the second question is answered wrong, it still says that it is correct, because the first question is answered correctly. The method I'm using to determine whether the quiz is correct or false is the handleMultiplechoiceAnswer() method, so I think something is wrong in that method, however I can't pick my finger on it. Thanks for the help.
EDIT:
Very sorry, I forgot to put an example of my json structure. Here it is:
{
"id": "32bec4d6-b5fd-4360-bede-9c902abd95de",
"title": "random quiz",
"owner": "mohemohe",
"questions": [
{
"questionText": "Choose numbers above 10",
"answers": [
{
"answerText": "9",
"correctAnswer": "Incorrect"
},
{
"answerText": "11",
"correctAnswer": "Correct"
}
],
"types": "Multiple-choice"
},
{
"questionText": "Which website is this?",
"answers": [
{
"answerText": "stackoverflow",
"correctAnswer": "Correct"
},
{
"answerText": "google",
"correctAnswer": "Incorrect"
}
],
"types": "Multiple-choice"
}
],
"access": "Public"
}
EDIT 2:
Managed to make a plunker example: https://plnkr.co/edit/CoV1AQtVtbbS2kNYK7FR
The solve:
I think that the reason why this is always returning true / 'Correct' is because of the if statement evaluating it:
if(this.answersArray[k].correctAnswer == rawr[k].value && rawr[k].value == "Correct" && rawr[k].checked == true){...}
If we break this down...
this.answersArray[k].correctAnswer == rawr[k].value
rawr[k].value == "Correct"
rawr[k].checked == true
Instantly you can see that you are expecting rawr[k].value to equal both correctAnswer AND 'Correct'. Because the result is always returning true, this means that this.answersArray[k].correctAnswer == 'Correct'.
So this essentially negates the first 2 conditions in your if statement - so essentially your if statement becomes... if(rawr[k].checked == true).
Just FYI there is absolutely no need for even checking rawr[k].checked == true if you swap querySelectorAll('#rawr'); to querySelectorAll('#rawr:checked');
The following is just general feedback:
I think that you really need to consider re-working this entire script. It is very hard to read, highly un-optomised and needs to be refactored into a more robust approach.
First I would recommend creating a variable to store the currentQuestion so that you don't have to do quizToDisplay[0] every single time.
var currentQuestion: any;
ngOnInit(){
....
this.quizToDisplay = data;
this.currentQuestion = this.quizToDisplay[0];
}
Or at the very least... this.quizToDisplay = data[0]
Go and check out es6 array methods.
for(let i = 0; i < this.quizToDisplay[0].questions.length; i++){
if(this.quizToDisplay[0].questions[i].types == "Multiple-choice"){
for(let j = 0; j < this.quizToDisplay[0].questions[i].answers.length; j++){
this.answersArray.push(this.quizToDisplay[0].questions[i].answers[j]);
}
}
}
Can be transmutted into the following which is much easier to read:
this.quizToDisplay[0].questions.forEach((questionGroup, i)=>{
if(questionGroup.types == "Multiple-choice"){
questionGroup.forEach((question, ii)=>{
this.answersArray.push(question.answers[ii]);
});
}
});
Why not just include the feedback string in the json for each question? That way you can do something like the following and mitigate any future requirements that the feedback string is unique for a particular question:
<div *ngIf="submitted == true">
<p *ngIf="correct" class="correct-colour">{{quizAnswers.correctFeedback}}</p>
<p *ngIf="!correct" class="incorrect-colour">{{quizAnswers.incorrectFeedback}}</p>
</div>
I think that the naming conventions need to be improved. for example: quizQuestions.answers should be quizQuestions.options.
Don't forget that as you are using an object, you have the ability to assign new object properties "on the fly". For example you could do:
this.quizToDisplay[0]['answeredCorrectly'] = true;
I would strongly recommend creating a click event on your <input> so that you can trap the selected options more effectively. Using the above methodology you could...
// .html
<input ... (click)="optionClicked(option)">
// .ts
optionClicked(_option: Object){
quizQuestions['clicked'] = !quizQuestions['clicked'] || true;
}
If you ever want future code review, try codereview.stackexchange.com
{
"id": "32bec4d6-b5fd-4360-bede-9c902abd95de",
"title": "random quiz",
"owner": "mohemohe",
"questions": [
{
"questionText": "Choose numbers above 10",
"answers": [
{
"answerText": "9",
"correctAnswer": "Incorrect"
},
{
"answerText": "11",
"correctAnswer": "Correct"
}
],
"types": "Multiple-choice"
},
{
"questionText": "Which website is this?",
"answers": [
{
"answerText": "stackoverflow",
"correctAnswer": "Correct"
},
{
"answerText": "google",
"correctAnswer": "Incorrect"
}
],
"types": "Multiple-choice"
}
],
"access": "Public"
}
Related
Edit: So, I found the solution to my initial question, which made me realize I have another issue.
It seemed to be easier than I thought
setNumbers(e) {
e.preventDefault();
var already_exists = false;
var ls_data = this.state.storedNumbers;
var rname = document.getElementById('name').value;
var rnumb = document.getElementById('nummer').value;
var ls_key = this.state.ls_key;
for (key in ls_data) {
if(ls_data.hasOwnProperty(key) === true) {
if(ls_data[key].name === rname) {
if(ls_data[key].Number === rnumb) {
already_exists = true;
}
}
}
}
if(!already_exists) {
ls_key++;
ls_data[ls_key] = {
name: rname,
Number: rnumb
};
localStorage.setItem("ls_numbers", JSON.stringify(this.state.storedNumbers));
localStorage.setItem("ls_key", ls_key);
this.setState({
ls_key: localStorage.getItem("ls_key"),
});
}
}
But now my issue is, that I can't iterate over it, because it is a nested object and not an array. So .map will not work (this.state.storedNumbers.map is not a function).
Changing storedNumber to an array sadly doesn't solve the issue, as
ls_data[ls_key] = {
name: rname,
Number: rnumb
};
isn't working in an array. So now my question is. Can I use my ls_key variable to create a name object in my array? Using the code above results in:
[
null,
{
"name" : "Person 1",
"Number" : "XXXXXXXX"
},
{
"name" : "Person 2",
"Number" : "XXXXXXXX"
}
]
while the array should look like:
[
"1": {
"name" : "Person 1",
"Number" : "XXXXXXXX"
},
"2": {
"name" : "Person 2",
"Number" : "XXXXXXXX"
}
]
Or is there a way to iterate over the nested JSON result, as .map does for an array?
Alright then, just figured it out myself:
The reason my data got malformed (initial question), still in the blue about that. I've changed a lot of code and reverted to the original code again, et voila, miraculously it all worked. Can't tell you what the difference was. After that I could easily simplify the code as shown in the edited question.
To iterate over the data, the code below was my solution. Should you have a more cleaner solution, let me know!
{this.state.storedNumbers.length < 1
? <li className='list-group-item'><strong>Geen recente nummers...</strong><span className='pull-right'><span className='glyphicon glyphicon-warning glyph-xl'></span></span></li>
: Object.keys(this.state.storedNumbers).map((number) => {
return (
<div className='item' key={number}>
<a className='list-group-item list-link'>
<strong>{this.state.storedNumbers[number].name}</strong> - ({this.state.storedNumbers[number].Number})
</a>
<span className='button-delete glyph-xl'>
<i className='glyphicon glyphicon-trash glyph-xl' aria-hidden='true'></i>
</span>
</div>
)})
}
currently I am working on app for managing workouts. I would like to sort workouts by date. My record of one workout looks like that:
"title": "Running through the park",
"type": "Running",
"distance": "10",
"duration": [
{
"hours": "2"
},
{
"minutes": "40"
},
{
"seconds": "44"
}
],
"note": "Running in NY park."
I would like to sort workouts by the day. For example:
09.10
running
basketball
07.10
push-ups
football
Currently it looks like that: workouts-main screen
My html file for that component is simple :
<ion-list>
<ion-item *ngFor="let workout of workouts" (click)="workoutSelected($event,workout)">
{{workout.title}}
</ion-item>
What's the best way to do that?
As I stumbled recently on similar issue and couldn't find fully satisfactory answer I would like to share my solution.
.ts
objectEntries: any[];
groupedObjectEntries: any[];
ionViewDidLoad() {
// put in the line below your custom function for getting data into objectEntries object
.....
this.groupObjectEntries(this.objectEntries);
}
groupObjectEntries(objectEntries){
var currentDate: any;
var currentObjectEntries = [];
var output = [];
// sort descending
let sortedObjectEntries =objectEntries.sort(function (a, b) {
var key1 = new Date(a.date);
var key2 = new Date(b.date);
if (key1 < key2) {
return 1;
} else if (key1 == key2) {
return 0;
} else {
return -1;
}
});
sortedObjectEntries.forEach((item, index) => {
// conversion from JSON format
var itemDate = new (item.date);
itemDate.setHours(0,0,0,0);
if(itemDate != currentDate){
currentDate = itemDate;
let newGroup = {
groupDate: currentDate,
objectEntries: []
};
currentObjectEntries = newGroup.objectEntries;
output.push(newGroup);
}
currentObjectEntries.push(item);
});
this.groupedObjectEntries = output;
}
.html
<ion-content>
<ion-item-group *ngFor="let group of groupedObjectEntries">
<ion-item-divider sticky>
<ion-item color="gray" ><h4>{{ group.groupDate | date:'fullDate' }} </h4> </ion-item>
</ion-item-divider>
<button ion-item *ngFor="let item of group.objectEntries" (click)="itemSelected(item)">
<h3>{{ item.field1 }} </h3>
<p>{{ item.field2 }}</p>
</button>
</ion-item-group>
</ion-content>
I have a select that looks like this
<select
class="form-control"
ng-model="vm.transaction.location_from"
ng-options="l.name for l in vm.locations">
</select>
with vm.locations sourcing from the following JSON:
[
{
"id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e",
"name": "Location 1"
},
{
"id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2",
"name": "Location 2"
}
]
Further, I have another select that looks like:
<select
class="form-control"
ng-model="vm.transaction.item"
ng-options="i.name for i in vm.items">
</select>
with vm.items sourcing from the following JSON:
[
{
"id": "9f582e58-45dd-4341-97a6-82fe637d769e",
"name": "20oz Soft Drink Cup",
"locations": [
{
"inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
"location_id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e"
},
{
"inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
"location_id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2"
}
],
}
]
I want to, on change of the ng-mode="vm.transaction.item" select, have the ng-model="vm.transaction.location_from" be filtered to only show values that match from the locations array. I know I can use a | filter: { }, but I'm not sure what that filter should look like.
Hope this is your expected results.
Below are two options I tried ... demo | http://embed.plnkr.co/689OQztgu8F800YjBB2L/
Ref : underscorejs | angular-filter | everything-about-custom-filters-in-angular-js
// 1. filter items collection by location
angular.module('demo').filter('withLocation', function () {
return function (items, selectedLocation) {
function isLocationInLocations (elem) { return selectedLocation && elem.location_id === selectedLocation.id; }
function itemHasLocation (elm){ return (elm.locations && elm.locations.filter(isLocationInLocations).length > 0); }
return items.filter(itemHasLocation);
}});
// 2. filter function to check if option can be rendered ....
vm._filters.selectableItems = function(selectedLocation) {
return function(item) {
var locationsHasLocation = function(elem) { return selectedLocation && elem.location_id === selectedLocation.id; }
return (item.locations && item.locations.filter(locationsHasLocation).length > 0);
}
}
var app = angular.module("Test", []);
app.controller("Ctrl1", function($scope) {
$scope.location_fromArr =
[{
"id": "9f582e58-45dd-4341-97a6-82fe637d769e",
"name": "20oz Soft Drink Cup",
"locations": [{
"inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
"location_id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e"
},{
"inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
"location_id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2"
}],
}];
$scope.itemArr =
[{
"id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e",
"name": "Location 1"
},{
"id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2",
"name": "Location 2"
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="Test" ng-controller="Ctrl1">
Item
<select
class="form-control"
ng-model="item"
ng-options="i.name for i in itemArr">
</select>
Location
<select
class="form-control"
ng-model="location_from"
ng-options="l.name for l in location_fromArr | filter:{l.id: location_from.location_id}">
</select>
</div>
One way to do this is to supply a filter function to filter the locations. Something like:
vm.filterFun = function(selectedLocations) {
return function (location) {
var n;
if (!selectedLocations) {
return true;
}
for(n=0;n<selectedLocations.length;n += 1) {
if (selectedLocations[n].location_id === location.id) {
return true;
}
}
return false;
}
}
This is actually a function returning a filter function, based on the item selected.
Then in your select you apply the filter with:
<select
class="form-control"
ng-model="vm.transaction.location_from"
ng-options="l as l.name for l in vm.locations | filter:vm.filterFun(vm.transaction.item.locations)">
</select>
See plunker here.
I would forego angular filters and use the getterSetter option of ngModelOptions.
It could look something like this:
var selectedItem, selectedLocation;
var items = [];
var locations = [];
vm._items = items; // Static, always allow all items to be selected.
vm.locations = function () {
// Return differing results based on selectedItem.locations.
};
vm._transaction = {
location: function (v) {
/**
* If v is null, it is not present in the selectedItem.locations array.
* The extra check will ensure that we don't persist a filtered out location when
* selecting another item.
*/
return (v || v === null) ? (selectedLocation = v) : selectedLocation;
},
item: function (v) {
return v ? (selectedItem = v) : selectedItem;
}
};
Here's a plunker demonstrating the behaviour.
Not as simple/straight-forward as a filter, but I would bet (at least in the case of a piped filter) that you'd possibly see a slight performance gain going with this approach.
I do not have numbers to back up the above statement, and it usually boils down to the size of your dataset anyway. Grain of salt.
If you need it to function the other way around, you could write up a secondary filter like such:
function superFilter2 (arr) {
// If no location is selected, we can safely return the entire set.
if (!selectedLocation) {
return arr;
}
// Grab the current location ID.
var id = selectedLocation.id;
// Return the items that are present in the selected location.
return arr.filter(function (item) {
return item.locations.map(function (l) {
return l.location_id;
}).indexOf(id);
});
}
With that and the filter in the supplied plunker, there are some similarities that could be moved into higher order functions. Eventually with some functional sauce you could probably end up with a single god function that would work both ways.
you can do this:
<select
class="form-control"
ng-model="vm.transaction.item"
ng-change="itemCahngedFn()"
ng-options="i.name for i in vm.items">
</select>
var itemChangedFn = function(){
var filtredItems = [];
angular.forEach(vm.locations, function(item){
if(item.name == vm.transaction.item){
filtredItems .push(item.location);
}
});
vm.locations= filtredItems ;
}
i think filter:{ id : item.locations[0].location_id } should do the trick.
here is the jsfiddle
how do you think?
In the view there are three checkboxes that change the states of the three values of $scope.colorChoice.
I would like to write a function that compares every true color to the corresponding color in the JSON list.
If a person has at least one color in its array that has been checked true,
the persons name should be displayed.
How can i write such a function?
So far I've come so far:
JSON list:
[
{
"name": "kevin",
"colors": ["red, green"]
},
{
"name": "hans",
"colors": ["green"]
},
{
"name": "jolene",
"colors": ["red, blue"]
},
{
"name": "peter",
"colors": ["blue"]
}
]
Checkboxes:
<label ng-repeat="(item,enabled) in colorChoice">
<input type="checkbox" ng-model="colorChoice[item]">
</label>
Controller:
$scope.colorChoice = {
red: false,
green: false,
blue: false
};
For example:
$scope.colorChoice = {
red: true,
green: false,
blue: true
};
...would display:
Kevin, Jolene, Peter
Thanks for your help!
Vin
One thing you might want to look into is the angular-checklist-model,
http://vitalets.github.io/checklist-model/
That won't solve your problem as I see you are already handling what it would handle for you. I find it very clean to use for a purpose like yours though.
With that colorChoice object you could do something like this whether you use angular-checklist-model or not though:
HTML
<ul>
<li ng-repeat='person in people | filter: colorFilter'>{{person.name}}</li>
</ul>
Controller Filter Function
$scope.colorFilter = function(person) {
for (var i = 0; i < person.colors.length; i++) {
var color = person.colors[i];
if ($scope.colorChoice[color] == true)
return true;
}
return false;
};
I like to use the angular filters like so with functions that return true or false. They can be extremely versatile for situations like this.
angular filter guide
Thanks Kyle - The checklist-model looks very interesting.
I've come up with the following solution now:
First a little helper function to filter out all activated checkboxes:
$scope.colorChoiceTrue = function () {
var result = [];
for (var key in $scope.colorChoice) {
if ($scope.colorChoice[key] === true) {
result.push(key);
};
};
return result;
}
Another helper function to search a string in an array:
$scope.searchStringInArray = function (str, strArray) {
for (var j = 0; j < strArray.length; j++) {
if (strArray[j].match(str)) return true;
}
return false;
}
Finally, this function returns every person who has at least one color matching the colorChoice:
$scope.peopleSelected = function () {
var result = [];
angular.forEach($scope.people, function (entry, key) {
if ($scope.searchStringInArray(entry.color, $scope.colorChoiceTrue())) {
result.push(entry.name);
};
});
return result;
};
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I plan to have two sets of radio options on my form, one radio called Operating System and another radio called Database.
The option selected by the Operating System radio dictates the values available for selection in the Database radio group.
In my json object the requires field indicates the visibility of the option when the sku of the Operating System is selected. If no requires field is provided for a database option, then it will always be available regardless of the selected operating system.
How would one approach this in knockout, or do I need to rethink my approach?
My jsfiddle is here
var osOptions = [{
name: "Windows Standard",
sku: "201",
},{
name: "Windows Enterprise",
sku: "202",
}, {
name: "CentOS Linux",
sku: "203",
}, {
name: "Debian",
sku: "204",
}];
var databaseOptions = [{
name: None,
}, {
name: "SQL Express",
sku: 401,
requires: ["201", "202"]
}, {
name: "SQL Standard",
sku: 402,
requires: ["202"]
}, {
name: "MySQL",
sku: "MySQL1",
requires: ["201", "202", "203"]
}, {
name: "RavenDb",
sku: 403,
}, {
name: "MongoDB",
sku: 404,
requires: ["204"]
}];
function viewModel() {
this.os = osOptions;
this.database = databaseOptions;
this.selectedOs = ko.observable();
this.selectedDb = ko.observable();
}
ko.applyBindings(new viewModel);
<!- view html -->
<h1>Select OS:</h1>
<div data-bind="foreach: os" >
<div>
<input type="radio" name="optionsGroup" data-bind="attr: {value: name}, checked: $root.selectedOs" />
<span data-bind="text: name"></span>
</div>
</div>
<h1>Select Db</h1>
<div data-bind="foreach: database" >
<div>
<input type="radio" name="optionsGroup" data-bind="attr: {value: name}, checked: $root.selectedDb" />
<span data-bind="text: name"></span>
</div>
</div>
I would create a different computed collection availableDatabases where
first I would look up the currently selected OS
then I would use the ko.utils.arrayFilter to filter out the databases where the the requires array does not contain the selected sku.
So I would write something like this:
this.availableDatabases = ko.computed(function() {
var selectedOsName = this.selectedOs();
var selectedOs = ko.utils.arrayFirst(this.os, function(os){
return os.name == selectedOsName;
}, this);
if (!selectedOs)
return [];
return ko.utils.arrayFilter(this.database, function(db){
return db.requires && db.requires.indexOf(selectedOs.sku) > -1;
}, this)
}, this);
And use this new collection in the view:
<div data-bind="foreach: availableDatabases" >
<div>
<input type="radio" name="optionsGroup"
data-bind="attr: {value: name}, checked: $root.selectedDb" />
<span data-bind="text: name"></span>
</div>
</div>
Demo JSFiddle.
Note If you have the sku instead of the name as the value for you first radio buttons:
<input type="radio" name="optionsGroup"
data-bind="attr: {value: sku}, checked: $root.selectedOs" />
Then there is no lookup needed in the computed because selectedOs would contain the sku property directly (Demo)...
Take a look at this fiddle
You can create an computed that retrieves the available databases.
JS :
function viewModel() {
var self = this;
this.os = osOptions;
this.database = databaseOptions;
this.selectedOs = ko.observable();
this.selectedDb = ko.observable();
this.availableDatabase = ko.computed(function () {
var osSku = self.selectedOs();
return ko.utils.arrayFilter(self.database, function (dbItem) {
if (osSku == null) return false;
if (dbItem.requires == null) return true;
var dbs = ko.utils.arrayFirst(dbItem.requires, function (requiredOS) {
return requiredOS == osSku;
}) != null;
return dbs;
});
});
};
ko.applyBindings(new viewModel);
I hope it helps.