vue-generators / vue-form-generator

:clipboard: A schema-based form generator component for Vue.js
MIT License
2.99k stars 532 forks source link

How to add dynamic fields #226

Open Isomorpheus opened 7 years ago

Isomorpheus commented 7 years ago

How can you add dynamic fields or groups with this component? Something like this I mean: http://formvalidation.io/examples/adding-dynamic-field/

lionel-bijaoui commented 7 years ago

You would have to edit schema.fields. There is no easy way to do it. You could use field buttons with a method on onclick to trigger the change. This method should be the one editing the schema you send to VFG (via the schema props). You may also want to edit the model, depending on your need.

It may be interesting to add a way to do that easily with VFG. @icebob , what do you think ?

If you are able to implement this in your project, could you do a PR to add this to the plugin ? Thank you !

icebob commented 7 years ago

It can be a good feature, PR is welcomed :)

theKhorshed commented 6 years ago

This would be really a nice addition!

lukeramsden commented 6 years ago

Yeah had to ditch VFG because of this, was using it to create order forms but couldn't create dynamic rows.

DelfsEngineering commented 6 years ago

I do this just fine. Edit source schema and new fields appear. Also great for sub components line invoice line items.

lukeramsden commented 6 years ago

But how do you know where to insert the new row? I have plenty of fields before and after the dynamic row set, and potentially multiple dynamic sections. I'm just going to make the form manually and use v-for

DelfsEngineering commented 6 years ago

Well your code that the button runs need context. if you want to add a line item row then the add new row button just has to push onto the array. I have created custom components for various array like rows. By pushing a new element in the data model, a new row appears, delete a row works the same way. let me make a gif ..

lukeramsden commented 6 years ago

I'll try doing some custom components then, but it's still not a clean way of doing it.

DelfsEngineering commented 6 years ago

This uses Vue-form-wizard, and VFG, and another VFG inside the first ! ezgif-5-f53c867eb3

lukeramsden commented 6 years ago

That looks great, maybe you could make a JSFiddle and add it to the docs? Dynamic rows seem like a fairly common use-case.

On Thu, Oct 12, 2017 at 5:06 PM, DelfsEngineering notifications@github.com wrote:

This uses Vue-form-wizard, and VFG, and another VFG inside the first ! [image: ezgif-5-f53c867eb3] https://user-images.githubusercontent.com/11218557/31506564-be37598c-af45-11e7-9bc4-a2173b9c5084.gif

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/icebob/vue-form-generator/issues/226#issuecomment-336185210, or mute the thread https://github.com/notifications/unsubscribe-auth/ANJqF7NUSEaePK_ciedESBroCSB0Vpg5ks5srjkAgaJpZM4N2WY7 .

theKhorshed commented 6 years ago

Great @DelfsEngineering! Please share how you've achieved dynamic fields, adding a JSFiddle would be nice. TIA

DelfsEngineering commented 6 years ago

I cant make a fiddle but here is the whole accordion component used in the first part of the demo above. And special thanks to @icebob for the great contribution. Questions and comments welcome.

// Custom VFG components
// ACCORDION cdelfs@delfsengineering.ca

<template>
    <div class="wrapper field-input" :key="uid">
        <div class="icon-add pull-right" v-on:click="add"><span class="hidden-xs">Add </span><i class="fa fa-plus-square-o fa-2x"></i></div>
        <div class="panel panel-default">
            <div class="panel-body" style="display: block;">
                <div v-if="!value || value.length" class="panel-group accordion" id="accordion">
                    <div class="panel panel-default" v-for="(row, index) in value" >

                        <!--HEADER-->
                        <div class="panel-heading"   v-on:click.prevent="tabToggle(index)"  aria-expanded="false" >

                            <!--TRASH ICON-->
                            <div class="icon-delete pull-right" v-on:click.prevent="remove(index)">
                                <i class="fa fa-trash-o fa-lg"></i>
                            </div>

                            <!--TITLE LABEL-->
                            <div>
                                <h4 class="panel-title">
                                    <span  :class="row.tabIcon" ></span>
                                    &nbsp{{ row[schema.tabLabelModel]}}
                                </h4>
                            </div>

                        </div>

                        <!--CONTENT-->
                        <div :id="'tab'+index" class="panel-collapse collapse " v-bind:class="{ 'in': tab === index}"  aria-expanded="false">
                            <div class="panel-body">
                                <vue-form-generator :schema="schema.schema" :model="value[index]"  :options="formOptions">
                                {{modelparent}}
                                </vue-form-generator>
                            </div>
                        </div>

                    </div>
                </div>
            </div>
        </div>
    </div>

</template> 

