Angular 2 - Handling multiple checkboxes checked state - forms

I'm building a registration form, and have to handle multiple checkboxes generated through *ngFor structural directive.
Template:
<div class="form-group">
<label class="col-lg-3 control-label">Response Type</label>
<div class="col-lg-7">
<div class="checkbox" *ngFor="let type of client.responseTypes; let i = index">
<label>
<input type="checkbox" name="responseTypes" [(ngModel)]="client.responseTypes[i].checked" /> {{type.value}}
</label>
</div>
</div>
</div>
TS model object:
client.responseTypes = [
{ value: 'type1', checked: false},
{ value: 'type2', checked: true },
{ value: 'type3', checked: true }
];
The actual values behind the scene checked or unchecked are stored correctly. But, the checkboxes do not show the correct value.
If one of the three objects.checked == false, all checkboxes will show unchecked
Only if all three objects.checked == true, all three will be checked in the form.
I tried fiddling with [value]="client.responseTypes[i].checked" and [checked]="client.responseTypes[i].checked" on the input element, but to no success.
Any pointers in the right direction are massively appreciated.
Regards, Chris

the syntax is ok, well, you can do too
<input type="checkbox" name="responseTypes" [(ngModel)]="type.checked">
the problem is in other place (when you defined the client -I think you write some like this.client.responseTypes=...-), you can write
{{client |json }}
to check it

Alright after trying to wrap my head around why my local application wasn't showing the result I wanted, I started to copy and paste my form-group out of the <form></form> element it was in.
Ta-daa, it works now.
FYI: Placing *ngFor checkboxes inside a <form></form> element results in unwanted behaviour.

