Here is aikau Combobox usage:
{
name: "alfresco/forms/controls/ComboBox",
config: {
fieldId: "someFieldId",
label: "myListName",
name: "assoc_sc_goods",
addedAndRemovedValues: true,
valueDelimiter: ",",
firstValueIsDefault: false,
showAllOptionsOnOpen: true,
searchStartsWith: true,
optionsConfig: {
queryAttribute: "label",
labelAttribute: "label",
valueAttribute: "name",
publishTopic: "ALF_GET_FORM_CONTROL_OPTIONS",
publishPayload: {
resultsProperty: "options",
url: url.context + "/proxy/alfresco/slingshot/datalists/lists/node/workspace/SpacesStore/11111111-1111-1111-1111-111111111111",
itemsAttribute: "datalists",
labelAttribute: "name",
valueAttribute: "nodeRef"
}
}
}
}
It displays all values from list with UUID 11111111-1111-1111-1111-111111111111 well, e.g. it shows names instead of nodeRef values. But when I press button on form, it sends
"assoc_sc_goods_added": "goodName"
While it is expected
"assoc_sc_goods_added": "workspace://SpacesStore/22222222-2222-2222-2222-222222222222"
Where 22222222-2222-2222-2222-222222222222 is UUID of selected list item.
It seems that Combobox request json, extract values for html select by labelAttribute but when it comes to apply selected values it forget to convert selected labelAttribute back to valueAttribute.
How configure aikau Combobox to fix such issue?
You need to change both valueAttribute settings to be "nodeRef" - currently only one of them is.
Related
I have a dropdown component as shown below: (For Reproduction go to this link)
I want to add search to filter functionality that allows to filter the dropdown elements, based on the query: this.DropDownElements.includes(searchParameter).
constructor(props)
{
super(props);
this.DropDownElements = this.props.Values.map((element,ind) => ({
value: element,
isChecked: false,
id: "DropdownElems:" + this.props.UniqueIdentifier + ind,
}));
this.ConstantDropDownElements = this.props.Values.map((element,ind) => ({
value: element,
isChecked: false,
id: "DropdownElems:" + this.props.UniqueIdentifier + ind,
}));
}
I am storing searchParameter value in this.state.Value.
state = {
isEditing: false,
isFirstOnChange: true,
isDropDownOpen: false,
isMultiSelectOn: true,
elements: this.DropDownElements,
placeholder: this.props.PlaceHolderText,
Value: "",
GlobalFilters: [
{
selection: "Select All",
isSelected: false,
},
{
selection: "Clear All",
isSelected: false,
},
],
};
Now here comes the challenging part. When I search in the search box, I want to perform autofilter and then make the selections. What I tried is creating an unaffected DropDown as initialized in constructor and then filtering the DropDownElements based on the search parameter passed. But then mapping the isChecked key of the object is getting very complex to me. Does anyone have a simple solution and approach for the same?
My current Component allows to select and de-select without losing the state (during the collapse and open of dropdown), my final goal is to add a search query feature which filters up the data, and then when I make the selection it should be mapped correctly to the right element of the DropDownElements.
I have a div and a following javascript:
let usersNotContributingIds = [ 19, 20, 21 ];
let usersNotContributingNames = [ "Flavius K.", "Pogchamp", "Lazy Gopnik" ];
let contributorToBeAddedId; // the value that will be used for further actions
$("#alpaca-search-contributing-users").alpaca({
data: null,
schema: {
type: "object",
enum: usersNotContributingIds,
},
options: {
name: "pls",
label: 'Contributor Fullname',
optionLabels: usersNotContributingNames,
helper: "Select user sou want to add as a contributor",
id: "select2-search",
focus: false,
events: {
change: function() {
console.log(this.getValue().value);
contributorToBeAddedId = this.getValue().value
},
focus: function() {
console.log(this.name);
},
blur: function() {
console.log(this.name + ": blur");
},
ready: function() {
console.log(this.name);
}
},
},
postRender: function(control) {
$('#select2-search').select2();
}
});
Obviously, I want to get the newly set value, or anyhow access the selected value and use it. For example with AJAX and a button.
The problem is, that when I have 3 or less options, Alpaca render the field not as a search, but as a radio-something and the this.getValue() is null.
Is there a way to force Alpaca to NOT USE THE RADIO BUTTONS? I dont want to use them, even if I had only 1 option. Documentation just promtly states, that if there are 3 or less options, it will generate radio buttons instead of select, but it says nothing about the fact, that it breaks everything and that I would not be able to retrieve the value the same way as with select field.
If I am doing something inefficiently or wrong, please tell me, I am new with Alpaca and I just want a neat select dropdown with search, that I can use to pick users from a list with any length. Also, I would like the "null" or "none" option to not be there.
To have your select component rendered you should use the option type and set it to "select".
The issue with the value is because you're using it wrong, to get the value in alpaca you only do this.getValue() and there's no need to add .value.
FYI: If you see the error "This field should have one of the values in Flavius K., Lazy Gopnik, Pogchamp. Current value is: 19" you should update your enum array to have strings instead of ints let usersNotContributingIds = [ "19", "20", "21" ];.
Here's a working fiddle for this.
I'm wanting to disable an option if it has already been selected in one of the object groups.
So, if I selected "2013" then added another sample, "2013" would not be available in that group, unless that option is changed in the original group.
Is there an easy way to do this that I'm missing? Do I need to reactively update the schema when a selection is made?
samples:{
type: Array,
optional: true,
maxCount: 5
},
"samples.$":{
type: Object,
optional: true
},
"samples.$.sample":{
type:[String],
autoform: {
type: "select",
options: function () {
return [
{
optgroup: "Group",
options: [
{label: "2013", value: 2013},
{label: "2014", value: 2014},
{label: "2015", value: 2015}
]
}
];
}
}
},
Proof of Concept
I know this post is about 3 years old. However, I came across the same issue and want to provide an answer for all those who also stumbled over this post.
This answer is only a proof of concept and does not provide a full generic and performant solution, that could be used on production apps.
A fully generic solution would require a deep change in the code of how select field options are generated and updated in AutoForm.
Some prior notes.
I am using Autoform >=6 which provides a good API to instantly obtain field and form values in your SimpleSchema without greater trouble. SimpleSchema is included as npm package and Tracker has to be passed to it in order to ensure Meteor reactivity.
Functions like AutoForm.getFieldValue are reactive, which is a real great improvement. However, reactively changing the select options based on a reactive value causes a lot of update cycles and slows the performance (as we will see later).
Using AutoForm.getFormValues is not working, when using it within options of an Object field. While working within Array field, it will not behave reactively in Object fields, thus not update the filtering on them.
Manipulating Options for Arrays of Select Inputs (failing)
You can't use it with array types of fields. It's because if you change the select options, it applies for all your select instances in the array. It will therefore also apply to your already selected values and strips them away, too. This makes your select looks like it is always "not selected"
You can test that yourself with the following example code:
new SimpleSchema({
samples:{
type: Array,
optional: true,
maxCount: 5
},
"samples.$":{
type: String,
autoform: {
type: "select",
options: function () {
const values = AutoForm.getFormValues('sampleSchemaForm') || {};
const samples = values && values.insertDoc && values.insertDoc.samples
? values.insertDoc.samples
: [];
const mappedSamples = samples.map(x => x.sample);
const filteredOpts = [
{label: "2013", value: "2013"},
{label: "2014", value: "2014"},
{label: "2015", value: "2015"}
].filter(y => mappedSamples.indexOf(y.value) === -1);
return [
{
optgroup: "Group",
options:filteredOpts,
}
];
}
}
},
}, {tracker: Tracker});
Using fixed values on an Object Field
when taking a closer look at the schema, I saw the maxCount property. This made me think, that if you anyway have a list of max options, you could solve this by having fixed properties on a samples object (by the way: maxCount: 5 makes no sense, when there are only three select options).
This causes each select to have it's own update, that does not interfere the others. It requires an external function, that keeps track of all selected values but that came out be very easy.
Consider the following code:
export const SampleSchema = new SimpleSchema({
samples:{
type: Object,
optional: true,
},
"samples.a":{
type: String,
optional:true,
autoform: {
type: "select",
options: function () {
const samples = AutoForm.getFieldValue("samples");
return getOptions(samples, 'a');
}
}
},
"samples.b":{
type: String,
optional:true,
autoform: {
type: "select",
options: function () {
const samples = AutoForm.getFieldValue("samples");
return getOptions(samples, 'b');
}
}
},
"samples.c":{
type: String,
optional:true,
autoform: {
type: "select",
options: function () {
const samples = AutoForm.getFieldValue("samples");
return getOptions(samples, 'c');
}
}
},
}, {tracker: Tracker});
The code above has three sample entries (a, b and c) which will let their options be computed by an external function.
This function needs to fulfill certain requirements:
filter no options if nothin is selected
filter not the option, that is selected by the current samples select
filter all other options, if they are selected by another select
The code for this function is the following:
function getOptions(samples={}, prop) {
// get keys of selections to
// determine, for which one
// we will filter options
const sampleKeys = Object.keys(samples);
// get sample values to
// determine which values
// to filter here
const sampleValues = Object.values(samples);
const filteredOptiond = [
// note that values are stored as strings anyway
// so instead of parsing let's make them strings
{label: "2013", value: "2013"},
{label: "2014", value: "2014"},
{label: "2015", value: "2015"}
].filter(option => {
// case 1: nothing is selected yet
if (sampleKeys.length === 0) return true;
// case2: this selection has a
// selected option and current option
// is the selected -> keep this option
if (sampleKeys.indexOf(prop) > -1 && option.value === samples[prop])
return true;
// case 3: this selection has no value
// but others may have selected this option
return sampleValues.indexOf(option.value) === -1;
});
return [
{
optgroup: "Group",
options: filteredOptiond,
}
]
};
Some Notes on this Concept
Good:
-it works
-you can basically extend and scale it to your desired complexity (optgroups, more fields on samples, checking against other fields with other fields etc.)
Bad:
- performance
- bound to a given (or the nearest) form context (see here)
- much more code to write, than for an array.
I recently posted about getting a combobox into the settings of a Rally app, now I'm trying to figure out how checkboxes work in settings. I assumed they would work similarly [ish] but for some reason it's not [hence why i'm on this site again].
My checkbox field and getSettingsField function look like this right now:
getSettingsFields: function() {
return [
{
xtype: 'fieldcontainer',
defaultType: 'checkboxfield',
items: [
{
name: 'box1',
boxLabel: 'Box 1:',
inputValue: true,
value: true,
id: 'boxone'
}
]
}
];
}
At the top of my app I also have the following default settings set:
config: {
defaultSettings: {
box1: true
}
},
I console.log()'d the setting for that checkbox inside the launch function and found that the setting starts at "true" but the checkbox is not originally checked. When I check the box and save the settings, the setting stays at "true" and again unchecks when i go back to the settings tab. This would all be okay, but when I save the settings with the box unchecked, the setting still stays as "true".
I tried changing the defaultSetting to false just for testing, but again I only got a "true" setting field for box1. My logging line, console.log('Setting: ' + this.getSettings()); is what is showing me the current value for each setting each time the app is loaded & each time the settings are changed.
The goal is to get the checkbox setting to read correctly [true / false or whatever syntax the settings come in] at the beginning of the app so a grid can be filtered later. Does someone know what I'm doing wrong?
So apparently the settings tab is returning a string, so "true" is returned from the inputValue, not the boolean true value. Also, the value setting is messing things up, so here's what I ended up using:
config: {
defaultSettings: {
boxes: "check"
}
},
...
getSettingsFields: function() {
return [
{
xtype: 'fieldcontainer',
defaultType: 'checkboxfield',
items: [
{
name: 'boxes',
boxLabel: 'Box 1:',
inputValue: "one",
checked: this.getSettings().boxes.split(',').indexOf('one') > -1,
id: 'boxone'
},
{
name: 'boxes',
boxLabel: 'Box 2:',
inputValue: "two",
checked: this.getSettings().boxes.split(',').indexOf('two') > -1,
id: 'boxtwo'
}
]
}
];
}
I learned that the 'name' field is a generic field that should apply to all checkboxes that are related to each other in the settings panel. The 'inputValue' field is the returned string to the settings field, and each returns to a string in this.getSettings().boxes.
I wanted to keep the boxes to remember if they were checked before, so that's where the line checked: this.getSettings().boxes.split(',').indexOf('one') > -1 comes from. If the string 'one' is in the settings, that means the index will be larger than one, so checked will be true and therefore the box will be checked next time someone opens the settings menu.
I have written some code that works pretty well, but I have a strange bug
Here is an example...
PLEASE WATCH MY COMBOBOX BUG VIDEO
Like I said, this works well every time datachanged fires - the right index is selected and the displayField is displayed but, everytime after I type some text in the combobox, later, when the "datachanged" fires, it wont display the displayField. Instead, it displays the value from the setValue method I launch.
The strange thing is that if I don't ever type text and change the selection with the mouse there is no bug. Finally, this appears only when I type text in the combobox.
Has anyone heard of this bug, have a solution, or some wise advice?
The Code !
Two data stores :
ficheDataStore = new Ext.data.Store({
id: 'ficheDataStore',
autoLoad: true,
proxy: new Ext.data.HttpProxy({
url: 'ficheDetail.aspx', // File to connect to
method: 'GET'
}),
baseParams: { clientId: clientId, Type: 'Fiche' }, // this parameter asks for listing
reader: new Ext.data.JsonReader({ // we tell the datastore where to get his data from
root: 'results'
}, [
{ name: 'GUID', type: 'string', mapping: 'GUID' },
{ name: 'TagClient', type: 'string', mapping: 'TagClient' },
{ name: 'Nom', type: 'string', mapping: 'Nom' },
{ name: 'Compteur', type: 'string', mapping: 'CompteurCommunes' },
{ name: 'CompteurCommunesFacturation', type: 'string', mapping: 'CompteurCommunesFacturation' },
{ name: 'AdresseFacturation', type: 'string', mapping: 'AdresseFacturation' },
{ name: 'Codes', type: 'string', mapping: 'Codes' },
{ name: 'Observations', type: 'string', mapping: 'Observations' },
{ name: 'Adresse', type: 'string', mapping: 'Adresse' }
])
});
communesDataStore = new Ext.data.Store({
autoLoad: true,
proxy: new Ext.data.HttpProxy({ url: 'ficheDetail.aspx?Type=Communes' }),
reader: new Ext.data.JsonReader({ root: 'results' }, [{ name: 'Compteur' }, { name: 'Localisation'}])
});
Who return something like this for the
first:
{results:[{"Nom":"cercle interieur"},{"Observations":""},{"Codes":" "},{"Adresse":"dd"},{"CompteurCommunes"
:"1"},{"TagClient":"3-56"},{"GUID":"443609c6-d064-4676-a492-7baa7b4288d1"},{"AdresseFacturation":""}
,{"CompteurCommunesFacturation":"1"}]}
For the latter :
{"results":[{ "Compteur" : "1","Localisation" : "6200 ST ISIDORE"},{ "Compteur" : "2","Localisation"
: "21340 CHANGE"},{ "Compteur" : "3","Localisation" : "1200 ELOISE"},{ "Compteur" : "4","Localisation"
: "1200 ST GERMAIN SUR RHONE"},{ "Compteur" : "5","Localisation" : "75000 PARIS"},{ "Compteur" : "6"
,"Localisation" : "75001 PARIS 1ER ARRONDISSEMENT"}]}
a Combobox :
var comb = new Ext.form.ComboBox(
{
store: communesDataStore,
fieldLabel: 'Code postal',
// hiddenName: 'Compteur',
name: 'CompteurCommune',
id: 'CompteurCommunes',
width: 300,
typeAhead: true,
mode: 'local',
minChars: 0,
selecOnFocus: true,
forceSelection: true,
valueField: 'Compteur',
displayField: 'Localisation',
autocomplete: true,
emptyText: 'Selectionnez un code postal',
triggerAction: 'all',
value: ''
});
in a datachanged event i set the new value of the Combobox "CompteurCommunes" :
ficheDataStore.addListener('datachanged', handleDatachangedEvent);
function handleDatachangedEvent()
{
try {
comb.setValue(ficheDataStore.getAt(4).data.Compteur);
}
catch (err) { }
}
It's probably because when you type random data into combo, it may not locate correct fieldValue every time. Then it stucks at the last non-existing value.
Try to set ComboBox to any existing value (in combo's datastore) before doing new setValue() in your datachanged event handler. Or you can try to use clearValue() method to reset the previous (undefined) valueField.
There also initList() method existing to reset combo to initial state.
EDIT: After some testing, I found that:
combo.store.clearFilter(); must be used before setValue in the external event handler.
function formatComboBox(value, metaData, record, rowIndex, colIndex, store) {
myStore = Ext.getCmp('myComboBox');
myStore.clearFilter();
var idx = myStore.find('value',value);
return (idx != '-1') ? myStore.getAt(idx).data.label : value;
}
First, the Ext JS combobox should automatically apply the value and display when an item is selected, barring you've assigned a store and told Ext the field requires a value.
The value you appear to be asking for (CompteurCommunes) does not appear in your reader definitions, so it would be a part of the records in the data store.
What is the underlying reason for why you are trying to set this value for the ComboBox?
You can have a look at hiddenName and hiddenId parameter of Ext.form.ComboBox. If you set the value of hidden form field linked with combobox then combobox would render the label of that value instead of the value itself.
This is useful when you need to set the value at server end and direct the user to the page.
Another useful method is selectByValue. This method would select the element that has the value equal to the passed argument.
In your dataChangedListener instead of setting the value of combobox, you should set the value hidden form field associated with ComboBox. Also after setting the value of hidden field you might have to fire selectByValue method.
You can have a look at ExtJS API Documentation for further reference.
In case anybody - like me - get here through google because it's the most similar to their ComboBox ft. setValue() problem:
After an hour of stepping in out and over Ext's internals, I found that I needed to set lazyInit: false for the combo boxes. It defaults to true and being true may cause unlogical behaviour if you don't know about this. And why would you? Anything else seems to be not lazy by default.