<script>

    import  { EventBus } from'../../../../js/eventBus';

    module.exports= {
        props: ['modelparent'],
        name: 'accordion',
        mixins: [ VueFormGenerator.abstractField],
        methods: {

            tabToggle: function(index) {
                // debugger
                console.log('tab, myIndes', this.tab, index)
                if (this.tab === index) {
                    this.tab = -1 // dont show any    
                }
                else {
                    this.tab = index
                }
            },

            add: function() {
                if (this.schema.max == undefined || this.value.length < this.schema.max) {
                    this.value.push({ new: true })
                    // https://vuejs.org/v2/guide/list.html#Caveats
                    Vue.set(this.value[this.value.length - 1], [this.schema.tabLabelModel], this.value.length)
                    this.tab = this.value.length - 1
                    console.log('new tab is: ', this.tab)
                }
            },

            remove: function(index) {
                var modalConfig= {
                    icon: "warning",
                    body: "Are you sure you wish to delete this entry?",
                    rows: this.value,
                    index: index,
                    buttons: [ {
                        text: "Delete",
                        class: "btn btn-danger btn-square",
                        onClick: function(event, rows, rowIndex) {
                            rows.splice(rowIndex, 1);
                            EventBus.$emit('hideModal');
                        }
                    }
                    ]
                }
                EventBus.$emit('showModal', modalConfig);

            }
        },
        mounted() {   },

        data() {
            return {
                tab: -1,
                uid: (new Date().getTime()).toString(36),
                showLast: false
            }
        } }
</script>

<style scoped>

.panel-default>.panel-heading {
    background-color: #f5f5f5;
    cursor: pointer;
}
.fa-plus-square-o {
    position: relative;
    top: 5px;
}

.icon-add {
    position: absolute;
    right: 30px;
    top: 0px;
}
.icon-delete {
    display: none;
    margin-bottom: 20px;
    cursor: pointer;

}

.panel-heading:hover .icon-delete {
    display: inline-block;

}

</style>
lukeramsden commented 6 years ago

I'm happy to make a PR and try and implement this in the core, but first, we need to decide on how it will work.

The easiest way in terms of implementation is probably just adding a field to the core library, of type dynamic or similar, that takes a schema property, which is just the schema passed to the VFG for each row.

Anything else anyone wants to add to that?

edit: Made a fork to work on this - if anyone can help, please feel free to make a PR to the dynamic-rows branch: https://github.com/lukeramsden/vue-form-generator/tree/dynamic-rows

edit2: How it would work:

const schema = {
    fields: [{
        // Root field
        type: 'dynamic',
        // Has to be an array, but VFG has a type check for model bindings?
        model: 'user'
        schema: {
            fields: [{
                type: 'input',
                inputType: 'text',
                model: 'firstName'
            }]
        }
    }]
}
lukeramsden commented 6 years ago

Progress!! It's working well functionally, but having some problems cleaning it up, like the nested fieldset for no apparent reason. I'll make the PR, and someone who knows more about VFG can clean it up :P:P In Action

DelfsEngineering commented 6 years ago

This should not be part of core, the way the rows replicated is highly dependant on the CSS and div structure of the repeating element. I use the for accordions, tabs and rows for example. All have different v-for= code

lukeramsden commented 6 years ago

Take a look at the PR, it's not really dependant on anything. If it makes sense to move it to optional we can do that, but it definitely makes sense to have at least a basic row field.

At the very least if the PR is closed, add this to the examples so people know how to do it for themselves in future. We'll see what icebob thinks.

dmilkovski commented 6 years ago

Hi everyone, thanks for great example @DelfsEngineering. How I can run validation on dynamic fields from main form, when I submitting it? TIA

Isomorpheus commented 6 years ago

Thanks Delfs Engineering I will try your solution.

funkyfuture commented 5 years ago

@lionel-bijaoui what's the reason why this was removed as goal for version 3? i couldn't find any related discussion.

zoul0813 commented 5 years ago

@lionel-bijaoui can we close this?

I don't think that modifying the schema should be a part of VFG Core. Modifying the form schema is very project dependent as @DelfsEngineering mentioned above.

goatandsheep commented 5 years ago

@zoul0813 sure it's project dependent, but so are most of the features here. @DelfsEngineering was saying that the reason why they didn't make a pull request is because their code that got it to work was project dependent, but remaking it should not be out of the question

zoul0813 commented 5 years ago

@goatandsheep adjusting the schema dynamically is highly dependent upon the project, and how the schema is provided to VFG to begin with. I don't think it should be a part of VFG Core, but I do see this as a potential add-on to VFG as a secondary package that could be installed and used.

I think it would probably be a relatively easy project to setup and get off the ground, but unfortunately, don't have the time to work on something like this at the moment.

We are more than happy to provide links in the README and Docs to any useful VFG Add-ons.

goatandsheep commented 5 years ago

@zoul0813 I just noticed vfg-field-array and I agree that we should close this ticket