How to check if selected dynamic form input meets some criteria - javascript

I'm still relatively new to Angular and the logic of this nested JSON is getting the better of me, so I apologize.
I am looking to create a dynamic form and used this tutorial as a starting point.
I am trying to build out conditional fields that will only display if the user selects a certain option. I would like it to work so that if any of my options has children defined in an array, a subsequent input with those children as options appears. I still have a ton of things to consider, but right now I'm trying to get it so that when a user selects Option 2, Option 2A and Option 2B automatically appear in another dropdown underneath.
How can I create an ngIf* statement that says "if the currently selected option has children, add them to another input"?
Here is my code in a sandbox
I have tried checking that the array length of "children" is longer than zero, and also created a boolean called "hasChildren" and manually assigning a true/false value to check against (though I don't prefer this solution because the finished app will have many nested options and it would be easier to just check if the array is empty or not).
I also tried this solution but must have had a syntax error, because it didn't work for me.
HTML:
<ng-template [ngSwitchCase]="'select'">
<div class="form-group">
<label [for]="input.controlName"> {{input.controlName}}</label>
<select [formControlName]="input.controlName" [name]="input.controlName" [id]="input.controlName"
[required]="input.validators.required">
<option value="">{{input.placeholder}}</option>
<option *ngFor="let option of input.options" [value]="option.value">{{option.optionName}}</option>
</select>
</div>
<div *ngIf="this.input.options.children > 0" class="form-group">
<label [for]="input.options.optionName">{{input.options.optionName}}</label>
<select [formOptionName]="input.options.children.childName" [name]="input.options.children.childName" [id]="input.options.children.childName">
<option *ngFor="let children of input.options.children" [value]="children.value">{{children.childName}}</option>
</select>
</div>
</ng-template>
form-data.ts
export interface FormData {
controlName: string;
controlType: string;
valueType?: string;
currentValue?: string;
placeholder?: string;
options?: Array<{
optionName: string;
value: string;
hasChildren: boolean;
children: Array<{
childName: string;
childValue: string;
}>
}>;
validators?: {
required?: boolean;
minlength?: number;
maxlength?: number;
};
}
mock-form.ts
import { FormData } from './../interface/form-data';
export const MockForm: FormData[] = [
{
controlName: 'Options',
placeholder: 'Choose an option',
controlType: 'select',
options: [{
optionName: 'Option 1',
value: 'option 1',
hasChildren: false,
children: []
},
{
optionName: 'Option 2',
value: 'option 2',
hasChildren: true,
children: [{
childName: 'Option 2A',
childValue: 'option 2a',
},
{
childName: 'Option 2B',
childValue: 'option 2b'
}],
}],
validators: {
required: true
}
}
]
I expect to get an output of a second dropdown containing options "Option 2A" and "Option 2B" when the user selects "Option 2," but I get a parsing error.
Many thanks in advance!

I have modified your code please check:
Your updated code sandbox code

check this demo
I think you are looking for cascading dropdown. The above solution may help you check it.

Related

Tooltip disappears after disabling and re-enabling BootstrapVue table column

I have a BootstrapVue table which looks like this;
When you hover your mouse on the First Name column, the tooltip Tooltip for First name will appear. The checkboxes at the top will cause the corresponding table columns to appear/disappear.
Here's the description of the bug I encounter.
I uncheck First Name checkbox. Column First Name disappears. Now, I recheck the First Name checkbox. Column First Name reappears again. This is fine. However, the tooltip no longer works when I hover my mouse on the First Name column.
Here's the complete code in a single HTML file.
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.2.2/dist/bootstrap-vue.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.2.2/dist/bootstrap-vue.min.js"></script>
<div id='app'>
<b-checkbox
:disabled="visibleFields.length == 1 && field.visible"
v-for="field in showFields"
:key="field.key"
v-model="field.visible"
inline
>
{{ field.label }}
</b-checkbox>
<b-table :items="items" :fields="visibleFields" bordered>
</b-table>
<b-tooltip target="HeaderFirst" triggers="hover" container="HeaderFirst">
Tooltip for First name<br>
</b-tooltip>
</div>
<script>
new Vue({
el: '#app',
computed: {
visibleFields() {
return this.fields.filter(field => field.visible)
},
showFields() {
return this.fields.filter(field => field.key.includes('first') || field.key.includes('last'))
}
},
data: dataInit,
})
function dataInit() {
let init_data = {};
init_data.fields = [
{ key: 'id', label: 'ID', visible: true },
{ key: 'first', label: 'First Name', visible: true,
thAttr: {
id: "HeaderFirst"
},
},
{ key: 'last', label: 'Last Name', visible: true },
{ key: 'age', label: 'Age', visible: true },
];
init_data.items = [
{ id: 1, first: 'Mike', last: 'Kristensen', age: 16 },
{ id: 2, first: 'Peter', last: 'Madsen', age: 52 },
{ id: 3, first: 'Mads', last: 'Mikkelsen', age: 76 },
{ id: 4, first: 'Mikkel', last: 'Hansen', age: 34 },
];
return init_data;
}
</script>
I am using vue v2.6, BootstrapVue.
The <b-tooltip>'s target must exist in the DOM upon mounting. It does not dynamically attach a new tooltip to newly created elements in the DOM.
The first run of your code shows a tooltip because <b-table> initially contains the #HeaderFirst element. When you uncheck the First Name box, the existing elements in <b-table> are replaced with new elements via the computed property. The elements removed from the DOM include the one that <b-tooltip> initially attached a tooltip to, and no new tooltip is generated after <b-table> was updated.
Solution
One solution is to render <b-tooltip> only when the target element is visible:
Create a computed prop that determines whether the #HeaderFirst field is visible.
Conditionally render <b-tooltip> based on that computed prop.
new Vue({
computed: { 1️⃣
firstNameHeaderVisible() {
return this.fields.find(field => field.thAttr?.id === 'HeaderFirst')?.visible
}
},
⋮
})
2️⃣
<b-tooltip target="HeaderFirst" v-if="firstNameHeaderVisible">
demo
On disabled elements event hover is not working. You need to wrap element by other e.g. div where you set up tooltip and then it should work well

How can I use multiple b-form-radio-group avoiding visual interference among them?

I'm new using Vue and specifically Bootstrap Vue and I'm trying to build a form with multiple radio groups.
My problem is that when I change the value in one of them the others don't change their values (this was checked with Vue DevTools) but visually it looks like none of the values are selected
I don't know what is wrong with my approach
I post here a simplified version of the code looking for some help, thanks in advance:
<template>
<div>
<b-form-group label="Radio group 1" v-slot="{ ariaDescribedby }">
<b-form-radio-group
id="radio-group-1"
v-model="selected1"
:options="options1"
:aria-describedby="ariaDescribedby"
name="radio-options"
></b-form-radio-group>
</b-form-group>
<b-form-group label="Radio Group 2" v-slot="{ ariaDescribedby }">
<b-form-radio-group
id="radio-group-2"
v-model="selected2"
:options="options2"
:aria-describedby="ariaDescribedby"
name="radio-options"
></b-form-radio-group>
</b-form-group>
</div>
</template>
<script>
export default {
data() {
return {
selected1: 'first',
options1: [
{ text: 'First', value: 'first' },
{ text: 'Second', value: 'second' },
],
selected2: 'one',
options2: [
{ text: 'One', value: 'one' },
{ text: 'Two', value: 'two' },
]
}
}
}
</script>
Since both <b-form-radio-group> elements have the same name, "radio-options", visually they are treated like one group. The different v-model would still function correctly but this isn't what you want visually. Give the second group a different name:
<b-form-radio-group
id="radio-group-2"
v-model="selected2"
:options="options2"
:aria-describedby="ariaDescribedby"
name="radio-options2"
></b-form-radio-group>
Here I changed it to radio-options2.

How can I change the value of an item in an array based on multiple conditions?

I am trying to change the value of an item in array, based on matching other items in the array. The array might contain details of the section (non-unique), a unique ID, and a value I wish to change (in this case a 'selected' flag). The goal is to be able to have multiple items which can have their selected flag. Within any single section, only one item could be 'selected' but multiple sections could have an individual item 'selected'. Conceptually, I think this could be thought of in the same way as having multiple groups of radio buttons.
The ultimate aim is to be able to use state to remember the selections made in a component that is created using props. I'm keen to understand not simply copy. I'll get my head around state mutations next, but better to solve this problem first.
So, take an array like:
menuItems: [
{
section: 'National',
id: 'First item',
selected: false
},
{
section: 'National',
id: 'Second item',
selected: false
},
{
section: 'National',
id: 'Third item',
selected: true
},
{
section: 'Local',
id: 'Fourth item',
selected: false
},
{
section: 'Local',
id: 'Fifth item',
selected: false
},
{
section: 'Local',
id: 'Sixth item',
selected: true
}
]
And some search strings like:
searchSection: 'National',
searchId: 'First item'
How would I create a function that could change the selected flag of the item with id: First item to true, the others (second, third item) to false, and don't change anything in the 'Local' section?
I have tried to get my head around using forEach loops to no avail, even though this feels the right approach. Using findIndex for the section seems destined to fail as there are multiple items to be found.
First SO question - so pre-emptive apologies if problems with the way I have asked. I'm using Vue3. All advice appreciated.
Loop through the items testing for the proper section. With the section, if there is an id match, set selected to true, otherwise set selected to false:
methods: {
flag(searchSection, searchId) {
this.menuItems.forEach(item => {
if (item.section === searchSection) {
item.selected = item.id === searchId;
}
});
}
}
Call the function:
this.flag('National', 'First item');

How to create a list such that each option is a list in AngularJS?

I want to create a list in which each option is a list itself, but when I try this, the select tag ends it prematurely. Can this be done at all in AngularJS 1.3?
<body ng-app="">
<select placeHolder="This">
<option>A place-holder!</option>
<option>
<select>
<option>A place-holder!</option>
</select>
</option>
<option>Greetings!</option>
</select>
</body>
Something like this, using optgroup creates a sub-group of option rather than a new list.
It's not a standard HTML component. You'll need a JS library that implements it. One I could find is selectivity.js but you would have to write a directive to integrate it with your angular app.
$('#select-with-sublist').selectivity({
allowClear: true,
items: [{
id: 'a',
text: 'A',
submenu: {
items: [
{ id: 'a1', text: 'A1' },
{ id: 'a2', text: 'A2' },
{ id: 'a3', text: 'A3' }
]
}
}, {
id: 'b',
text: 'B',
submenu: {
items: [
{ id: 'b1', text: 'B1' },
{ id: 'b2', text: 'B2' }
]
}
}],
placeholder: 'Select',
showSearchInputInDropdown: false
});
Working example: https://jsfiddle.net/LukaszWiktor/ge64o2ym/
Use <optgroup> instead of the inner select.
<body ng-app="">
<select placeHolder="This">
<option>A place-holder!</option>
<optgroup label="Inner list">
<option>A place-holder!</option>
<option>A place-holder!</option>
</optgroup>
<option>Greetings!</option>
</select>
</body>
This can be done using this example, its a Multi level drop-down menu in Bootstrap 3. Its better to use this since its more flexible. not restricted to two levels only.

Vue JS Option element "selected" is only attribute, need it as property

Trying to get an option to be selected by default if selected is present on the option object
This is what i have:
<template>
<select v-model="model" :placeholder="placeholder">
<option v-for="option in options" :value="option.value" :selected="option.selected">{{option.label}}</option>
</select>
</template>
<script>
export default {
props: {
model: {
default: null
},
placeholder: {
default: null
},
options: {
default: null
}
}
};
</script>
This sets selected attribute correctly, i can see it in element explorer
This doesnt seem to work, since its only an attribute, if i check the element, the "selected" property on it is false!
How do i set selected property of the option element?
You just need to set the originally selected value to the value of the v-model, i.e.
new Vue({
el: '#app',
data: {
options: [
{
value: 'foo',
label: 'foo label',
},
{
value: 'bar',
label: 'bar label',
},
{
value: 'baz',
label: 'baz label',
},
],
selected: 'bar',
}
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<select v-model="selected">
<option v-for="option in options" :value="option.value">{{option.label}}</option>
</select>
</div>
Found the issue, the "data" property for my component was coming from somewhere else.
So i needed to find where it was coming from.

Categories

Resources