vue-generators / vue-form-generator

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

Styling forms #136

Open pimhooghiemstra opened 7 years ago

pimhooghiemstra commented 7 years ago

I was wondering if it is possible to use VFG for forms that need more HTML, like question icons with tooltips, headings and paragraphs in between the form fields. Is this supported out of the box?

I was thinking in this direction: Create multiple instances of VFG for each part and wrap those in the HTML needed. Any thought on this would be appreciated. Also, let me know if I am not clearly describing what I would like to achieve

icebob commented 7 years ago

Sorry, but currently it is not supported. because the previous version of Vue didn't support the slot access the child props. But after v2.1.0 it is available with scoped slot. So in the future we will able to make the HTML layout of vfg would be customizable. But it is a bigger modification and currently I have not enough time to develop it.

lionel-bijaoui commented 7 years ago

An "easy" solution would be to create custom fields (an header field, a paragraph field, etc.) and add them into you schema. This is more of a hack than anything else.

cristijora commented 7 years ago

After looking more in depth at scoped slots I don't think this is very hard to do. The issue that will arise here is the nesting of components. Since VFG has a v-for internally which generates the defined inputs you have to do something like a slot and inside it a template. I will play around with it a bit when I have the time. Maybe next week.

pimhooghiemstra commented 7 years ago

It would be nice! But only when there is time to do it ;-)

2017-03-02 12:46 GMT+01:00 cristijora notifications@github.com:

After looking more in depth at scoped slots I don't think this is very hard to do. The issue that will arise here is the nesting of components. Since VFG has a v-for internally which generates the defined inputs you have to do something like a slot and inside it a template. I will play around with it a bit when I have the time. Maybe next week.

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

cristijora commented 7 years ago

@icebob @lionel-bijaoui please see my commit referencing this. It's a bit messy but the idea is the following:

component(...irrelevant props)
   template(scope="props", slot="before")
    slot(:name='`${field.model}-before`')
   template(scope="props", slot="after")
    slot(:name='`${field.model}-after`')

Here we have the dynamic slots which can be nested with templates

wrapper.input-group
    slot(name="before")
        input.form-control(....irrelevant props)
        slot(name="after")

Usage:

<vue-form-generator>
<span slot="firstName-before" class="input-group-addon">
     <i class="fa fa-search"></i>
</span>
 <span slot="lastName-after" class="input-group-addon">
    <i class="fa fa-search"></i>
</span>
</vue-form-generator> 

