vue-form-generator icon indicating copy to clipboard operation
vue-form-generator copied to clipboard

How to add dynamic fields

Open Isomorpheus opened this issue 8 years ago • 23 comments

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

Isomorpheus avatar Jun 11 '17 10:06 Isomorpheus

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 !

lionel-bijaoui avatar Jun 12 '17 14:06 lionel-bijaoui

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

icebob avatar Jun 12 '17 18:06 icebob

This would be really a nice addition!

theKhorshed avatar Oct 12 '17 11:10 theKhorshed

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

lukeramsden avatar Oct 12 '17 15:10 lukeramsden

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

DelfsEngineering avatar Oct 12 '17 15:10 DelfsEngineering

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

lukeramsden avatar Oct 12 '17 15:10 lukeramsden

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 ..

DelfsEngineering avatar Oct 12 '17 15:10 DelfsEngineering

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

lukeramsden avatar Oct 12 '17 15:10 lukeramsden

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

DelfsEngineering avatar Oct 12 '17 16:10 DelfsEngineering

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 [email protected] 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 .

lukeramsden avatar Oct 12 '17 16:10 lukeramsden

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

theKhorshed avatar Oct 12 '17 16:10 theKhorshed

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 [email protected]

<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>

DelfsEngineering avatar Oct 12 '17 17:10 DelfsEngineering

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 avatar Oct 17 '17 11:10 lukeramsden

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

lukeramsden avatar Oct 17 '17 13:10 lukeramsden

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

DelfsEngineering avatar Oct 17 '17 13:10 DelfsEngineering

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.

lukeramsden avatar Oct 17 '17 13:10 lukeramsden

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

dmilkovski avatar Nov 06 '17 21:11 dmilkovski

Thanks Delfs Engineering I will try your solution.

Isomorpheus avatar Nov 13 '17 10:11 Isomorpheus

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

funkyfuture avatar Oct 29 '18 19:10 funkyfuture

@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.

zoul0813 avatar Dec 10 '18 20:12 zoul0813

@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

goatandsheep avatar Feb 21 '19 18:02 goatandsheep

@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.

zoul0813 avatar Feb 21 '19 18:02 zoul0813

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

goatandsheep avatar Feb 21 '19 18:02 goatandsheep