Vue-formulate: [Proposal] Hooks to extend any generated elements within Generated Forms (FormulateForm)

Created on 26 Jun 2020  Â·  18Comments  Â·  Source: wearebraid/vue-formulate

Describe the new feature you'd like
Extend attributes of each generated FormulateInput through FormulateForm/FormulateSchema.
A use case of mine is to extend the default classes by FormulateForm.

I've implemented the solution right there: https://gist.github.com/gahabeen/6ed9da45d4658c03b236753b5331fe54.
(Look of the extendInput prop)

What percentage of vue-formulate users would benefit?
50% (Difficult to say)

feature request

Most helpful comment

Hooks will now be a core feature of 3.0, but not implemented in 2.x

All 18 comments

The event stuff looks great! I'm trying to grok the extendInput, reading through your code it seems that the idea is these are props you can define that will apply to _all_ inputs and non inputs is that right? so it's a short hand for applying some attribute or prop to all inputs? If so, i've done a lot of thinking about this too 😉

Hooks

I'd like to suggest we extend the schema format to include on events (as previously discussed), but instead of defining an object (extendInput) to extend inputs with we hooks (functions) that allows us to write schema plugins. That way the schema itself stays really simple and easy to understand, but if you want to to use an alternative format a plugin could transform it into the base schema.

So for example since you mentioned classes, let’s take that as an example: classes are already fully supported by the schema, but you need to apply them on every input node. I would propose we implement 2 schema hooks, something like processSchema and processSchemaInput:

// Walk the full schema on your own, or manipulate it in any way.
function addBootstrapClasses (schema) {
  // ... process the entire schema to add bootstrap classes
}

// This would be called for every `FormulateInput`.
function processSchemaInput (inputSchema) {
  return {...inputSchema, ...{ input-class: ['form-control'] }}
}

Vue.use(VueFormulate, {
  processSchema: addBootstrapClasses
})

Then your input could be simple:

<FormulateForm
  :schema="[
    { type: 'select' }
  ]"
/>

And you would still end up with inputs that have the desired classes assigned. The added benefit is people could write plugins to support other pre-existing form schemas (open api, formvuelatte, vue-form-generator etc).

Events

The EventBus is removed in version 3, and is generally "deprecated" in version 2 (docs are removed) so I would probably want to use a different mechanism for moving the events up the tree, but the idea would be essentially the same as the event bus. Event bus functionality is pretty trivial to replicate.


Curious to hear your thoughts on that direction for core features? In the meantime, your plugins are doing a great job of bridging the gap.

The event stuff looks great! I'm trying to grok the extendInput, reading through your code it seems that the idea is these are props you can define that will apply to _all_ inputs and non inputs is that right? so it's a short hand for applying some attribute or prop to all inputs? If so, i've done a lot of thinking about this too 😉

What it does is that it applies attributes on each FormulateInput. (I first used it to extend the props but realized you were reading the attributes instead for the classes on FormulateInput.) The idea is that I've got general styling in main.js for inputs but if I want to generate a form from a schema and want smaller labels/fields, for example, I'd add this little extra styling from the parent FormulateForm component through the extendInput prop (naming isn't amazing, maybe inputAttributes?).

I'm not sure you hook system would be useful in my case, would it?

The EventBus is removed in version 3, and is generally "deprecated" in version 2 (docs are removed)

Good catch. I really went for the build-in stuff. Indeed a simple EventEmitter would do the job.

@gahabeen so is your expectation that something like extendInputs would apply to all schema nodes, even ones that are not FormulateInput? One thing to think about and avoid is attributes bleeding through to div elements. I think hooks provide a good global option, but I also wonder if we did implement something like extendInputs if it would be better of as a function so you can do have the option to be a little more intelligent about which elements it applies to.

<FormulateForm
  :schema="[
    {...}
  ]"
  :extend-inputs="extendInputs"
/>
function extendInputs (node) {
  return node.type === 'select' ? { ...node, labelClass: ['small-label-class'] } : node
}

Opinions on using a functional approach like that?

It was only for FormulateInput.

