Search code examples
meteormeteor-autoformmeteor-collection2

Quickform / Autoform updating nested array of objects


I'm having trouble adding new nested/array values to a collection using autoForm.

I'm trying to use a quickForm to update questions. I'd like the user to be able to add more answer options. My schema looks like this (simplified to omit order, some metadata, etc):

questionSchema = new SimpleSchema({
  label: {
    type:   String
  },
  answers: {
    type: Array,
    minCount: 2,
    maxCount: 6
  },
  "answers.$": {
    type: Object
  },
  "answers.$._id": {
    type: String,
    regEx: SimpleSchema.RegEx.Id,
    autoValue: function(){ return Random.id(); },
    autoform: {
      type: "hidden"
    }
  },
  "answers.$.label": {
    type: String,
    regEx: /.{1,150}/,
    autoform: {
      label: false
    }
  },
  "answers.$.count": {
    type: Number,
    defaultValue: 0,
    autoform: {
      type: "hidden"
    }
  }
});

Other than answers.$.label, I was not using any autoform options when I was just adding questions via a quickForm type='insert'. I added those options when I wanted to edit questions because otherwise I got a complaint that I'd left count null. So I made them hidden but let them be in the form.

My edit form looks like this:

{{> quickForm collection="Questions" id="editQuestionForm"
    type="update" setArrayItems="true" doc=questionToEdit
    fields="label, answers"}}

I'm currently able to update the labels for my question and any answers that I initially added. But I can't add new answer options. When I do that, it's denied because count is not optional. But I specified a defaultValue...

I would rather my quickForm look like this so that I'm not putting the counts or _ids where the user could change them:

{{> quickForm collection="Questions" id="editQuestionForm"
    type="update" setArrayItems="true" doc=questionToEdit
    fields="label, answers, answers.$.label"}}

But maybe I need to keep answers.$._id there and hidden to ensure my changes update the right answers?

So:

  1. My answer counts default to 0 on insert, so why doesn't that happen when I edit and add answers?

  2. Can autoForm do an upsert instead of an update? Insert new questions, update existing question labels, use defaultalue or autoValue as needed.

  3. Should I use a method for this type of thing?


Solution

  • EDIT: I've updated the example and deployed the test app to metoer at http://test-questions.meteor.com/. Its a bit rough around the edges (to be honest, it's anything but useful) but it should show the functionality in action. Use the add a new question link at the bottom. The existing questions should show up at the bottom of addquestion form. Click an existing question to edit it. Overall, the functionality is there. Just don't hate me for bad design. Blame the goddess of time.


    The way I usually do embedded docs is by dividing each sub object into a separate schema. This keeps the code tidy and easier to understand as well as to avoid typical pitfalls.

    Here a sample project demonstrating the below shema in action. Just git pull and meteor run: https://github.com/nanlab/question


    new link http://app-bj9coxfk.meteorpad.com/

    Code: http://meteorpad.com/pad/7fAH5RCrSdwTiugmc/


    question.js:

    Questions = new Mongo.Collection("questions");
    
    SimpleSchema.answerSchema = new SimpleSchema({
        _id: {
            type: String,
            regEx: SimpleSchema.RegEx.Id,
            autoValue: function() {
                return Random.id();
            },
            autoform: {
                type: "hidden"
            }
        },
        label: {
            type: String,
            regEx: /.{1,150}/,
            autoform: {
                label: false
            }
        },
        count: {
            type: Number,
            autoValue: function() {
                return 0;
            },
        }
    })
    
    Questions.attachSchema(new SimpleSchema({
        label: {
            type: String
        },
        answers: {
            type: [SimpleSchema.answerSchema],
            minCount: 2,
            maxCount: 6
        },
    }))
    
    
    Questions.allow({
      insert: function(){return true;},
      update: function(){return true;},
    })
    
    
    if(Meteor.isServer) {
        Meteor.publish("questions", function() {
            return Questions.find();
        })
    } else {
        Meteor.subscribe("questions");
    }