Meteor Autoform - disable selection options on array of Object fields

I want to disable an option if it is already selected in one of the object groups.

So, if I had selected "2013" then added another example, "2013" will not be available in this group, unless this option is changed in the original group.

Is there an easy way to do this that I am missing? Do I need to promptly update the schema when choosing?

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}
             ]
            }
            ];
        }   
    }

},

      

+3


source to share


1 answer


Proof of concept

I know this post is about 3 years old. However, I ran into the same problem and want to give an answer to all those who also stumbled upon this post.

This answer is only a proof of concept and does not provide a complete general and effective solution that can be used in production applications.

A completely generic solution would require a deep code change to how select box parameters are generated and updated in AutoForm

.


Some previous notes.

I am using Autoform> = 6, which provides a nice API to get field and form values ​​in your SimpleSchema instantly without too much trouble. SimpleSchema

is included as an npm package and Tracker

must be passed to it in order to make Meteor reactive.

Functions like AutoForm.getFieldValue are reactive, which is a real big improvement. However, reactively changing the selection parameters based on the reactive value causes many update cycles and slows down performance (as we will see below).

Using AutoForm.getFormValues not work when used in the options field Object. When working on an Array field, it will not behave reactively on Object fields, so it will not update filtering on them.


Manipulating parameters of arrays of selected inputs (failure)

You cannot use it with array field types. This is because if you change the select options it applies to all of your select instances in the array. Therefore, it will also apply to your already selected values ​​and will delete them as well. This makes your choice look like it is always "unselected"

You can test this with the following code example:

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 ​​in an object field

Upon closer inspection of the schema, I saw the property maxCount

. This made me think that if you have a list of max options, you can solve this by setting the properties of the object samples

(by the way: maxCount: 5

doesn't make sense when there are only three choices).

This results in each choice having its own update that does not interfere with the others. This requires an external function that keeps track of all selected values, but this turned out to be very simple.

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});

      

In the above code, there are three example entries (a, b and c) that will allow their parameters to be computed by an external function.

This function must meet certain requirements:

  • filter has no parameters if nothin is selected
  • not the filter that is currently selected samples

    select
  • filter all other options if selected by another selection

The code for this function is as follows:

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 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 expand and scale it to the complexity you want (optgroups, more fields on samples

, check for other fields with different fields, etc.)

Bad: - view - tied to a specific (or closest) context of the form (see here ) - much more code to write than array.

0


source







All Articles