wearebraid / vue-formulate

⚡️ The easiest way to build forms with Vue.
https://vueformulate.com
MIT License
2.24k stars 246 forks source link

Can you use v-if's using the schema generator. #230

Open jamessan85 opened 3 years ago

jamessan85 commented 3 years ago

Is it possible to use v-if's when generating forms with the schema.

Obviously you can use something like below if you were using the templates but ideally would prefer to be able to use it with the schema generator <template> <FormulateInput v-if="foo" type="text" v-model="myModel" /> </template>

justin-schroeder commented 3 years ago

The schema itself is reactive, so the recommendation is to use something like a computed function to add/remove/filter any fields you'd like to manipulate conditionally. The tricky thing about schemas is we need to be able to represent them in simple JSON format, and obviously expressions are not supported in JSON.

So for now, manipulate the schema itself conditionally. If anyone things up a good way to represent conditional logic in json format, im all ears 👍

justin-schroeder commented 3 years ago

Marking this as stale for now. If future users determine this to be particularly useful or someone has a good way to represent these conditionals in json I would be happy to re-open it.

davidebigpicture commented 3 years ago

How about JSONLogic as a means of declaratively defining rules in schema?

Logic

{"if" : [
  {"<": [{"var":"temp"}, 0] }, "freezing",
  {"<": [{"var":"temp"}, 100] }, "liquid",
  "gas"
]}

Data

{"temp":55}

Result

"liquid"
justin-schroeder commented 3 years ago

interesting idea @davidebigpicture. It's worth considering. I'll look into the best ways to do this.

wh1337 commented 3 years ago

I too would like to do this, as we want to use this library to allow our client to be able to dynamically create forms and use conditional questions based on prior responses.

I think the JSONLogic idea from @davidebigpicture has good merit and could be a potential solution

TL;DR +1 for this feature

EDIT: Any way we can re-open this issue?

veritymedia commented 3 years ago

I would also love this feature! Im also trying to use vue formulate for user created surveys, which must support conditional logic in the schema. A side question, is VF the best tool to use when making a user created survey app (using a custom visual drag-and-drop style survey builder which produces the schema), and then presenting these forms?

eyleron commented 3 years ago

Assay of Conditionals in Other Libraries

vue-form-json-schema

Has a [very verbose] way of handling this: Dynamic Options Example on Code Sandbox

       "component": "div",
        "model": "age",
        "errorHandler": true,
        "displayOptions": {
          "model": "age",
          "schema": {
            "not": {
              "type": "number"
            }
          }
        },
        "fieldOptions": {
          "class": [
            "alert alert-danger"
          ]
        },
        "children": [
          {
            "component": "div",
            "fieldOptions": {
              "domProps": {
                "innerHTML": "This field is required"
              }
            }
          }
        ]
      }

Blitzar BlitzForm

A newer form generation library that puts expressions in the JSON: Docs: Dynamic Prop Based on the Value of Another Field

  {
    id: 'under18',
    component: 'input',
    type: 'checkbox',
    defaultValue: false,
    label: 'Are you under 18?',
  },
  {
    id: 'parentalConsent',
    component: 'input',
    type: 'checkbox',
    defaultValue: false,
    label: 'Do you have parental consent?',
    subLabel: 'Only applicable when under 18',
    evaluatedProps: ['disabled'],
    // component props:
    disabled: (val, { formData }) => !formData.under18,
  },

and right after that: Show or Hide a Field Based on Another Field

  {
    id: 'car',
    component: 'input',
    type: 'checkbox',
    defaultValue: false,
    label: 'Do you have a car?',
  },
  {
    id: 'carType',
    component: 'input',
    label: 'What is the brand?',
    subLabel: 'This is only shown when the first question is `true`.',
    evaluatedProps: ['showCondition'],
    showCondition: (val, { formData }) => formData.car,
  },

FormVueLate

Doesn't bring any form field components, but has validator plugins, schema, form model, etc. Docs: Conditionally displaying an element within the schema codesandbox

  type: {
    component: FormSelect,
    label: "Schema A or B?",
    options: ["A", "B"],
  },
  aField: {
    component: FormText,
    label: "A field",
    condition: model => model.type === 'A'
  },
  bField: {
    component: FormText,
    label: 'B field',
    condition(model) {
      return model.type === 'B'
    }
  }

NCForm

Uses the concept of "dx expressions" disabled: 'dx: {{$root.person.age}} < 18'

vjsf

Conditional content using valid json, to create a "pseudo language" with if/else/then.

      "properties": {
        "booleanConditionProp": {
          "type": "boolean",
          "x-display": "switch",
          "title": "I'm a boolean used to toggle the content below"
        }
      },
      "if": {
        "required": [
          "booleanConditionProp"
        ],
        "properties": {
          "booleanConditionProp": {
            "const": true
          }
        }
      },
      "then": {
        "properties": {
          "stringProp1": {
            "type": "string",
            "title": "I'm a string available if the boolean switch is true"
          }
        }
      }

VueJS Generators

Another pretty limited library, but they are also putting expression in the JSON, for better or worse, for visible, disabled, readonly and featured(?) proeprties: Docs: Dynamic Visibility

  visible: function(model) {
    //visible if business is selected
    return model && model.type == "business";
  }

Alpaca Forms

This doesn't use Vue but it's an oldie that I thought I'd include. I'll probably find some React-forms libraries' examples later too. Alpaca uses JSON Schema Dependencies. Docs: Alpaca Conditional Dependencies Docs: Alpaca Dependencies Docs: JSON Schema Dependencies

"schema": {
        "title": "Survey",
        "type": "object",
        "properties": {
            "fan": {
                "title": "Are you an Ice Cream fanatic?",
                "type": "string",
                "enum": ["Yes", "No", "Maybe"]
            },
            "icecream": {
                "title": "I see... so what is your favorite flavor?",
                "type": "String",
                "enum": ["Vanilla", "Chocolate", "Coffee", "Strawberry", "Mint"]
            }
        },
        "dependencies": {
            "icecream": ["fan"]
        }
},
    "options": {
        "fields": {
            "fan": {
                "removeDefaultNone": true,
            },
            "icecream": {
                "dependencies": {
                    "fan": ["Yes", "Maybe"]
                }
            }
        },

The dependency is set on the triggered (shown/hidden) field, referencing the triggering field. The dependencies for enumerables appear to require setting the dependency in the schema of the fields as well as in the options.

It also appears they don't support "if date is between", "if this field is X and that other field is Y". They just support "show this field if this other field(s) values are ...."


Hope these help and spark some ideas and discussion!

Vue Formulate is the best library I've found for flexibility of custom components, validation, plugins, lack of verbosity, and thoughtfulness. I want schema-based form generation to be a first-class citizen. Our forms' definitions are stored in a database, so they're all dynamic, so the template options don't work for us. By virtue of a URL, I need to dynamically load the right form definition from the server: no hand-crafting form templates.

Inspections: "Does this facility perform surgery?" : then show a bunch of surgery questions about surgery.

Medical Doctor License Applications: "Felony record in the past year?" " then require/display fields asking for explanation and upload documents.

Employment History: Repeater groups of fields for unlimited history. If gap in dates > 6 months: then ask for explanation. ...

dheimoz commented 3 years ago

hey @eyleron , thanks for this insightful information. I was wondering, which library you use the most?

eyleron commented 3 years ago

You're welcome.

I'm not using any of them yet, except some experiments to show coworkers what a stringified representation of some of our forms would look like so I can get buy-in.

It's chilling to come across what were once enthusiastically supported libraries that are now not maintained.

I recognize it's been a lot of decisions by the Vue Formulate team to balance, between things like:

justin-schroeder commented 3 years ago

I appreciate all the effort and research done into this topic. I'm not promising anything...but I think the new version has some plumbing that will allow for creating something like this as a plug-in perhaps. Either way, I hear that this is valuable to people so I'll re-open at least as a placeholder.

The question to me, is how far down this path do you go. It's only a matter of time before people are gonna want a Turing-complete language written in ...json. It feels like a slippery slope to me at least 🤷‍♂️

eyleron commented 3 years ago

I appreciate you considering this, and I hear you on the slippery slope! You can start with required and shown/hidden dependencies, and folks will ask for more of the things they can do in templates/computed, like how in Vue conditional logic like ternaries can determine which label, color, etc. prop value is used.

Then again, look at all the great work your team has done with everything else...I suspect many of the issues are features you didn't initially anticipate, as you start off not knowing what you don't know. Maybe you draw a line, or maybe it's a "temporal line" based on what the interest is now, what the low-hanging-fruit is now, 80-20 rule, etc. And that line changes each year as capabilities, interest, and expectations change.

Since most conditionally-shown fields are probably required, maybe the highest priority is handling just showing/hiding fields based on another field's value.

Making it a plugin and making your own take on it is a good idea. It'd provide the 80% people need, with the ability to take that and fork it into their own plugin, or provide their own (like the Turing-complete language!).

(I just checked out Alpaca Forms' dependencies which I'll add to the above list).

eyleron commented 3 years ago

List of Behaviors

We can make a list of the behaviors so y'all can mull, comment and prioritize.

Show/Hide Field on Another Field(s) Value(s)

felonyYn: Have you been convicted of a felony? ( ) Yes ( ) No felonyexplain: Please explain: [ textarea ] // triggered by (felonyYn = "Y")

Show/Hide Field on Another Field(s) Truthy Expression

(see JSONLogic)

numGuests: How many guests [ number ] payNotice: Please note your ticket only includes 1 guest; you'll have to pay for each additional // triggered by (numGuests > 1)

dateExam: List the date of your last exam [ date ] examReq: You'll need to have a new exam // triggered by (current_date - dateExam > 3 years)

Show/Hide Field Group on Another Field(s) Value(s)

As above but triggering an entire group of fields like Mailing Address (if different from Physical Address)

Require/Unrequire Field on Another Field(s) Value(s)

NotifyMethodYn: Do you want to be notified by cell or email? ( ) Cell ( ) Email email: [ email ] cell: [ tel ] // required by (notifyMethodYn = "Cell")

...

andyfensham commented 2 years ago

There is another product called Formly https://formly.dev/. They have something called Expression Properties, where you can access the form data and the json template and do some common actions like for e.g.

[
    {
      key: 'text',
      type: 'input',
      templateOptions: {
        label: 'Text',
        placeholder: 'Type here to see the other field become enabled...',
      },
    },
    {
      key: 'text2',
      type: 'input',
      templateOptions: {
        label: 'Hey!',
        placeholder: 'This one is disabled if there is no text in the other input',
      },
      expressionProperties: {
        'templateOptions.disabled': '!model.text',
      },
    },
  ];

or for e.g. to change the required property, the following.

[
    {
      key: 'checked',
      type: 'checkbox',
      templateOptions: {
        label: 'Required?',
      },
    },
    {
      key: 'text',
      type: 'input',
      templateOptions: {
        label: 'Moehahah',
        placeholder: 'Formly is terrific!',
      },
      validation: {
        show: true,
      },
      expressionProperties: {
        'templateOptions.required': 'model.checked',
      },
    },
  ];

So they will have something that will cover common actions but also events e.g. on click etc.

One could make that available in the Context Object

See the following link where the previous version (Angular 1) where Expressions are explained. http://docs.angular-formly.com/docs/formly-expressions

justin-schroeder commented 2 years ago

Interesting. It definitely seems it is a bit of a wild west out there, but I'm sure we can come up with something really solid. Thanks for the feature lists @eyleron and this example too @andyfensham

hisuwh commented 2 years ago

Will this be supported in the new FormKit package?

justin-schroeder commented 2 years ago

Very much so. FormKit uses a nearly Turing complete schema :)

luan-nk-nguyen commented 2 years ago

Yes, @hisuwh! FormKit schema is a first-class citizen and the Alpha already supports boolean logic, comparison, and arithmetic expressions, conditional rendering, loops, and dynamic data as well.

hisuwh commented 2 years ago

Sounds kl. Is the beta still planned for "late 2021" - I applied so hopefully I get a seat. Not much of 2021 left though

luan-nk-nguyen commented 2 years ago

Yes indeed @hisuwh. The private Beta opens in about 1 week.

hisuwh commented 2 years ago

excited!