formers / former

A powerful form builder, for Laravel and other frameworks (stand-alone too)
https://formers.github.io/former/
1.34k stars 204 forks source link

Is it possible to render two controls alongside one label? #42

Closed spib closed 11 years ago

spib commented 11 years ago

Hi,

Is is possible to render two controls (for example a textbox and a select) alongside one label? I've tried using prepend/append but that doesn't give me what I need.

I guess ideally I'd be able to open a control group (with a label), then add controls (which would not render their own control groups), and the close the group. Something like

{{ Former::control_group_open('label.label')}}
  {{ Former::xlarge_text('value', false)}}
  {{ Former::select('values', false, $value_list) }}
{{ Former::control_group_close() }}

Is this possible?

Thanks

Anahkiasen commented 11 years ago

It is possible on Former 3 on the composer branch but that version only works on Laravel 4, when it will be done I'll convert it back for Laravel 3. You were close on the syntax !

{{ Former::group('label.label')}}
  {{ Former::xlarge_text('value', false)}}
  {{ Former::select('values', false, $value_list) }}
{{ Former:: closeGroup() }}
kapooostin commented 11 years ago

Great! I just was forced to use plain HTML instead of Former because I needed a composite control.

cviebrock commented 11 years ago

I tried pretty much the same code as above:

{{ Former::group('Start Date', array('class'=>'datetime') ) }}
  {{ Former::small_text('startdate', false)->addClass('date')->readOnly()->appendIcon('calendar') }}
  {{ Former::small_text('starttime', false)->addClass('time')->readOnly()->appendIcon('time') }}
{{ Former::closeGroup() }}

And the output shows it wrapping each field in a control-group anyway, which forces them onto new lines (using Bootstrap):

<div class="datetime control-group">
    <label for="Start Date" class="control-label">Start Date</label>  
    <div class="control-group">
        <div class="controls">
            <div class=" input-append">
                <input class="input-small date" readonly="true" type="text" name="startdate" id="startdate">
                <span class="add-on"><i class="icon-calendar"></i></span>
            </div>
        </div>
    </div>  
    <div class="control-group">
        <div class="controls">
            <div class=" input-append">
                <input class="input-small time" readonly="true" type="text" name="starttime">
                <span class="add-on"><i class="icon-time"></i></span>
            </div>
        </div>
    </div>
</div>

I'm also not sure why my "startdate" input gets an id but my "starttime" one doesn't. This is on L3, BTW.

Thanks!

Anahkiasen commented 11 years ago

Hm maybe Former is not detecting the group as opened, I'll check that out.

cviebrock commented 11 years ago

Further to this, I'm not sure if the errors from the previous group are carrying forward.

My post controller redirects back to the get controller on errors with:

return Redirect::back()
    ->with_input()
    ->with_errors($campaign->errors);

In my case, $campaign->errors contains

Laravel\Messages Object
(
    [messages] => Array
        (
            [url] => Array
                (
                    [0] => Required
                )

        )

    [format] => :message
)

The URL field happens be just before the grouping in my view:

{{ Former::xlarge_url('url','Campaign URL')->required()->prepend( home_url() )->blockHelp('Can contain "a-z", "0-9", "-" and "_"; must start with "a-z".') }}

{{ Former::group('Start Date', array('class'=>'datetime') ) }}
  {{ Former::text('startdate', false)->addClass('datepicker')->prependIcon('calendar') }}
  {{ Former::small_text('starttime', false)->addClass('timepicker')->prependIcon('time')->inlineHelp('Eastern time (EST/EDT).') }}
{{ Former::closeGroup() }}

Now, when there is an error in with the URL, then the Start Date group inherits the error and is output with:

<div class="datetime control-group error required">
  <label for="Start Date" class="control-label">Start Date</label>
    <div class="control-group">
       ...

All of this only happens if the field before it has errors.

Note that it also seems to inherit the required setting, even though that's not specified in the Former::group() at all. This seems to happen regardless of whether the previous field has errors.

Hope this helps track things down ... maybe!

cviebrock commented 11 years ago

Ping on this, with an update.

Under L4, dev-agnostic, this code:

{{ Former::group('Length Range') }}
    {{ Former::small_number('min_length','Minimum')->min(2)->max(15) }}
    {{ Former::small_number('max_length','Maximum')->min(2)->max(15) }}
{{ Former::closeGroup() }}

produces this