The usage would be to specify the name of the model field + before or `after which would use the slot inside the field which is either before or after the field. Sample output for this case: image

icebob commented 7 years ago

Good idea!

cristijora commented 7 years ago

We have to think about slot naming and some conventions. Dobyou think it's a good way of specyfing custom stuff? I can't think of another option where you could specify html like this.

icebob commented 7 years ago

I think your field-specific slot name is a good idea and solve a problem. But I'm thinking on an other common slot too (e.g. if you want to add a common div to wrap every field).

cristijora commented 7 years ago

I dont quite get it. Can you show a badic example?

icebob commented 7 years ago

I try to create an example

icebob commented 7 years ago

I would like to create slots which can change the whole generator behavior. I can't create sample because it is not working with current vfg layout.

Let us assume that I want to put the label & the field to a table. And I add my custom layout as a slot.

<vue-form-generator :schema="schema" :model="model" :options="formOptions">
    <template slot="field" scope="props">
        <tr>
            <td>{{ props.field.label }}<td>
            <td>
                <!-- but in this place I dont want to override the original <component....> template-->
            </td>
        </tr>
    </template>

</vue-form-generator>

But I can't reuse some parts of the original template if I add a slot.

cristijora commented 7 years ago

This fiddle could be a starting point to maybe implement this thing you wanted above. Although it's possibile, I'm still mising or don't know yet how to treat the component since it's actually inside a v-for and the pug template makes it harder to visually understand the template at least for me.

cristijora commented 7 years ago

Just realized it's possible :) example of what we have right now. Everything except (template) can be wrapped in a named slot and on an else condition we can declare the component

template(v-for='field in fields')
   slot(name="slotName") 
       .... whatever content 
       component
   component(v-if="hasSlotDefined")

The issue is that combined with the stuff from above it might get confusing:

<vue-form-generator :schema="schema" :model="model" :options="formOptions">
    //this would be the general template for each field
    <template slot="field" scope="props">
        <tr>
            <td>{{ props.field.label }}<td>
            <td>
                <!-- but in this place I dont want to override the original <component....> template-->
            </td>
        </tr>
    </template>
   //everything after would customize the field themselves
   <span slot="firstName-before" class="input-group-addon">
      <i class="fa fa-search"></i>
    </span>
   <span slot="lastName-after" class="input-group-addon">
     <i class="fa fa-search"></i>
   </span>

</vue-form-generator>
icebob commented 7 years ago

Hmm, great!

icebob commented 7 years ago

I don't understand exactly. Could you make an example with vfg template? Simple layout of vfg:

<div>
  <fieldset>
    <template v-for="field in fields">
      <div v-if="fieldVisible(field)">
        <label>{{ field.label }}</label>
        <div class="field-wrap">
          <component :is="getFieldType(field)" :disabled="fieldDisabled(field)" :model="model" :schema.sync="field" @model-updated="modelUpdated" @validated="onFieldValidated"></component>
        </div>
      </div>
    </template>
  </fieldset>
</div>
cristijora commented 7 years ago

Place the slot inside the v-for. The content inside will get rendered if you don't have slots defined by default. If you have the slot defined which will replace the initial content then render the given slot template + the component inside the v-for

<div>
  <fieldset>
    <template v-for="field in fields">
    <slot :name="slotName" :field="field">
      <div v-if="fieldVisible(field)">
        <label>{{ field.label }}</label>
        <div class="field-wrap">
          <component :is="getFieldType(field)" :disabled="fieldDisabled(field)" :model="model" :schema.sync="field" @model-updated="modelUpdated" @validated="onFieldValidated"></component>
        </div>
      </div>
    <slot>
    <template v-if="hasSlot">
       <component :is="getFieldType(field)" :disabled="fieldDisabled(field)" :model="model" :schema.sync="field" @model-updated="modelUpdated" @validated="onFieldValidated">  </component>
    </template>
    </template>
  </fieldset>
</div>

//js code
data(){
 return { slotName:'field'}
}
computed:{
  hasSlot(){
       return this.$slots[this.slotName] !== undefined || this.$scopedSlots[this.slotName] !== undefined;
  }
}

So the following code

<vue-form-generator :schema="schema" :model="model" :options="formOptions">
    <template slot="field" scope="props">
        <tr>
            <td>{{ props.field.label }}<td>
        </tr>
    </template>

</vue-form-generator>

Will render

<tr>
    <td>{{ props.field.label }}<td>
    <component> ...field content</component>
</tr>
icebob commented 7 years ago

And how looks in the previous example:

<vue-form-generator :schema="schema" :model="model" :options="formOptions">
    <template slot="field" scope="props">
        <tr>
            <td>{{ props.field.label }}<td>
            <td>
                <!-- ??? -->
            </td>
        </tr>
    </template>
</vue-form-generator>

How can I call the original <component...> inside <td>?

cristijora commented 7 years ago

Well unfortunately I don't know how to control the tag of the component in this case. router-link for example can render certain tags based on a prop Here is an example of a wrapper component but still I don't know how to place the content there as you want it

dflock commented 7 years ago

How about doing this incrementally? Even having just the before & after slots would be very useful. What do you think about just merging that to start with, then working on the more complex case afterwards?

qikkeronline commented 6 years ago

Just came here to say that I'd also love to just be able to add markup before and after a field. It would make the entire library suitable for building more customized forms without having to create custom fields for each instance of a field that requires slightly different markup around it.

mrksdiehl commented 6 years ago

Is this still planned? Would be really nice to have.

lionel-bijaoui commented 6 years ago

Hello everyone! This is starting to exist #493 I will also add a way to customize fully the wrapper (form-element) and some level of customization to form-group. For now, it is only label, help, hint and error. Would you still need "before/after a field" slot ?