Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to dynamically show and add fields of a sub-schema depending on another field? #759

Closed
miri-am opened this issue Mar 11, 2015 · 4 comments

Comments

@miri-am
Copy link

miri-am commented Mar 11, 2015

What is the best approach to dynamically show fields of a sub-schema (Object) depending on another field?
In the following example a document (Schemas.Main) can include several items defined in Schemas.Items. The fields that are needed to fill in items are dependendent on the selected type.

For example if a user selects type=="type1", fields "type1_field1" and "type1_field2" need to be filled.

A solution probably needs to use autoForm and combine AutoForm.getFieldValue and setting fields of an afArrayField, correct? I have tried a lot of combinations but either the ability to add additional items is lost (missing plus-sign) or I cannot add different items (i.e. all items are type1). Any hints how to solve this?

//Schemas
Schemas = {}; Collections = {};
Main = Collections.Main = new Mongo.Collection("Main");
Main.attachSchema(Schemas.Main);
Meteor.isClient && Template.registerHelper("Schemas", Schemas);

Schemas.Main = new SimpleSchema({
  name: {
    type: String
  },
  items: {
    type: [Schemas.Items]
  }
});

Schemas.Items = new SimpleSchema({
      type: { //FormActions...
          type: String,
          allowedValues: ['type1', 'type2', 'type3'],
          autoform: {
              type: "selectize"
          }
      },
      type1_field1: {
        type: String
      },
      type1_field2: {
        type: String
      },
      type2_field1: {
        type: String
      },
      type2_field2: {
        type: String
      },
      type3_field1: {
        type: String
      },
      type3_field2: {
        type: String
      }            
});
 {{#autoForm id="testForm" type="insert" collection=Collections.Main}}
     {{> afFieldInput name='name'}}

     {{> afArrayField name='items' fields="items.$.type, items.$.type1_field1"}} //How to set these fields dynamically depending on type?

    <div class="form-group">
      <button type="submit" class="btn btn-primary">Create</button>
    </div>
{{/autoForm}}  
@miri-am
Copy link
Author

miri-am commented Mar 11, 2015

@nQk2
Copy link

nQk2 commented Mar 11, 2015

I had a similar question. I want the user to be able to add multiple sub-categories based on (multiple) main categories.

Solution below. You need AutoForm 5.0.

Duplicate of #585

Schemas.Product = new SimpleSchema({
  name: {
    type: String,
    label: "Name",
    max: 100,
  },
  categories: {
    type: [Object],
    minCount: 1
  },
  "categories.$.mainCategory": {
    type: String,
    autoform: {
      options: function() {
        return Categories.find().map(function(obj) {
          return { label: obj.name, value: obj.name };
        });
      }
    }
  },
  "categories.$.subCategory": {
    type: String,
    autoform: {
      options: function() {
        var mainCat = AutoForm.getFieldValue(this.name.replace('.subCategory', '.mainCategory'));
        return Categories.findOne({ name: mainCat }).subCategories.map(function(obj) {
          return { label: obj.name, value: obj.name };
        });
      }
    }
  }
});

Schemas.Category = new SimpleSchema({
  name: {
    type: String,
    unique: true
  },
  subCategories: {
    type: [Object],
    minCount: 1
  },
  "subCategories.$.name": {
    type: String
  }
});

@miri-am
Copy link
Author

miri-am commented Mar 12, 2015

thanks @nQk2.

I finally used another approach and created an own template based on afArrayField, which uses {{> UI.dynamic template=currentFieldValue}}.

Dont know if this is a good approach but seems to be working for my situation:

Template.registerHelper("currentFieldValue", function () {
  return AutoForm.getFieldValue("testForm", this.current.type) || "no type so far";
});
 {{#autoForm id="testForm" type="insert" collection=Collections.Main}}
     {{> afFieldInput name='name'}}
     {{> afArrayField name='items' id="something" template="mycustom"}}     
    <div class="form-group">
      <button type="submit" class="btn btn-primary">Create</button>
    </div>
{{/autoForm}}  

<template name="afArrayField_mycustom"> 
    <div class="panel panel-default">
        <div class="panel-heading">{{afFieldLabelText name=this.atts.name}}</div>
        {{#if afFieldIsInvalid name=this.atts.name}}
        <div class="panel-body has-error">
            <span class="help-block">{{{afFieldMessage name=this.atts.name}}}</span>
        </div>
        {{/if}}
        <ul class="list-group">
            {{#afEachArrayItem name=this.atts.name minCount=this.atts.minCount maxCount=this.atts.maxCount}}
            <li class="list-group-item autoform-array-item">            
                <div>
                    <div class="autoform-remove-item-wrap">
                        {{#if afArrayFieldHasMoreThanMinimum name=../atts.name minCount=../atts.minCount maxCount=../atts.maxCount}}
                        <button type="button" class="btn btn-primary autoform-remove-item"><span class="glyphicon glyphicon-minus"></span>
                        </button>
                        {{/if}}
                    </div>
                    <div class="autoform-array-item-body">  
                    <!--all actions have a type -->
                    {{> afFieldInput name=this.current.type label=false options="auto"}}
                    <!--branch here for other fields that depend on type -->
                    {{> UI.dynamic template=currentFieldValue}}
                    </div>
                </div>
            </li>
            {{/afEachArrayItem}} 
            {{#if afArrayFieldHasLessThanMaximum name=this.atts.name minCount=this.atts.minCount maxCount=this.atts.maxCount}}
            <li class="list-group-item">
                <button type="button" class="btn btn-primary autoform-add-item" data-autoform-field="{{this.atts.name}}" data-autoform-minCount="{{this.atts.minCount}}" data-autoform-maxCount="{{this.atts.maxCount}}"><span class="glyphicon glyphicon-plus"></span>
                </button>
            </li>
            {{/if}}
        </ul>
    </div>
</template>

<template name="type1">
<!--include type1 fields here-->
</template>

<template name="type2">
<!--include type2 fields here-->
</template>

<template name="type3">
<!--include type3 fields here-->
</template>``

@miri-am miri-am closed this as completed Mar 12, 2015
@guns2410
Copy link

guns2410 commented Mar 1, 2016

Based on Miriam's answer, I would also like to share what i did to get things working. May be it could benifit.

Schemas -> actions

    Schemas.actions = new SimpleSchema({
      actions                : {
        type    : Array,
        optional: false,
        minCount: 1,
        autoform: {
          name: "actions"
        }
      },
      "actions.$"            : {
        type: Object
      },
      "actions.$.action_type": {
        type    : String,
        optional: false,
        label   : "Action Type",
        autoform: {
          type   : "select",
          class  : "action_type form-control",
          name   : "action_type",
          label  : "Select type of action",
          options: function()
          {
            let returnValue = [
              {label: "Action 1", value: "action_1"},
              {label: "Action 2", value: "action_2"},
            ];

            return returnValue;
          }
        }
      },
      "actions.$.action_1" : {
        type    : Schemas.action1,
        minCount: 1,
        optional: true,
        label   : "Action 1",
      }
    });

Schemas -> action1

    Schemas.action1 = new SimpleSchema({
      "action1_to"     : {
        type    : String,
        optional: false,
        label   : "Email To",
        autoform: {
          type       : "text",
          label      : "Email To",
          placeholder: "Comma seperated values...",
          class      : "form-control"
        }
      },
      "action1_cc"     : {
        type    : String,
        optional: true,
        label   : "Email Cc",
        autoform: {
          type       : "text",
          label      : "Email Cc",
          placeholder: "Comma seperated values...",
          class      : "form-control"
        }
      },
      "action1_subject": {
        type    : String,
        optional: false,
        label   : "Subject",
        autoform: {
          type       : "text",
          label      : "Subject",
          placeholder: "Subject for the Email",
          class      : "form-control"
        }
      },
      "action1_body"   : {
        type    : String,
        optional: false,
        label   : "Email Content",
        autoform: {
          label      : "Email Content",
          rows       : 6,
          class      : "form-control auto-size",
          placeholder: "Email Content goes here...",
          style      : "font-size: 120%;"
        }
      }
    });

Please note that Schemas.action1 should be loaded before Schemas.actions else you would only have a textbox rendered instead of the form

Templates

    <template name="actions">
        {{#autoForm id="actions-form" doc=step.data schema=schema}}
            {{> afArrayField name="actions" template="actions" step=step}}
            {{> wizardButtons}}
        {{/autoForm}}
    </template>

    <template name="afArrayField_actions">
        <div class="panel panel-default">
            <div class="panel-heading">{{afFieldLabelText name=this.atts.name}}</div>
            {{#if afFieldIsInvalid name=this.atts.name}}
                <div class="panel-body has-error">
                    <span class="help-block">{{{afFieldMessage name=this.atts.name}}}</span>
                </div>
            {{/if}}
            <ul class="list-group">
                {{#afEachArrayItem name=this.atts.name minCount=this.atts.minCount maxCount=this.atts.maxCount}}
                    <li class="list-group-item autoform-array-item">
                        <div>
                            <div class="autoform-remove-item-wrap">
                                {{#if afArrayFieldHasMoreThanMinimum name=../atts.name minCount=../atts.minCount maxCount=../atts.maxCount}}
                                    <button type="button" class="btn btn-primary autoform-remove-item"><span
                                            class="glyphicon glyphicon-minus"></span></button>
                                {{/if}}
                            </div>
                            <div class="autoform-array-item-body">
                                {{> afQuickField name=this.current.action_type label=false options=actionOptions}}

                                {{> UI.dynamic template=currentFieldValue data=this }}

                            </div>
                        </div>
                    </li>
                {{/afEachArrayItem}}
                {{#if afArrayFieldHasLessThanMaximum name=this.atts.name minCount=this.atts.minCount maxCount=this.atts.maxCount}}
                    <li class="list-group-item">
                        <button type="button" class="btn btn-primary autoform-add-item"
                                data-autoform-field="{{this.atts.name}}" data-autoform-minCount="{{this.atts.minCount}}"
                                data-autoform-maxCount="{{this.atts.maxCount}}"><span
                                class="glyphicon glyphicon-plus"></span></button>
                    </li>
                {{/if}}
            </ul>
        </div>
    </template>

    <template name="action_1">
        {{> afQuickField name=this.current.action_1 }}
    </template>

You would see {{> wizardButtons}} in the template which is because I am using the form-wizard package

and a Template.registerHelper for currentFieldValue helper

    Template.registerHelper("currentFieldValue", function()
    {
      let val = AutoForm.getFieldValue(this.current.action_type, "actions-form");
      return val || null;
    });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants