Using objects as options in Autoform
Asked Answered
W

2

6

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"
        }
    }
})
Wicks answered 6/8, 2016 at 17:39 Comment(3)
Have you seen this issue?Dorindadorine
@Dorindadorine nope. That pretty much covers it. Thanks for giving me closure.Wicks
Question is, are you sure that you want to embed the entire document? Maybe you only need a subset? Is there any chance that a dimension would change in the future (and if so, how do you plan to update the nested documents in a maintainable way)? You could also use a Meteor method to handle the form and do everything there.Dorindadorine
D
1

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

  • You can use a package like publish-composite to create a reactive-join in a publication, while only embedding the _ids in the stack documents.
  • You can use something like the PeerDB package to do the de-normalization for you, which will also update nested documents for you. Take into account that it comes with a learning curve.
  • Manually code the specific forms that cannot be easily created with AutoForm. This gives you maximum control and sometimes it is easier than all of the tinkering.

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);
  }
});
Dorindadorine answered 18/8, 2016 at 18:23 Comment(0)
H
0

The only thing i can advise at this moment (if the values doesnt support object type), is to convert object into string(i.e. serialized string) and set that as the value for "dimensions" key (instead of object) and save that into DB.

And while getting back from db, just unserialize that value (string) into object again.

Hypothesize answered 18/8, 2016 at 10:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.