<div class="control-group">
    <label for="Length Range" class="control-label">Length Range</label>
    <div class="control-group">
        <label for="min_length" class="control-label">Minimum</label>
        <div class="controls">
            <input class="input-small" min="2" max="15" id="min_length" type="number" name="min_length">
        </div>
    </div>
    <div class="control-group">
        <label for="max_length" class="control-label">Maximum</label>
        <div class="controls">
            <input class="input-small" min="2" max="15" id="max_length" type="number" name="max_length">
        </div>
    </div>
</div>

Setting the labels to false for the two inputs doesn't help.

petercoles commented 11 years ago

I'm suffering the same problem, namely when grouped fields are preceded by a field that fails validation, the error class is wrongly attached to the following group's control-group div.

The Group::open() method is invoked as expected, but the previous field's name is carried forward causing that previous field's error message to be erroneously retrieved and returned by Former::getErrors(), which in turn triggers the attachment of the error class on the group.

petercoles commented 11 years ago

Some simplified code to illustrate ...

{{ Former::text('card_number', 'Card Number') }}

{{ Former::group('Card Start Date') }}
    <div class="controls">
        {{ Former::select('card_start_month', '')->options(Config::get('cards.months'))->class('span6')->raw() }}
        {{ Former::select('card_start_year', '')->range(2003, date('Y'))->select(date('Y'))->class('span6')->raw() }}
    </div>
{{ Former::closeGroup() }}

An error in the card_number field, triggers the error class to be added to the Card Start Date group.

Anahkiasen commented 11 years ago

That's because Former adds the error to the group by default, as inline fields don't have help texts. I don't really know how I could handle that one.

petercoles commented 11 years ago

Hmm. That's not exactly how I see it. Looks to me like it's not a default being used, but instead the data from the previous field.

This happens in part because when a group is constructed directly the opening tag data is determined before the group can have any idea what fields it's wrapping, so doesn't have the new data to replace that of the previous field.

However, it's actually surprisingly easy to fix and I now have working code that appears to do so. It passes all the existing tests, but I'm running into some difficulties constructing new ones for this specific issue.

I'll put it into a pull request for you to take a look at.

Anahkiasen commented 11 years ago

I've kind of lost track of what the core problem of this issue was – can this be closed ?

petercoles commented 11 years ago

Yes. It mutated a couple of times. Original point answered. Bit in the middle is a little unclear, but cviebrock who raised that moves on to the more significant issue which is resolved by #162 - so I'd recommend closing it.

cviebrock commented 11 years ago

I just noticed the activity here. Can we take a look at this again, at least the original problem of not being able to put 2 controls alongside one label?

With Laravel 4.0.5 and the dev-master version of Former, I do this:

{{ Former::open_horizontal() }}

{{ Former::group('Start Date') }}
  {{ Former::small_text('startdate', false)->addClass('date')->placeholder('dd/mm/yyyy') }}
  {{ Former::small_text('starttime', false)->addClass('time')->placeholder('hh:mm') }}
{{ Former::closeGroup() }}

{{ Former::close() }}

The end result is this:

<div class="control-group">
    <label for="Start Date" class="control-label">Start Date</label>
    <div class="control-group">
        <div class="controls">
            <input class="input-small date" placeholder="dd/mm/yyyy" id="startdate" type="text" name="startdate">
        </div>
    </div>
    <div class="control-group">
        <div class="controls">
            <input class="input-small time" placeholder="hh:mm" id="starttime" type="text" name="starttime">
        </div>
    </div>
</div>

Again, each input is wrapped in a control-group which causes them to render on different lines in Bootstrap.

Am I missing something? Has the syntax changed since the original bug report, or does it still not work?

Thanks!

Anahkiasen commented 11 years ago

You need to add ->raw() after each field.

cviebrock commented 11 years ago

Ah ... And I'll need to add in the control group divs (for Bootstrap), so the full template is going to be:

{{ Former::group('Start Date') }}
<div class="control-group">
  <div class="controls">
    {{ Former::small_text('startdate', false)->addClass('date')->placeholder('dd/mm/yyyy')->raw() }}
    {{ Former::small_text('starttime', false)->addClass('time')->placeholder('hh:mm')->raw() }}
  </div>
</div>
{{ Former::closeGroup() }}

This works, thanks. Although, to be picky, this now requires that I hard-code some of my formatting (those two divs) into my template. If I change from Bootstrap to Foundation, for example, it's not as easy as a one-liner.

Anahkiasen commented 11 years ago

Well in theory the control group itself is created by the Former::group, you don't need it there. But yeah you need the controls, but that's a compromise I made, it was either add some more automation but then require even more commands to create custom fields, or this. This way you can really do whatever you want in the group.