mongodbmeteormeteor-autoformmeteor-collection2simple-schema

Using objects as options in Autoform


In my Stacks schema i have a dimensions property defined as such:

dimensions: {
    type: [String],
    autoform: {
        options: function() {
            return Dimensions.find().map(function(d) {
                return { label: d.name, value: d._id };
            });
        }
    }
}

This works really well, and using Mongol I'm able to see that an attempt to insert data through the form worked well (in this case I chose two dimensions to insert)

Mongol image

However what I really what is data that stores the actual dimension object rather than it's key. Something like this:

[Mongol good[2]

To try to achieve this I changed type:[String] to type:[DimensionSchema] and value: d._id to value: d. The thinking here that I'm telling the form that I am expecting an object and am now returning the object itself.

However when I run this I get the following error in my console.

Meteor does not currently support objects other than ObjectID as ids

Poking around a little bit and changing type:[DimensionSchema] to type: DimensionSchema I see some new errors in the console (presumably they get buried when the type is an array

console image

So it appears that autoform is trying to take the value I want stored in the database and trying to use that as an id. Any thoughts on the best way to do this?.

For reference here is my DimensionSchema

export const DimensionSchema = new SimpleSchema({
    name: {
        type: String,
        label: "Name"
    },
    value: {
        type: Number,
        decimal: true,
        label: "Value",
        min: 0

    },
    tol: {
        type: Number,
        decimal: true,
        label: "Tolerance"
    },
    author: {
        type: String,
        label: "Author",
        autoValue: function() {
            return this.userId
        },
        autoform: {
            type: "hidden"
        }
    },
    createdAt: {
        type: Date,
        label: "Created At",
        autoValue: function() {
            return new Date()
        },
        autoform: {
            type: "hidden"
        }
    }
})

Solution

  • According to my experience and aldeed himself in this issue, autoform is not very friendly to fields that are arrays of objects.

    I would generally advise against embedding this data in such a way. It makes the data more difficult to maintain in case a dimension document is modified in the future.

    alternatives

    if you insist on using AutoForm

    While it may be possible to create a custom input type (via AutoForm.addInputType()), I would not recommend it. It would require you to create a template and modify the data in its valueOut method and it would not be very easy to generate edit forms.

    Since this is a specific use case, I believe that the best approach is to use a slightly modified schema and handle the data in a Meteor method.

    Define a schema with an array of strings:

    export const StacksSchemaSubset = new SimpleSchema({
      desc: {
        type: String
      },
      ...
      dimensions: {
        type: [String],
        autoform: {
          options: function() {
            return Dimensions.find().map(function(d) {
                return { label: d.name, value: d._id };
            });
          }
        }
      }
    });
    

    Then, render a quickForm, specifying a schema and a method:

    <template name="StacksForm">
      
      {{> quickForm
        schema=reducedSchema
            id="createStack"
            type="method"
            meteormethod="createStack"
            omitFields="createdAt"
      }}
    
    </template>
    

    And define the appropriate helper to deliver the schema:

    Template.StacksForm.helpers({
      reducedSchema() {
        return StacksSchemaSubset;
      }
    });
    

    And on the server, define the method and mutate the data before inserting.

    Meteor.methods({
      createStack(data) {
        // validate data
        const dims = Dimensions.find({_id: {$in: data.dimensions}}).fetch(); // specify fields if needed
        data.dimensions = dims;
        Stacks.insert(data);
      }
    });