I'm creating multiple input fields with checkboxes in vue and I want that only one can be true. So if the user clicks on one the others should be false so that only the last clicked checkbox is true.
My code is like that:
new Vue({
el: "#app",
data: {
selected: null,
options: [
{"id": 1, "title": "One", "value": false},
{"id": 2, "title": "Two", "value": false },
{"id": 3, "title": "Three", "value": false},
]
},
watch: {
selected(selected) {
this.options.forEach((option, index) => {
option.id == selected ? option.value = true : option.value = false;
});
}
}
Unfortunately my watcher isn't working properly. I would be really glad if somebody can show me how to correct it. I want that always the last true element is the only true element and the watches sets all other elements in options to false.
If i understood your requirements correctly you can still do it with radio buttons. You can specify the value to be used inside the selected variable, as described here: https://v2.vuejs.org/v2/guide/forms.html#Radio-1. This means that you can set up a watcher and then mutate the options list accordingly:
selected: function (newVal) {
this.options.forEach(option => {
if (option.id === newVal) option.value = true
else option.value = false
})
console.log(this.options)
}
Here is a sandbox to see it in action:
https://codesandbox.io/s/heuristic-goldberg-lilsw
Update: Just saw that you want to use the </b-switch> from buefy. You can still do something similar by calling a function from the input event which then mutates the options list according to the just changed element. Something like this:
<div v-for="(option,index) in options" :key="index">
<div class="box">
<div class="field">
<b-switch v-model="option.value" #input="(modelValue) => onSwitchChanged(modelValue, option.id)">
{{ option.title }}
</b-switch>
<label :for="index">{{ option.title }}</label>
</div>
</div>
</div>
function onSwitchChanged(modelValue, id) {
if (!modelValue) return
this.options.forEach(option => {
if (option.id === id) option.value = true
else option.value = false
})
}
If you want that Only One will be selected then you have to use radio button. Checkbox has options to select all But One by One.
Without watch you can use methods. Pass index to the method.
<input
type="checkbox"
:id="index"
:value="option.id"
#click="selectAnOption(index)"
>
Method:
methods: {
selectAnOption(index) {
this.options[index].value = true
}
}
Full Code here: https://jsfiddle.net/8ktdp9ew/
Related
I have a child component which consists of 3 checkboxes (generated dynamically using ngFor) and an Apply and Cancel button.
Selector tag for the child is added in the parent's template. Parent component accesses this child component using #ViewChild and calls the present() method exposed by the child component with an object as argument as below which consists of checked state of the checkboxes.
Every time when modal is displayed, present() method is getting called. For the first time, UI/checkboxes is getting updated/checked as the values sent by parent. But, in the subsequent calls to present(), even though options.checked value is getting updated as expected in the ts file, this is not getting reflected in the UI. Every time the modal is displayed, I want checkbox to be checked or unchecked based on the value sent by the parent in present() method. Need help. Thanks in advance
parent.component.ts:
#ViewChild(childModalComponent) childModalComponent: ChildModalComponent;
onBtnClick() {
this.childModalComponent.present({
checkbox1: true,
checkbox2: false,
checkbox3: false
});
}
parent.component.html:
<feature-child-modal></feature-child-modal>
child.component.ts:
#ViewChild('childModal') childModal: ElementRef;
ngOnInit() {
this.options = [
{
label: 'label1',
value: 'value1',
checked: false,
},
{
label: 'label2',
value: 'value2',
checked: false,
},
{
label: 'label3',
value: 'value3',
checked: false,
},
];
}
present(optionsState: CompressTransactionType) {
this.options.forEach(item => {
if(item.value == "value1"){
item.checked = optionsState.checkbox1;
}
if(item.value == "value2"){
item.checked = optionsState.checkbox2;
}
if(item.value == "value3"){
item.checked = optionsState.checkbox3;
}
});
this.childModal.nativeElement.present();
}
dismiss() {
this.childModal.nativeElement.dismiss();
}
child.component.html:
<div *ngFor="let option of options">
<input
type="checkbox"
[value]="option.value"
(change)="onOptionsSelectChanged($event)"
[checked]="option.checked" />
</div>
try passing the array object here option instead of $event. Have a look in this url..
https://stackblitz.com/edit/angular-ivy-wifdeg
I have this array of objects. In each object there is another array.
panels = [{
Id: "26cfdb68-ef69-4df0-b4dc-5b9c6501b0dd",
Name: "Celiac test",
Tests: [{
Id: "e2bb4607-c227-4483-a3e9-55c1bc5a6781",
Name: "test 1 (DGP) IgG"
},
{
Id: "e2bb4607-c227-4483-a3e9-55c1bc5a6781",
Name: "test 2 (DGP) IgG"
},
{
Id: "e2bb4607-c227-4483-a3e9-55c1bc5a6781",
Name: "test 3 (DGP) IgG"
}
]
}, and so on],
I have mapped it to a bootstrap accordion with checkboxes.
First there is a checkbox for the main object, then checkboxes for the array within that object.
What I want is that when I click on the main Panel checkbox it should select the Tests checkboxes and save the panel object in the object variable, say selectedPanel, and when I deselect the main Panel it should deselect all the Tests checkboxes too.
but the main thing is that when I deselect one of the Tests checkboxes it should be removed from selectedPanel.
and the issue is it should be done on basis of Id, not checked/selected coming from backend.
Can anyone help me in this regard?
I have created a stackblitz too:
Stackblitz
Try modifying your checkItem() as below
checkItem(panel: any) {
panel.isChecked = !panel.isChecked;
panel.Tests.forEach(e => {
e.isChecked = true;
});
}
When you are done, your panels object will hold the statues of all checkboxes.
First create array which will hold selected checkboxes. This array will hold id of selected panels/tests.
selectedCheckboxes: [];
To make binding with checkbox "checked" attribute you will need another param to your object, let's call it "checked" for simplicity.
checked: true/false
In your HTML code
<input type="checkbox" class="form-check-input ml-5" id="{{panel.Id}}" name="{{panel.Name}}" (change)="togglePanelCheck(panel)" [checked]="panel.checked">
<input type="checkbox" class="form-check-input ml-5" id="{{test.Id}}" name="{{test.Name}}" [checked]="test.checked" (change)="toggleCheck(test)">
Code for toggle checking:
togglePanelCheck(panel){
if(panel.checked){
this.uncheckPanel(panel);
} else {
this.checkPanel(panel);
}
}
checkPanel(panel){
this.checkItem(panel);
if(this.panelHasTests()){
panel.tests.foreach(test => {
this.checkItem(test);
});
}
}
uncheckPanel(){
this.uncheckItem(panel);
if(this.panelHasTests()){
panel.tests.foreach(test => {
this.uncheckItem(test);
});
}
}
toggleCheck(item){
if(item.checked){
this.uncheckItem(item);
} else {
this.checkItem(item);
}
}
checkItem(item){
this.selectedCheckboxes.push(item.id);
item.checked = true;
}
uncheckItem(item){
const index = this.selectedCheckBoxes.indexOf(item.id);
this.selectedCheckBoxes.splice(index, 1);
item.checked = false;
}
private panelHasTests(panel) : boolean {
return panel.tests && panel.tests.length != 0;
}
SAMPLE https://stackblitz.com/edit/usjgwp?file=index.html
I want to show a number of kendo dropdownlist(s) on a page. The exact number depends on an API call. This API call will give me an array of stakeholder objects. Stakeholder objects have the following properties: Id, name, type, role and isSelected.
The number of dropdownlist that has to be shown on this page should be equal to the number of unique type values in the API response array. i.e,
numberOfDropdowns = stakeholders.map(a => a.type).distinct().count().
Now, each dropdown will have a datasource based on the type property. i.e, For a dropdown for type = 1, dataSource will be stakeholders.filter(s => s.type == 1).
Also the default values in the dropdowns will be based on the isSelected property. For every type, only one object will have isSelected = true.
I have achieved these things by using the following code:
<template>
<div
v-if="selectedStakeholders.length > 0"
v-for="(stakeholderLabel, index) in stakeholderLabels"
:key="stakeholderLabel.Key"
>
<label>{{ stakeholderLabel.Value }}:</label>
<kendo-dropdownlist
v-model="selectedStakeholders[index].Id"
:data-source="stakeholders.filter(s => s.type == stakeholderLabel.Key)"
data-text-field="name"
data-value-field="Id"
></kendo-dropdownlist>
<button #click="updateStakeholders">Update form</button>
</div>
</template>
<script>
import STAKEHOLDER_SERVICE from "somePath";
export default {
name: "someName",
props: {
value1: String,
value2: String,
},
data() {
return {
payload: {
value1: this.value1,
value2: this.value2
},
stakeholders: [],
selectedStakeholders: [],
stakeholderLabels: [] // [{Key: 1, Value: "Stakeholder1"}, {Key: 2, Value: "Stakeholder2"}, ... ]
};
},
mounted: async function() {
await this.setStakeholderLabels();
await this.setStakeholderDataSource();
this.setSelectedStakeholdersArray();
},
methods: {
async setStakeholderLabels() {
let kvPairs = await STAKEHOLDER_SERVICE.getStakeholderLabels();
kvPairs = kvPairs.sort((kv1, kv2) => (kv1.Key > kv2.Key ? 1 : -1));
kvPairs.forEach(kvPair => this.stakeholderLabels.push(kvPair));
},
async setStakeholderDataSource() {
this.stakeholders = await STAKEHOLDER_SERVICE.getStakeholders(
this.payload
);
}
setSelectedStakeholdersArray() {
const selectedStakeholders = this.stakeholders
.filter(s => s.isSelected === true)
.sort((s1, s2) => (s1.type > s2.type ? 1 : -1));
selectedStakeholders.forEach(selectedStakeholder =>
this.selectedStakeholders.push(selectedStakeholder)
);
},
async updateStakeholders() {
console.log(this.selectedStakeholders);
}
}
};
</script>
The problem is that I am not able to change the selection in the dropdownlist the selection always remains the same as the default selected values. Even when I choose a different option in any dropdownlist, the selection does not actually change.
I've also tried binding like this:
<kendo-dropdownlist
v-model="selectedStakeholders[index]"
value-primitive="false"
:data-source="stakeholders.filter(s => s.type == stakeholderLabel.Key)"
data-text-field="name"
data-value-field="Id"
></kendo-dropdownlist>
If I bind like this, I am able to change selection but then the default selection does not happen, the first option is always the selection option i.e, default selection is not based on the isSelected property.
My requirement is that I have to show the dropdown with some default selections, allow the user to choose different options in all the different dropdowns and then retrieve all the selection then the update button is clicked.
UPDATE:
When I use the first method for binding, The Id property of objects in the selectedStakeholders array is actually changing, but it does not reflect on the UI, i.e, on the UI, the selected option is always the default option even when user changes selection.
Also when I subscribe to the change and select events, I see that only select event is being triggered, change event never triggers.
So it turns out that it was a Vue.js limitation (or a JS limitation which vue inherited),
Link
I had to explicitly change the values in selectedStakeholders array like this:
<template>
<div
v-if="selectedStakeholders.length > 0"
v-for="(stakeholderLabel, index) in stakeholderLabels"
:key="stakeholderLabel.Key"
>
<label>{{ stakeholderLabel.Value }}:</label>
<kendo-dropdownlist
v-model="selectedStakeholders[index].Id"
:data-source="stakeholders.filter(s => s.type == stakeholderLabel.Key)"
data-text-field="name"
data-value-field="Id"
#select="selected"
></kendo-dropdownlist>
<button #click="updateStakeholders">Update form</button>
</div>
</template>
And in methods:
selected(e) {
const stakeholderTypeId = e.dataItem.type;
const selectedStakeholderIndexForTypeId = this.selectedStakeholders.findIndex(
s => s.type == stakeholderTypeId
);
this.$set(
this.selectedStakeholders,
selectedStakeholderIndexForTypeId,
e.dataItem
);
}
Tried my level best please help me out..
I am making an angular dynamic form with a form splitting into total of three parts in which the two parts were made already. Now I am making part three which will be generated by selecting an option from dropdown...
Even those part also is done...
But I am unable to make a form array in it... As I am new in angular please help me.
HTML:
Form part 3 will be here which will be array
<select multiple (change)="changeEvent($event)">
<option *ngFor="let opt of persons" [value]="opt.key">{{opt.value}}</option>
</select>
<div *ngFor="let item of array">
{{item.value}} is the parent
<div *ngFor="let child of item.templateChild">
{{child.property_name}}
<div *ngFor="let partThree of questionsParthree">
<ng-container>
<app-question [question]="partThree" [form]="formPartThree"></app-question>
</ng-container>
</div>
</div>
</div>
Select Box change event:
changeEvent(e) {
if (e.target.value == 1) {
this.array = [];
this.array.push(
{
key: 1, value: "Template one",
templateChild: [
{ property_name: "Property one" },
{ property_name: "Property two" }
]
}
);
let propertiesArray = [];
this.array.forEach(element => {
element.templateChild.forEach(data => {
propertiesArray.push(
{
key: data.property_name,
label: data.property_name,
"elementType": "textbox",
"type": "text"
}
)
});
});
this.questionsPartThree = this.service.getQuestions(propertiesArray);
this.formPartThree = this.qcs.toFormGroup(this.questionsPartThree);
this.formJoin = new FormGroup({ form1: this.form, form2: this.formPartTwo, form3: this.formPartThree });
}
}
Continuation like part 1 and 2..
I have posted the code related to creating the form array alone..
Updated Stackblitz https://stackblitz.com/edit/angular-x4a5b6-mnyifs
Here if we select any option then you will get the input boxes with values..
I would like to create array with it like,
If we select the first option Template One, Output expected is exactly as like
"templateChild" : [
{"property_one": "", "property_two":""}
]
So the final output of whole form going to be if i select Template One and also Template Two from select box (as select box is multi select),
{
"form1": {
"project_name": "",
"project_desc": ""
},
"form2": {
"property_one": "",
"property_two": ""
},
"template_details" : [
{ "template_name": "Template One",
"templateChild" : [{"property_one": "", "property_two":""}]
},
{ "template_name": "Template Two",
"templateChild" : [{"property_three": "", "property_four":"",
"property_five":""}]
}
]
}
Have a work around in the demo i have provided and give me a better solution..
Kindly please help me to create a form as like the above when we select an option from dropdown.. If i am wrong in my approach also please correct me..
If i finish this third part then everything will get alright any angular technical expert please help me..
Taking too long please help me out..
You can dynamically change an AbstractController inside a FormGroup using the setControl() method.
Add an empty form3 part for the moment
this.form3 = new FormGroup({});
this.formJoin = new FormGroup({ form1: this.form, form2: this.formPartTwo, form3: this.form3 })
When selecting an item, generate a new FormGroup according the form you create.
if (e.target.value == 1) {
this.array = [];
this.form3 = new FormGroup({'Property one': new FormControl('insert whatever you want')});
this.formJoin.setControl('form3', this.form3);
You should be able to do something what that start!
I am new to angular js programming so please excuse me if my question is naive.
I am populating a list of checkboxes depending upon values inside a json object.
I am using ng-repeat to iterate through the list of keys & values received & display the check box. When each of the check box is clicked I am making a server call to get relevant data. The data I receive should be populated in a dropdown list only when it is clicked & the dropdown should disappear on un-click. I want to capture the dropdown selected option after the user selects it.
I am stuck as to how to get it.
My code for reference :
HTML :
<div class="container" ng-controller="OptionsController" >
<div ng-repeat="(key, value) in options" ng-if="value === 'text'">
<!-- {{key}} : {{value}}-->
<input type="checkbox" ng-click="getDistinct(key)"/>
<label>{{key}}
</label>
<div class="animate-show">
<select id="selector" ng-model="QueryFormData.Graph.selected">
<option ng-repeat="name in QueryFormData.Graph.options"
ng-value="name">{{name}}</option>
</select>
</div>
</div>
</div>
Controller code :
myApp.controller('OptionsController', ['$scope', '$http', '$location', 'datashare',
function($scope, $http, $location, datashare) {
//$scope.options = datashare.jsonData;
$scope.options = {
"_id": "571a0fcfaa6d92581ec99b2e",
"Name": "text",
"isstring": "text",
"value1": "number",
"value": "number",
"status": "text",
"ItantaTime": "time"
};
$scope.keyArr = [];
$scope.QueryFormData = {
Graph: {
options: [
'Line Graph'
],
selected: 'Line Graph'
},
displayValue: ''
};
$scope.getDistinct = function(key) {
if ($scope.keyArr.indexOf(key) == -1) {
$scope.keyArr.push(key);
var ServerAddr = "http://localhost:2000/getDistinct/" + key;
console.log("Server Addr :" + ServerAddr);
$http.get(ServerAddr, {
headers: {
'Content-Type': 'application/json'
}
})
.success(function(result) {
console.log(result);
})
.error(function(data, status) {
console.log(data)
});
}
console.log("changed : " + key);
};
}
]);
With current code I am seeing all the dropdowns at once. I want to show the dropdowns as a toggle functionality on check box selection.
Any help is highly appreciated.
First, you don't need ng-if. Use the built-in filter:
<div ng-repeat="(key, value) in options" ng-if="value === 'text'">
Can be
<div ng-repeat="(key, value) in options | filter: { value: 'text' }">
For your filtering problem, you can again use the filter in select elements to only get the dropdown values you want. Your JSON response must have a filterable property though... So, please post your JSON.
You use it like this:
<select ng-options="item.key as item.name for item in collection| filter:{ someProperty: true }">