I have 3 different blocks of different <section>s.
You can choose second only after <option> in first block.
As well as third, only if <option> from second is chosen.
Changes from first box works ok.
But in second box everything works only for firs two <section>s in third block.
I'm trying to figure out the reason, since functions for both boxes are very similar and my own debugging haven't bring me the victory yet.
http://jsfiddle.net/A1ex5andr/3Lv1f2a8/
$(document).ready(function () {
var sel2 = '#sel2';
var sel3 = '#sel3';
$('#sel1').change(function () {
var selected = $('#sel1 option:selected').attr('id');
var target = "#sel2_";
var sel2New = target + (selected.substr(selected.length-1, 1));
$(sel2New).show();
$(sel2).hide();
sel2 = sel2New;
});
$('.sel2').change(function () {
var selectedMid = $(this).attr('id');
var target_Middle = (selectedMid.substr(selectedMid.length-1, 1));
var selected = $(this).find(":selected").attr('id');
var target = (selected.substr(selected.length-1, 1));
var sel3New = "#sel3_" + target_Middle + '_' + target ;
$(sel3New).show();
$(sel3).hide();
sel3 = sel3New;
});
});
The reason some of your 3rd level lists are not showing up is because of an ID mismatch.
For example, the following declaration is correct:
<select id="sel3_1_2">
The following one is incorrect:
<select id="sel_3_1_3">
Notice the extra underscore between sel and 3. If you do a find a replace in your code for sel_ to sel, I believe everything should start working as you expect.
It is because the IDs on some selects in 3rd block starts with sel_3* and in your javascript you're trying to access sel3*.
You really need to refactor your code!
var myList = [{
value: 'A',
title: 'Value A',
children: [{
value: 'a',
title: 'Small value A',
children: [
{ value: '1', title: 'Value 1' },
{ value: '2', title: 'Value 2' },
{ value: '3', title: 'Value 3' }
]
},{
value: 'b',
title: 'Small value B',
children: [
{ value: '4', title: 'Value 4' },
{ value: '5', title: 'Value 5' },
{ value: '6', title: 'Value 6' }
]
}]
},{
value: 'B',
title: 'Value B',
children: [{
value: 'c',
title: 'Small value C',
children: [
{ value: '7', title: 'Value 7' },
{ value: '8', title: 'Value 8' },
{ value: '9', title: 'Value 9' }
]
},{
value: 'd',
title: 'Small value D',
children: [
{ value: '10', title: 'Value 10' },
{ value: '11', title: 'Value 11' },
{ value: '12', title: 'Value 12' }
]
}]
}];
function createSelect($parent, list) {
var $select = $('<select>');
if ($parent.is('select')) {
$select.insertAfter($parent);
} else {
$select.appendTo($parent);
}
$.each(list, function() {
$('<option>')
.data('children', this.children)
.attr('value', this.value)
.text(this.title || this.value)
.appendTo($select);
});
$select.on('change', function() {
var $self = $(this);
var childList = $self.children('option:selected').data('children');
$self.nextAll().remove();
if (!childList) return;
createSelect($self, childList);
});
$select.trigger('change');
}
function addNone(list) {
list.unshift({ value: '-' });
$.each(list, function() {
if (!this.children) return;
addNone(this.children);
});
}
addNone(myList);
createSelect($('#selectGroup'), myList);
select {
margin: 5px 0;
border-radius: 7px;
background: #5a5772;
color:#fff;
border:none;
outline:none;
cursor:pointer;
width: 203px;
height: 50px;
box-sizing: border-box;
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="selectGroup"></div>
Related
I am trying to add an onChange function that can add and delete options in the cascader menu. I would like to be able to select existing menu options and add options on to them as well.
My example data structure looks like this:
const [dataTitle, setDataTitle] = useState([
{
label: '2022',
value: '2022',
children: [
{
label: 'Fall',
value: 'Fall',
children: [
{
label: 'Week 1',
value: 'Week 1',
children: [
{
label: 'Practice 1',
value: 'Practice 1',
},
{
label: 'Practice 2',
value: 'Practice 2',
},
{
label: 'Practice 3',
value: 'Practice 3',
},
{
label: 'Practice 4',
value: 'Practice 4',
},
{
label: 'Game 1',
value: 'Game 1',
},
]
},
{
label: 'Week 2',
value: 'Week 2',
},
{
label: 'Week 3',
value: 'Week 3',
},
],
},
],
},
]);
I am using these onChange events below to add a 'create-new-option' button and to add the new option when prompted.
const handleLabelChange = (value, selectedOptions) => {
const lastOption = selectedOptions[selectedOptions.length - 1];
// Check if the last selected option has a value of 'create-new-option'
if (lastOption.value === 'create-new-option') {
// Prompt user for new option name
const newOptionName = prompt('Enter a name for the new option:');
// Add the new option to the options data
const newOption = {
label: newOptionName,
value: newOptionName,
};
let newDataTitle = [...dataTitle];
let parentOption = newDataTitle.find(o => o.value === lastOption.parentValue);
if (!parentOption) {
parentOption = { children: [] };
newDataTitle.push(parentOption);
}
// Check if parentOption.children is defined before trying to push
if (parentOption.children) {
parentOption.children.push(newOption);
}
setDataTitle(newDataTitle);
}
};
const addCreateNewOption = (options) => {
return options.map(option => {
return {
...option,
children: option.children
? [...addCreateNewOption(option.children), {value: 'create-new-option', label: 'Create new option', parentValue: option.value}]
: [{value: 'create-new-option', label: 'Create new option', parentValue: option.value}]
};
});
};
My issue is having the new option display in the correct spot and under the correct parents. It works correctly with the second level (I can add a 'Spring' Option and it correctly shows up under 2022 / Spring) but none others.
CodeSandbox Recreation
I have an array that looks something like this
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
As you can see, the organization name is something I want to key off of and create a data structure like this:
const output = [
// data.value will be their ID
{
organizationName: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' },
],
},
{
organizationName: 'Organization B',
data: [
{ label: 'Person 3', value: '3' },
],
},
]
What I've tried
I know I want to use reduce for something like this, but I feel like I'm off:
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
if (organizationName) {
acc.push({ organization: organizationName, data: [] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ name: externalPersonName, value: id });
}
return acc;
}, [] as any);
However the output comes out to something like this:
[
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization A', data: []},
{organizationName: 'Organization B', data: []},
];
data doesn't seem to get anything pushed inside the array in this reduce function, and the organization name get duplicated... what am I doing wrong?
Easiest way is to use an Map/Set/or object to keep track of orgs you create. This way you are not searching in the array to see if the organization was found already. After you are done, you can create the array you want from the object.
const externalPeople = {
data : [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
],
};
const providerOptions = Object.values(externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
name: externalPersonName,
id
} = currentValue;
// Is the org new? Yes, create an entry for it
if (!acc[organizationName]) {
acc[organizationName] = { organization: organizationName, data: [] };
}
// push the person to the organization
acc[organizationName].data.push({ name: externalPersonName, value: id });
return acc;
}, {}));
console.log(providerOptions)
Here is another solution
const example = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const result = example.reduce((res, entry) => {
const recordIndex = res.findIndex(rec => rec.organizationName === entry.organization.name);
if(recordIndex >= 0) {
res[recordIndex].data.push({ label: entry.name, value: entry.id});
} else {
const record = {
organizationName: entry.organization.name,
data: [{ label: entry.name, value: entry.id }]
};
res.push(record);
}
return res;
}, []);
console.log(result);
You are not checking if the value is already present in your accumulation acc
You can check it with a simple find in the if statement since it's an array
const providerOptions = externalPeople.data.reduce((acc, currentValue) => {
const {
organization: { name: organizationName },
} = currentValue;
//Check if organization is not present already
if (!acc.find(a => a.organization === organizationName)) {
//Add also the data of the element your are processing
acc.push({ organization: organizationName, data: [{label: currentValue.name, value: currentValue.id}] });
} else {
const { name: externalPersonName, id } = currentValue;
acc[acc.length - 1].data.push({ label: externalPersonName, value: id });
}
return acc;
}, [] as any);
I also added the data of the first element of the group you create when adding the organization.
The result should be as your expected output:
[
{
organization: 'Organization A',
data: [
{ label: 'Person 1', value: '1' },
{ label: 'Person 2', value: '2' }
]
},
{
organization: 'Organization B',
data: [
{ label: 'Person 3', value: '3' }
]
}
]
Hope it helps!
Compare this solution (using Lodash) with other solutions. Which one emphasises your intentions at most? This is why we use Lodash in our company - to maintain code as declarative as we can, because code readability, with minimum cognitive overload, is most important goal during coding.
const persons = [
{ id: '1', name: 'Person 1', organization: { id: '11', name: 'Organization A' } },
{ id: '2', name: 'Person 2', organization: { id: '12', name: 'Organization A' } },
{ id: '3', name: 'Person 3', organization: { id: '13', name: 'Organization B' } },
];
const personsByOrganizations = _.groupBy(persons, 'organization.name')
const output = _.map(personsByOrganizations, (persons, organizationName) => ({
organizationName,
data: _.map(persons, ({ name, id }) => ({
label: name,
value: id
}))
}))
Something like that with using a Set?
result = [...new Set(example.map(d => d.organization.name))].map(label => {
return {
organizationName: label,
data: example.filter(d => d.organization.name === label).map(d => {
return {label: d.name, value: d.id}
})
}
})
`
This question already has answers here:
Group array items using object
(19 answers)
Closed 2 years ago.
I have an array below.
var array = [
{ category: 'Input', field: 0, value: '17' },
{ category: 'Input', field: 0, value: '5' },
{ category: 'Input', field: 0, value: '8' },
{ category: 'Input', field: 5, value: '1' },
{ category: 'Input', field: 5, value: '4' },
{ category: 'Input', field: 0, value: '1' }
];
I want to convert the array-like below -- merge the objects based on the field value, if the value is same and get the result like below
[
{ category: 'Input', field: 0, value: ['17', '5', '8', '1'] },
{ category: 'Input', field: 5, value: ['1', '4'] }
];
var array = [
{ category: 'Input', field: 0, value: '17' },
{ category: 'Input', field: 0, value: '5' },
{ category: 'Input', field: 0, value: '8' },
{ category: 'Input', field: 5, value: '1' },
{ category: 'Input', field: 5, value: '4' },
{ category: 'Input', field: 0, value: '1' }
];
const obj = array.reduce((val, cur) => {
if (val[cur.field]) {
val[cur.field].push(cur.value);
} else {
val[cur.field] = [cur.value];
}
return val;
}, {});
const res = Object.keys(obj).map(key => ({
category: 'Input',
field: parseInt(key),
value: obj[key]
}));
console.log(res);
I have created dynamic form, in that form there is one dropdown. In that dropdown I want to have fiscal week for that I want to run loop inside ts function.
form.ts -
<div class="col-md-9" [ngSwitch]="field.type">
<dropdown *ngSwitchCase="'dropdown'" [field]="field" [form]="form"></dropdown>
</div>
dropdown.ts -
public field: any[] = [
{
type: 'dropdown',
name: 'fiscalweek',
label: 'fiscalweek',
placeholder: 'Fiscal Week',
required: true,
options: this.getWeek()
}
];
getWeek(){
this.week =
[
{ key: 'Select', label: 'ALL'},
{ key: '1', label: '1'},
{ key: '2', label: '2'},
{ key: '3', label: '3'},
{ key: '4', label: '4'},
{ key: '5', label: '5'},
{ key: '6', label: '6'},
.
.
.
{ key: '53', label: '53'}
]
;
return this.week;
}
I want to run a loop inside this getWeek from 1 to 53 instead of hardcoding.
Can someone please suggest me how to do that ?
You can populate week array by a simple for loop
Try like this:
Working Demo
getWeek() {
this.week = [];
this.week.push({ key: "Select", label: "ALL" });
for (var i = 1; i <= 53; i++) {
this.week.push({ key: i.toString(), label: i.toString() });
}
return this.week;
}
I am using this multiselect feature
http://docs-origin.sencha.com/extjs/4.2.2/#!/api/Ext.form.field.ComboBox-cfg-multiSelect
It works ok and i can select multiple values. But the search as you type only works for the first entry , it don't work for next entry
I want like we selects tags in Stackoverflow question
There is problem with raw ComboBox - it synchronizes selection with value list on picker. To have behaviour as described, you should extend it or find another component.
Below is a example extension, which is not perfect, but may be used as a start point:
Ext.define('Ext.form.field.MultiComboBox', {
extend: 'Ext.form.field.ComboBox',
initComponent: function() {
this.displayTpl = new Ext.XTemplate(
'<tpl for=".">' +
'<tpl if="xindex == xcount">{[typeof values === "string" ? values : values["' + this.displayField + '"]]}</tpl>' +
'</tpl>'
);
this.tpl = Ext.create('Ext.XTemplate',
'<tpl for=".">',
'<div class="x-boundlist-item x-boundlist-item-no-selection">{' + this.displayField + '}</div>',
'</tpl>'
);
this.multiSelect = true;
this.selection = [];
this.callParent();
},
// when tag is added or removed, this sets size for table cell
adjustSelectedWidth: function() {
var me = this,
cell = me.selectedCell,
width = 0;
cell.select('span.tag').each(function(el) {
width += el.getWidth() + el.getMargin('lr');
});
cell.setWidth(width);
},
// creates table cell for tags, and attaches click handler
afterRender: function(){
var me = this;
me.callParent(arguments);
var triggerWrap = me.triggerWrap,
tr = triggerWrap.down('tr');
// create table cell
me.selectedCell = tr.createChild({
tag: 'td',
cls: Ext.baseCSSPrefix + 'selected-cell'
}, tr.child('td'));
// attach click handler
me.mon(me.selectedCell, {
click: me.onSelectedCellClick,
scope: me
});
me.addChildEls({ name: 'selectedCell', select: '.' + Ext.baseCSSPrefix + 'selected-cell' });
},
// handle click on list
onItemClick: function(picker, record) {
var me = this,
cell = me.selectedCell,
value = record.get(me.valueField),
display = record.get(me.displayField);
if (me.selection.indexOf(record) === -1) {
// TODO: make template
// store selection
me.selection.push(record);
// create element which displays tag
me.selectedCell.createChild({
tag: 'span',
html: display + '<span class="remove-tag"></span>',
cls: 'tag tag-' + value,
recordValue: value
});
}
// adjust width
me.adjustSelectedWidth();
},
onSelectedCellClick: function(event) {
var me = this,
targetEl = event.getTarget('.remove-tag', null),
tagEl = targetEl.parentNode,
match = tagEl && tagEl.className.match(/tag/);
if (match) {
tagEl = Ext.get(tagEl);
var value = tagEl.getAttribute('recordValue');
var index = -1;
// remove value from selection
me.selection = me.selection.filter(function(element, index, array) {
return element.get(me.valueField) != value;
}, me);
// remove element which displays tag
tagEl.remove();
// adjust width
me.adjustSelectedWidth();
}
},
// return value based on selection stored in combo instead of selection model
getValue: function() {
var me = this,
sel = me.selection,
value = '';
for (var i = 0; i < sel.length; ++i) {
value += (i === 0 ? '' : me.delimiter) + sel[i].get(me.valueField);
}
return value;
}
});
Ext.onReady(function() {
Ext.define('Tag', {
extend: 'Ext.data.Model',
fields: [
{name: 'name', type: 'string'}
]
});
var data = {
tags: [
{ name: '00' },{ name: '01' },{ name: '02' },{ name: '03' },{ name: '04' },{ name: '05' },{ name: '06' },{ name: '07' },{ name: '08' },{ name: '09' },
{ name: '10' },{ name: '11' },{ name: '12' },{ name: '13' },{ name: '14' },{ name: '15' },{ name: '16' },{ name: '17' },{ name: '18' },{ name: '19' },
{ name: '20' },{ name: '21' },{ name: '22' },{ name: '23' },{ name: '24' },{ name: '25' },{ name: '26' },{ name: '27' },{ name: '28' },{ name: '29' },
{ name: '30' },{ name: '31' },{ name: '32' },{ name: '33' },{ name: '34' },{ name: '35' },{ name: '36' },{ name: '37' },{ name: '38' },{ name: '39' }
]
};
//note how we set the 'root' in the reader to match the data structure above
var store = Ext.create('Ext.data.Store', {
autoLoad: true,
model: 'Tag',
data : data,
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'tags'
}
}
});
Ext.create('Ext.form.Panel', {
renderTo: 'form',
items: [
Ext.create('Ext.form.field.MultiComboBox', {
store: store,
displayField: 'name',
valueField: 'name',
queryMode: 'local',
width: 400
})
]
});
});
Working sample: http://jsfiddle.net/f2JuX/16/