In angular every model bind with name , means you need to give unique name to each Form elements`
For Example:
<div class="form-group"><label class="col-lg-3 control-label">Response Type</label>
<div class="col-lg-7">
<div class="checkbox" *ngFor="let type of client.responseTypes; let i = index">
<label>
<input type="checkbox" name="type.id" [(ngModel)]="client.responseTypes[i].checked" /> {{type.value}}
</label>
</div>
</div>
</div>
Object :
client.responseTypes = [{id: '1', value: 'type1', checked: false},
{id: '2', value: 'type2', checked: true },
{id: '3', value: 'type3', checked: true }];
And now Check :) Thank you

Related

Data binding checkboxes in svelte

in a svelte/sapper form I want to send data to a user model
<input type="text" bind:value={user.label}>
..works as expected. But how to send user.offer_ids
let offer_ids = [];
{#each offers as offer}
<label>
<input type=checkbox bind:group={offer_ids} value="{offer.id}">{offer.label}
</label>
{/each}
I can't find any svelte sample how to do this job, which I think is basic in forms.. Thanks for help
May be it was unclear..I want to send data to Json:
<form>
<input type="text" bind:value={user.label}> //gives data to 'user'
{#each offers as offer}
<label>
<input type=checkbox bind:group={offer_ids} value="{offer.id}">{offer.label}
</label>
{/each}
//how to get all selected ids to 'user'
</form>
Hope this is more clear now.
With checkboxes, we bind to checked instead of value:
<script>
let name = 'world';
let flag = false
</script>
<style>
.highlighted {
color: red
}
</style>
<h1 class:highlighted={flag}>Hello {name}!</h1>
<label for="checkbox">Highlight header</label>
<input id="checkbox" type='checkbox' bind:checked={flag}/>
Edit
The offer model should have a selected or checked state. Then you can bind the checkbox to that:
<script>
let name = 'world';
let offers = [
{id: 1, name: 'One', checked: false},
{id: 2, name: 'Two', checked: false},
{id: 3, name: 'Three', checked: false},
]
</script>
{#each offers as offer, index (offer.id)}
<label for={'checkbox'+offer.id}>{offer.name}</label>
<input id={'checkbox'+offer.id} bind:checked={offer.checked} type='checkbox'/>
{/each}
<div>
{offers.filter(offer => offer.checked).map(offer => offer.name)}
</div>
Here, we display three checkboxes and the text below prints the names of the selected offers on the screen.
REPL: https://svelte.dev/repl/461fea432dc54ed0a744afc4e05bf34b?version=3.22.3
so my solution now:
let offer_ids = [];
<form>
{#each offers as offer, i (offer.id)}
<label>
<input type=checkbox bind:group={offer_ids} value="{offer.id}">{offer.label}
</label><br/>
{/each}
</form>
enter code here
and in the function...
async function publish() {
user.offer_ids = offer_ids
const response = await (api.post('users', {user}));

Why checkboxes don't work inside a form?

I have an objects array
pets = [{key: 'dog', isChecked: true}, {key: 'hamster', isChecked: false}, {key: 'cat', isChecked: false}];
and display it like checkboxes
<div *ngFor='let pet of pets'>
<input type='checkbox'
name='pets'
value='{{pet}}'
[(ngModel)]='pet.isChecked'
(change)='check()'
/>
{{pet.key}} - {{pet.isChecked}}
</div>
but as soon as I start putting it inside a form
<form>
<div *ngFor='let pet of pets'>
<input type='checkbox'
name='pets'
value='{{pet}}'
[(ngModel)]='pet.isChecked'
(change)='check()'
/>
{{pet.key}} - {{pet.isChecked}}
</div>
</form>
It stops displaying correctly.
How can I make this work with a form?
Plunkr link
and
Plunkr link with a form
HTML :
<form>
<div *ngFor='let pet of pets'>
<input type='checkbox'
name='pets'
id="pets{{getRandom()}}"
[ngModel]="pet.isChecked"
value='{{pet}}'
(click)="$event.stopPropagation(); check();"/>
{{pet.key}} - {{pet.isChecked}}
</div>
</form>
TS :
function getRandom() {
return Math.random() * 1000;
}

Extending Angular 2 dynamic forms to hide fields for certain values if conditions are met

I was following the example given on Dynamic Forms from the Angular 2 documentation (https://angular.io/docs/ts/latest/cookbook/dynamic-form.html). In this example you are shown how to create forms dynamically, based on metadata that describes the business object model.
This roughly looks as follows:
I have a YesNoQuestion class that describes how my questions are formatted.
import { QuestionBase } from './question-base';
export class YesNoQuestion extends QuestionBase<string> {
controlType = 'radio';
options: {key: string, value: string}[] = [];
constructor(options: {} = {}) {
super(options);
this.options = [
{key: 'true', value: 'Yes'},
{key: 'false', value: 'No'}
];
}
}
Each form is generated in my dynamic-form.component:
<div>
<form (ngSubmit)="onSubmit()" [formGroup]="form">
<div *ngFor="let question of questions" class="form-group">
<df-question [question]="question" [form]="form"></df-question>
</div>
<div class="form-row">
<button type="submit" [disabled]="!form.valid">Save</button>
</div>
</form>
<div *ngIf="payLoad" class="form-row">
<strong>Saved the following values</strong><br>{{payLoad}}
</div>
</div>
The dynamic-form calls the dynamic-form-question for each single question dynamic-form-question.component:
<div [formGroup]="form">
<label [attr.for]="question.key">{{question.label}}</label>
<div [ngSwitch]="question.controlType">
<div class="form-group" *ngSwitchCase="'radio'">
<div *ngFor="let opt of question.options">
<input type="radio" [formControlName]="question.key" [value]="opt.key" /> {{opt.value}}
</div>
</div>
... Some extra form types ...
</div>
<div class="errorMessage" *ngIf="!isValid">{{question.label}} is required</div>
</div>
Now I can generate a form by simply inserting a questions object like the following:
let questions: QuestionBase<any>[] = [
new YesNoQuestion({
key: 'a',
label: 'Label A',
}),
new YesNoQuestion({
key: 'b',
label: 'Label B',
}),
new YesNoQuestion({
key: 'c',
label: 'Label C',
}),
new TextareaQuestion({
key: 'd',
label: 'Label D',
}),
Now I am looking for a way to let these question be dependent of each other. I for example only want to see question A, and if yes is answered for question A I want to show question B. If no is answered I want to show question C.
Would something like that be implementable in my current approach? I haven't found a good tactic yet, I tried to add an attribute hide but didn't know how to reference other field values.

Angular2 log values after dynamically adding form fields

I am trying to console log the values of each object in the choices array. I am currently able to log the objects in the choices array but all of the values are empty. I am seeing timeZonePicker: "", startTimeInput: "", endTimeInput: "" for each object. I am able to add and remove from the choices array and log the key but I cannot log the value. I have tried a lot of different things but still unsuccessful.
<div class="container">
<div class="row">
<div class="col-md-9">
<div *ngFor="let choice of choices; trackBy=customTrackBy" class="form-inline">
<select [ngModel]="choice.timeZonePicker" class="form-control" id="timeZonePicker">
<option *ngFor="let timeZone of timeZones" [selected]="timeZone.chosenTimeZone == '(GMT) Western Europe Time, London, Lisbon, Casablanca, Greenwich'">{{ timeZone.chosenTimeZone }}</option>
</select>
<input [ngModel]="choice.startTimeInput" type="time" class="form-control" id="startTimeInput">
<input [ngModel]="choice.endTimeInput" type="time" class="form-control" id="endTimeInput">
</div> <!-- end form field div -->
<div class="container">
<button (click)="onSubmit()" class="btn btn-primary">Submit</button>
</div>
<div class="container">
<button class="pull-left btn btn-success" (click)="addNewChoice()">Add Field</button>
<button class="pull-left btn btn-danger" (click)="removeChoice()">Remove Field</button>
</div>
</div> <!-- end col-md-9 -->
</div> <!-- end row -->
</div> <!-- end container -->
Below is the component.
export class TimeZonesComponent {
constructor(){}
timeZones = [
{ val: -12, chosenTimeZone: '(GMT -12:00) Eniwetok, Kwajalein'},
{ val: -11, chosenTimeZone: '(GMT -11:00) Midway Island, Samoa'},....];
choices = [
{
timeZonePicker: '',
startTimeInput: '',
endTimeInput: ''
},
{
timeZonePicker: '',
startTimeInput: '',
endTimeInput: ''
}];
addNewChoice(){
var dataObj = {
timeZonePicker: '',
startTimeInput: '',
endTimeInput: ''
};
this.choices.push(dataObj);
}
removeChoice(){
var lastItem = this.choices.length - 1;
this.choices.splice(lastItem);
console.log(this.choices);
}
onSubmit(){
console.log(this.choices);
}
customTrackBy(index: number, obj: any){
return index;
}
}
I really appreciate any help.
I found out my error. I needed to use trackBy (which I wasn't initially) and [(ngModel]). I was only using one way binding but I needed two way. If anyone would like to see the code for learning, just comment and I will happily share it.

radio buttons in list don't group

I've got a pretty nasty problem here!
I have an <ul> with a group of input in every <li>. In every list element there is a radio button and I want to group them together to make them toggle when clicked.
The problem is that the radios don't connect! if I click two of them they get both checked!
Here's the code: http://jsfiddle.net/bakaburg1/Djq5q/1/
To notice: this is the code for a wordpress custom field.
Many Thanks!
EDIT1:
it's important that the fields in every list element remain connected so that on the PHP side i got a data structure as such:
array (
0 =>
array (
'answer' => 'answer1',
),
1 =>
array (
'answer' => 'answer2',
),
2 =>
array (
'is_right' => 'on',
'answer' => 'answer3',
),
3 =>
array (
'answer' => 'yiuyiuyiuj',
),
)
EDIT2:
for the time being I patched the issue with some jQuery:
jQuery('.mm_ps_answers input:radio').click(function(){
jQuery(this).parents('ul').find('input:radio').not(this).attr('checked', false)
})
Not sure the most elegant solution though, feel free to comment (not answer) on this snippet too!
EDIT3:
I solved the problem by giving three different names to the fields, and then merging the POSTed data in one array to be then serialized and saved in the database.
<li>
<a class="mm_ps_move button"><img src="http://localhost:8888/minimamedicamenta/wp-content/themes/minima/img/move.png"></a>
<a class="mm_ps_delete_answer button">Delete Answer</a>
<input type="radio" class="mm_ps_is_right" name="mm_ps_answers_is_right[]">
<input type="text" name="mm_ps_answers_input[]" value="">
<input type="hidden" name="mm_ps_answers_impressions[]">
<span class="mm_ps_impressions">impressions: <span>0</span></span>
</li>
Another problem poped up though... while the POSTed data in the back end for the text input fields is a well ordered array with a value for every field, also the empty ones, the radiobutton send only one value, so i have no idea of wich radio in wich list element has been checked, any help on this? Here's is the data in the POST for the three inputs (values are random strings):
[04-Aug-2012 17:18:45] Line (in ):
array (
0 => 'cvxcv',
1 => 'vs',
2 => '',
3 => 's',
)
[04-Aug-2012 17:18:45] Line (in ):
array (
0 => 'on',
)
[04-Aug-2012 17:18:45] Line (in ):
array (
0 => '',
1 => '',
2 => '',
3 => '',
)
EDIT4:
I patched the issues with a javascript that assign an incremental numerical value to the radio buttons on every action that modify the list (sorting, adding, deleting elements). Not very clean although.
<ul class="mm_ps_answers manage_list ui-sortable">
<li>
<input type="radio" name="SameName">
<input type="text" name="mm_ps_answers[0][answer]" value="jkjhk">
<span class="mm_ps_impressions">impressions: </span>
</li>
<li>
<input type="radio" name="SameName">
<input type="text" name="mm_ps_answers[1][answer]" value="lknjlk">
<span class="mm_ps_impressions">impressions: </span>
</li>
<li>
<input type="radio" name="SameName">
<input type="text" name="mm_ps_answers[2][answer]" value="òklkjl">
<span class="mm_ps_impressions">impressions: </span>
</li>
<li>
<input type="radio" name="SameName">
<input type="text" name="mm_ps_answers[3][answer]" value="p09i0i">
<span class="mm_ps_impressions">impressions: </span>
</li>
The property that makes a browser to toggle them is the NAME of the RadioButton. Name all those radio buttons the same(each group should have same name) and they will toggle.
If it's not possible for to use the name attribute for one reason or another (for example your name attribute is populated dynamically - which I suspect it is), you could use jQuery to toggle the checkboxes:
$(document).ready(function() {
$('input[type="radio"]').click(function() {
$('input[type="radio"]').prop('checked', false);
$(this).prop('checked', true);
});
});
To group radio buttons together you must specify the same value in the name attribute.
ex. (from W3Schools)
<form action="">
<input type="radio" name="sex" value="male" /> Male<br />
<input type="radio" name="sex" value="female" /> Female
</form>
Edit:
To keep the order you were planning in the jsFiddle link you provided I would recommend you make an iterative statement on one list item :
(sorry for incorrect php code but i haven't used it in a long time)
foreach($answer as $value){
<li>
<input type="radio" name="answer" value="$value[is_right]">
<input type="text" name="$value[answer]" value="jkjhk">
<span class="mm_ps_impressions">impressions: </span>
</li>
}