I am using Knockout to handle interactivity in a web app. I am trying to create a multiple-choice quiz widget. I want to present a question, and then a group of radio buttons which the user can pick from. I do not want a default answer.
I have the following in my code:
<!-- ko foreach: {data: answers, as: 'answer'} -->
<li class="list-group-item">
<input type="radio"
name="answers"
data-bind="checkedValue: answer, checked: $parent.current.problem.response">
<span data-bind="mathjax: answer.value.text">1.5</span>
</li>
<!-- /ko -->
Now, the Knockout documentation says:
When the user changes which radio button is selected, KO will set your model property to equal the value of the selected radio button. In the preceding example, clicking on the radio button with value="cherry" would set viewModel.spamFlavor to be "cherry".
So, based on the documentation, it is my understanding that the current.problem.response observable should be getting set to an answer object. But instead, it is getting set to on.
I think this is because my current.problem.response observable is initially null. This makes sense for my domain. It should be empty if the user hasn't made a selection.
What should I do?
Looks like your scenario works. Maybe the problem is out of the scene.
This fiddle demonstrates it works http://jsfiddle.net/tabalinas/y3LpD/
Open console to see selected option.
Demo view model looks like
var vm = {
answers: [
{ text: 'ans1' },
{ text: 'ans2' },
{ text: 'ans3' },
{ text: 'ans4' }
],
response: ko.observable(null)
};
vm.answer = vm.answers[0];
vm.response.subscribe(function(value) {
console.log(value);
});
The markup a little corrected according to the model
<!-- ko foreach: {data: answers, as: 'answer'} -->
<li class="list-group-item">
<input type="radio"
name="answers"
data-bind="checkedValue: answer, checked: $parent.response" />
<span data-bind="text: answer.text"></span>
</li>
<!-- /ko -->
Hope this can help. If you have any further problems, specify them in comments.
Related
Looking for your kind assistance as I am still new to coding and angular.
I have this form which allows to me do CRUD data.
In adding the data, I have a several checkbox which I can manage to successfully stored in the database.
However, when I am trying to edit the data, the checkbox are no longer showing check markings based on the data in the table.
I have a Modal form and I am having a hard time matching the data in the checkbox to the ones in database.
Component.html
<div>
<div *ngFor="let item of _placarddetails">
<input type="checkbox" name="{{item.id}}" [(ngModel)]="item.isselected">
<label> {{item.name}}</label>
</div>
</div>
Component.TS
ngOnInit(): void {
this.loadPlacardQualityList();
}
loadPlacardQualityList(){
this.service.getAllEdcmTicketNo().subscribe((data:any)=>{
this.PlacardQualityList=data;
this.PlacardQualityId=this.pq.PlacardQualityId;
this.EdcmTicketNo=this.pq.EdcmTicketNo;
this.PQDeliveryDate=this.pq.PQDeliveryDate;
this.PlacardAppearance=this.pq.PlacardAppearance;
this.PlacardDetails=this.pq.PlacardDetails ;
this.PlacardAcceptance=this.pq.PlacardAcceptance;
this.Inserts=this.pq.Inserts;
this.CheckedBy=this.pq.CheckedBy;
this.PowerProduct=this.pq.PowerProduct;
this.Comment=this.pq.Comment;});
this.
}
addPlacardQuality()
{
var val = {
PlacardQualityId:this.PlacardQualityId,
EdcmTicketNo:this.EdcmTicketNo,
PQDeliveryDate:this.PQDeliveryDate,
PlacardAppearance:this.PlacardAppearance,
PlacardDetails:this.PlacardDetails = this._placarddetails.filter(x=>x.isselected==true).map(x=>x.name).join(","),
PlacardDetailsID:this.PlacardDetails = this._placarddetails.filter(x=>x.isselected==true).map(x=>x.name).join(","),
Inserts:this.Inserts,
CheckedBy:this.CheckedBy,
PowerProduct:this.PowerProduct,
Comment:this.Comment};
this.service.addPlacardQuality(val).subscribe(res=>{alert(res.toString());
});
}
updatePlacardQuality(){
var val = {
PlacardQualityId:this.PlacardQualityId,
EdcmTicketNo:this.EdcmTicketNo,
PQDeliveryDate:this.PQDeliveryDate,
PlacardAppearance:this.PlacardAppearance,
PlacardDetails:this.PlacardDetails = this._placarddetails.filter(x=>x.isselected==true).map(x=>x.name).join(","),
Inserts:this.Inserts,
CheckedBy:this.CheckedBy,
PowerProduct:this.PowerProduct,
Comment:this.Comment}
this.service.updatePlacardQuality(val).subscribe(res=>{alert(res.toString());});
}
getPlacardDetails()
{
this._placarddetails=[
{id:1,name:"Company Name",isselected:false},
{id:2,name:"Company Logo",isselected:false},
{id:3,name:"Certification Level",isselected:false},
{id:4,name:"Year",isselected:false},
{id:5,name:"Badge Name",isselected:false},
]
}
class PDetail{
id!: number;
name!: string;
isselected!: boolean;
}
Here is the sample screenshot whenever I open the edit button.
sample screenshot
I understand that the reason why it is not showing a checkmarks is because of the getPlacardDetails() which is showing false value.
Is there a way that you can recommend a method how I can fix this?
I'm running out of resources and logic lol.
Sorry, still in-experience and I still have lots to learn.
Thank you in advance!
Use "checked" attribute of the checkbox to make it checked. Code as below:
<input type="checkbox" name="{{item.id}}" [(ngModel)]="item.isselected" [checked]="item.isselected">
Also for more details you can refer to below link:
Angular 5, HTML, boolean on checkbox is checked
I am a beginner to Vue, and learning something by doing. I was able to make a checkbox reusable, but getting some weird result for radio buttons.
I have the data in an array format in ProgramDesign.vue:
data() {
return {
strategies: [
"Not Important",
"Slightly Important",
"Moderately Important",
"Very Important",
"Extremely Important",
],
};
},
These are the options that get repeated on every question.
I made a separate component for the radio like this:
<template>
<div>
<span v-for="strategy in groups" :key="strategy">
<input :id="strategy" class="radio-style" name="strategy" type="radio" />
<label :for="strategy" class="radio-style-3-label">{{strategy}}</label>
</span>
</div>
</template>
<script>
export default {
props: {
groups: Array,
},
};
</script>
This is how it's used in ProgramDesign.vue:
<p>first question goes here ?</p>
<RadioButton :groups="strategies" />
<div class="line"></div>
<p>second question goes here ?</p>
<RadioButton :groups="strategies" />
I was able to get the reusable output, but when I click on the radio button for the second question, the buttons for the first question get selected. How can I fix this?
The problem is the input IDs and names are not unique between component instances, as can be seen in the rendering of your two RadioButton components (simplified for brevity):
<!-- RadioButton 1 -->
<div>
<span>
<input id="Not Important" name="strategy" type="radio">
<label for="Not Important">Not Important</label>
</span>
</div>
<!-- RadioButton 2 -->
<div>
<span>
<input id="Not Important"❌ name="strategy"❌ type="radio">
<label for="Not Important">Not Important</label>
</span>
</div>
Each label is linked to an input by matching the for and id attributes, such that clicking the label causes the linked radio input to change values. When there are multiple inputs with the same identifier, the browser links the label to the first matching input, causing the behavior you observed.
The name must also be unique between groups (RadioButton instances), since the browser creates radio groups of inputs that have matching names.
Solution
Alternatively, a label and input can be linked by putting the input inside the label, resolving the id/for duplication (and improving readability):
<label>
<input name="strategy" type="radio">
Not Important
</label>
And one way to resolve the duplicate names is to base the name on a counter incremented per instance:
<template>
<div>
<label v-for="strategy in groups" :key="strategy">
<input :name="'strategy' + groupId" type="radio">
{{strategy}}
</label>
</div>
</template>
<script>
let groupId = 0
export default {
props: {
groups: Array
},
data() {
return {
groupId: groupId++
}
}
}
</script>
I am new to Angular and typescript. I need help from someone in the following requirement.I have a web page where I have a list of check boxes as given here:
I have defined a isChecked boolean variable in component.ts file and has a data binding with the template using ngModel. A function onChange() is called when the checkbox is checked or un-checked. I want to pass two parameters to the function. One is check/uncheck value i.e like true/false and another is the string value containing the label of the checkbox clicked. For example if st1 is checked then onChange(check, 'st1') will be invoked. The logic inside component class will add 'st1' to an array if it is unchecked.
In the template I have written like this:
<ul>
<li [(ngModel)]="student" *ngFor="let student of studentsToLOad" [value]="student">
<b>{{student.name}}</b>
<input type="checkbox" id="present" name="present" [(ngModel)]="isChecked"
(change)="onChange(isChecked, student)"/>
</li>
</ul>
But when I am clicking on st1, all the other check boxes are also getting checked. Please help.
The ngModel value is set to a single variable isChecked. This variable is passed to all checkboxes. All the checkboxes will reflect their state based on isChecked. You could use some thing like student.checked. You could set the value in the onChange fucntion.
onChange(isChecked, student) {
student.isChecked = isChecked;
}
<ul>
<li [(ngModel)]="student" *ngFor="let student of studentsToLOad" [value]="student">
<b>{{student.name}}</b>
<input type="checkbox" id="present" name="present" [(ngModel)]="student.isChecked" (change)="onChange(isChecked, student)" />
</li>
</ul>
The error occurs because all the checkboxes are bound to a single global boolean flag isChecked.
You could remove the ngModel and value from the li element and bind the ngModel in input element to student specific booleans instead of a global boolean flag. Try the following
Controller
export class AppComponent {
studentsToLOad = [
{ name: 'st1', status: false },
{ name: 'st2', status: false },
{ name: 'st3', status: false },
{ name: 'st4', status: false },
];
onChange(status, name) {
console.log(name, status);
}
}
Template
<ul>
<li *ngFor="let student of studentsToLOad">
<b>{{student.name}}</b>
<input type="checkbox" id="present" name="present" [(ngModel)]="student.status"
(change)="onChange(student.status, student.name)"/>
</li>
</ul>
I have a array of json, something similar to this:
questions =
[
{
title: Your section is?
choices: [
{
id: 1,
label: "TestA"
},
{
id=2,
label: "TestB"
}
]
},
{
title: Your major is?
choices: [
{
id=3,
label: "Medicine"
},
{
id=4,
label: "Engineering"
}
]
}
]
My html:
<div *ngFor="let choice of questions">
<div *ngFor="let choice of questions.choices;let i=index">
<div class="radio-group" (click)="onSelectChoice(choice.id)">
<input type="radio" class="hide" name="choiceElements" [value]="choice.id" id="choice.label" formControlName="choiceElements">
<label [class.selected]="choice.id === selctedRadio" for="{{choice.label}}" class="" >{{choice.label}}</label>
</div>
</div>
</div>
My TS:
selctedRadio: '';
onSelectChoice(selctedChoice: any){
this.selctedRadio = selctedChoice;
}
So, in html I need two questions, each question having two buttons, for each question I should be able to select one radio button.
But in my case, I am able to select only one radio button and when I go to second question the selected first question becomes unselected.
Let me know how I can be able to select one radio answer from each question.
Your radio buttons must have the same name if and only if they belong to the same group.
You will have to find a way to create a unique-per-group name.
By the way, this is not angular specific, it's just how radio buttons work in plain HTML.
however, for more info about how to handle radio buttons in angular, you can check this :
Angular2 ReactiveFormsControl: how to bind radio buttons?
Angular2 - Radio Button Binding
Given this code:
<div ng-controller="MyCtrl">
<form ng-submit="onSubmitted()">
Header inputs:
<input type="name" ng-model="sample" required/>
<input type="name" ng-model="sampleX" required/>
<input type="submit" value="This submit triggers validation. But I wanted to put this button at the end of the page"/>
</form>
<hr/>
Some other form here. Think line items
<hr />
<a class="btn" ng-click="/* what could should be put here, so this can trigger the firt form's validation, then submit? */">Wanted this submit button to trigger the validation+submit on the form in which this button doesn't belong</a>
</div>
var app = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.onSubmitted = function() {
alert('submitted!');
};
}
I want the last button to trigger the validation(then submit when things are valid) on first form. As of now, only the button inside the form can trigger that form's validation and submission. Is there any possible way for a button outside the form to do that?
Live test: http://jsfiddle.net/dzjV4/1/
You can create directive which you can then attach to <a class="btn".... Check this jsfiddle
http://jsfiddle.net/dzjV4/2/
Note that I added to <input type='submit' id='clickMe'... and linked it with link at the bottom <a class='btn' linked="clickMe"...
for (control of $scope.[form name].$$controls) {
control.$setDirty();
control.$validate();
}
You can try the above codes. Make it running before submit.
Ideally there'd be a programmatic way to cause validation to re-run across a form. I have not investigated that completely but had a situation that required multiple controls to be re-validated based on different data in the scope -- without the user interacting with the individual controls. This arose because the form had two action buttons which each required different validation rules be in play when they were clicked.
The UI requirement changed before I fully implemented forcing re-validation but before it did I got most of what I needed by copying and then re-setting the form's data. This forced re-validation across the form within the current scope. Basically, it's along the lines of the following (not tested, but taken from the code that was working). In this case the form's data was bound to the properties in one object.
var formData = $parse(<form's model>);
var dataCopy = angular.copy( formData($scope) );
formData.assign( $scope, dataCopy );
This may or may not be acceptable, but if you can get away with the SUBMIT button being disabled until the form is completed, you can do this:
<form name="formName">
<input ng-required="true" />
</form>
<button ng-click="someFunction()" ng-disabled="formName.$invalid" />
It's also worth noting that this works in IE9 (if you're worried about that).
Give your form a name:
<div ng-controller="MyCtrl">
<form name="myForm">
<input name="myInput" />
</form>
</div>
So you can access your form validation status on your scope.
app.controller('MyCtrl', function($scope) {
$scope.myForm.$valid // form valid or not
$scope.myForm.myInput // input valid or not
// do something with myForm, e.g. display a message manually
})
angular doc
There is no way to trigger browser form behavior outside of a form. You have to do this manually.
Since my form fields only show validation messages if a field is invalid, and has been touched by the user:
<!-- form field -->
<div class="form-group" ng-class="{ 'has-error': rfi.rfiForm.stepTwo.Parent_Suffix__c.$touched && rfi.rfiForm.stepTwo.Parent_Suffix__c.$invalid }">
<!-- field label -->
<label class="control-label">Suffix</label>
<!-- end field label -->
<!-- field input -->
<select name="Parent_Suffix__c" class="form-control"
ng-options="item.value as item.label for item in rfi.contact.Parent_Suffixes"
ng-model="rfi.contact.Parent_Suffix__c" />
<!-- end field input -->
<!-- field help -->
<span class="help-block" ng-messages="rfi.rfiForm.stepTwo.Parent_Suffix__c.$error" ng-show="rfi.rfiForm.stepTwo.Parent_Suffix__c.$touched">
<span ng-message="required">this field is required</span>
</span>
<!-- end field help -->
</div>
<!-- end form field -->
I was able to use this code triggered by a button to show my invalid fields:
// Show/trigger any validation errors for this step
angular.forEach(vm.rfiForm.stepTwo.$error, function(error) {
angular.forEach(error, function(field) {
field.$setTouched();
});
});
// Prevent user from going to next step if current step is invalid
if (!vm.rfiForm.stepTwo.$valid) {
isValid = false;
}