A function seems way smarter indeed! I should have gone for that instead :)

@justin-schroeder I updated the gist to use a hooks oriented approach as it makes way more sense.

Hence I replaced extendInputs by nodeHook and componentHook and refactored a bit the code.
It lets me act on any generated node/component.

While they might seem to overlap nodeHook focus more on the definition while componentHook let's you override completly the component generation.

@gahabeen This is looking really good! Might even be material for a substantial feature PR if we keep noodling on it! Couple things that would be great:

  • Turn it back into a functional component (just move props to the render function and don’t use this). This is good for performance, and also to render unwrapped children which is not well supported in Vue 2, you’re supposed to use functional components so there is no implicit state. But there's nothing you're doing here that needs it to be stateful.

  • Use an alternative to EventBus. Personally, I would just write my own so we don't have to import Vue from 'vue'. I think it could be something pretty simple and self-contained within that file.

@justin-schroeder

For now, I've materialized the full set of features we talked about here (and in another issue) in a proper plugin right there: https://github.com/gahabeen/vue-formulate-extended.

I'll add features I'd like in it. It should be an easier way to present them in the future and make them available to anyone else.

This functionality along with events is critical to my adoption of Formulate which sold me on it's generated forms feature. Is it possible to merge the vue-formulate-extended repo into the vue-formulate repo?

Eventually we'll adopt these many of these features in core, but specifically which features are you referring to as _critical_? The ability to assign extra properties or classes that are not in your schema?

The ability to add additional props/classes using hooks would only be a helpful shortcut and shouldn't be a blocker. It would be super easy to just have a utility function in your own codebase to do this:

<FormulateForm
  :schema="modifyMySchema(schema)"
/>

As for events, yes we need this, and there isn't a great workaround other than listening to bubbling DOM events (which is totally valid). In the meantime though feel free to use @gahabeen's plugin. Unfortunately, it's not quite as simple as just merging that code in though, it needs code review and lots of tests written, but it is on the roadmap! 2 other thoughts if these are critical needs for you and you want to move the ball along:

  1. Consider contributing a PR with the features you want and appropriate tests, this would really help jumpstart the process.
  2. Checkout other vue form generators and see if you get further down the path: https://github.com/vue-generators/vue-form-generator

@wearebraid - Yeah, the major blocker is the lack of event support. My use case it that I want to add VueCaptcha to the schema. However it emits a verify event and currently there is no way to wire it to the callback function in my component. I've spent 8 hours trying to get @gahabeen's plugin to work but because it's still a work in progress I am running into various bugs and issues with it.

I hear you @timsayshey, we'll try to prioritize the events feature for sure.

I did just have an idea for how you could use components with events today though. You could wrap them in a higher order component and pass a function in as a prop. So for example for VueCaptcha

file: VueCaptchaEmitter.vue

<template>
  <VueCaptcha @verify="verify" />
</template>

<script>
export default {
  props: {
    verify: {
      type: Function,
      required: true
    }
  }
}
</script>

then your schema would look like:

<FormulateForm
  :schema="[
    {
       component: 'VueCaptchaEmitter',
       verify: () => alert('verified')
    }
  ]"
/>

Again, no totally ideal, but if definitely an acceptable workaround in the short term. Heck if this was React land that would probably be the expected way to do it 😂

Lol -- Just tried it out and it worked! Thanks, that will get me by until events are added. I'm hoping that they will also work with the JSON schemas as well since I hope to be able to load forms from an API in the future.

Sorry guys, been busy packing. (Moving from Finland to Norway).

The vue-formulate-extended package is indeed experimental. Also the code is (imo) really simple. The few features I've added so far can be easily tweaked on your own fork too!

@timsayshey Events are now part of the - now stabilized - plugin vue-formulate-extended.

Check it out in a live demo: https://codesandbox.io/s/events-propagation-b2vsf?file=/src/components/Sandbox.vue.

→ More thinking on the hook system is happening in #67. Suggestions welcome.

Hooks will now be a core feature of 3.0, but not implemented in 2.x

🙌

Was this page helpful?
0 / 5 - 0 ratings