Search code examples
javascriptmeteormeteor-autoformsimple-schema

Meteor AutoForm: How to update schema values with array of sub-documents?


I am new to Meteor AutoForm. I want to update player docs with country doc. Below is my code. If I add{{> afQuickField name="country"}} in AutoForm it doesn't works. {{> afQuickField name="name"}} alone works fine. How do I add entire country docs (_id,slug,name) in players country field?

JS:

CountrySchema = new SimpleSchema({
    _id: {
        type: String,
        index: true
    },
    slug: {
        type: String,
        max: 100,
        index: true
    },
    name: {
        type: String,
        max: 100
    }
});

Player.attachSchema(new SimpleSchema({
    name: {
        type: String,
        label: "Player name",
        index: true
    },
    country: {
        type: [CountrySchema],
        label: "country",
        max: 5,
        index: true,
        autoform: {
            options: function() {
                return Country.find().fetch().map(function(object) {
                    return {
                        label: object.name,
                        value: object._id
                    };
                });
            }
        }
    }
})); 

HTML:

{{#autoForm id="editplayer" }}
   {{> afQuickField name="name"}}
   {{> afQuickField name="country"}}
{{/autoForm}}

I added SimpleSchema.debug = true; console log shows SimpleSchema invalid keys for "editplayer" context.


Solution

  • If you want to render country as an array field you could use the afArrayField component:

    <template name="editPlayer">
        {{#autoForm id="editplayer" collection="Player" type="update" doc=currentPlayer}}
            {{> afQuickField name="name"}}
            {{> afArrayField name="country"}}
            <button type="submit">Submit</button>
        {{/autoForm}}
    </template>
    

    if (Meteor.isClient) {
        AutoForm.debug();
        Template.editPlayer.helpers({
            currentPlayer: function () {
                return Player.findOne();
            }
        });
    }
    
    if (Meteor.isServer) {
        Meteor.startup(function () {
            let country = {slug: 'austria', name: 'Austria'};
            Country.insert(country);
            Player.insert({name: 'Matthias Eckhart', country: [country]});
        });
    }
    
    Player = new Mongo.Collection("player");
    Country = new Mongo.Collection("country");
    
    CountrySchema = new SimpleSchema({
        slug: {
            type: String,
            max: 100,
            index: true
        },
        name: {
            type: String,
            max: 100
        }
    });
    
    Player.attachSchema(new SimpleSchema({
        name: {
            type: String,
            label: "Player name",
            index: true
        },
        country: {
            type: [CountrySchema],
            label: "country",
            max: 5,
            index: true,
            autoform: {
                options: function () {
                    return Country.find().map((object) => ({label: object.name, value: object._id}));
                }
            }
        }
    }));
    

    Please note, that I assumed you want to update a player document. Therefore, I set the attribute doc=currentPlayer and specified a player document via the currentPlayer helper function. If you set the data context, for instance via the data function in your Iron Router route, you could use doc=this.


    If you want to have a simple select form, your current data structure may require a workaround. The problem is that [CountrySchema] does not expand into proper schema keys. As a result, you need to explicitly specify the full schema and convert the selected options using AutoForm hooks and a Meteor helper function (currentPlayer):

    <template name="editPlayer">
        {{#autoForm id="editplayer"  collection="Player" type="update" doc=currentPlayer}}
            {{> afQuickField name="name"}}
            {{> afQuickField name="country"}}
            <button type="submit">Submit</button>
        {{/autoForm}}
    </template>
    

    if (Meteor.isClient) {
        AutoForm.debug();
        Template.editPlayer.helpers({
            currentPlayer: function () {
                let player = Player.findOne();
                if (player) player.country = _.map(player.country, (country) => country._id);
                return player;
            }
        });
        var playerUpdateHook = {
            before: {
                update: function (doc) {
                    doc['$set'].country = _.map(doc['$set'].country, (countryId) => Country.findOne({_id: countryId}));
                    return doc;
                }
            }
        };
        AutoForm.addHooks('editplayer', playerUpdateHook);
    }
    
    if (Meteor.isServer) {
        Meteor.startup(function () {
            let country = {slug: 'austria', name: 'Austria'};
            let countryId = Country.insert(country);
            country = Country.findOne({_id: countryId});
            Player.insert({name: 'Matthias Eckhart', country: [country]});
        });
    }
    
    Player = new Mongo.Collection("player");
    Country = new Mongo.Collection("country");
    
    CountrySchema = new SimpleSchema({
        _id: {
            type: String,
            index: true
        },
        slug: {
            type: String,
            max: 100,
            index: true
        },
        name: {
            type: String,
            max: 100
        }
    });
    
    Player.attachSchema(new SimpleSchema({
        name: {
            type: String,
            label: "Player name",
            index: true
        },
        country: {
            type: [CountrySchema],
            label: "country",
            max: 5,
            index: true
        },
        'country.$': {
            type: String,
            autoform: {
                options: function () {
                    return Country.find().map((object) => ({label: object.name, value: object._id}));
                }
            }
        }
    }));