Tuesday, April 11, 2017

Form helper - checkboxes are checked when they should not be

Form helper - checkboxes are checked when they should not be

PUBLISHED 2 YEARS AGO BY ROOKWOOD
Using standard illuminate/html, I'm building a form to assign users to groups. Boring, vanilla stuff. I'm trying to have the edit form show current assignments as already checked. You know, like every other such form ever. The problem I'm running into is that when running the test within the form helper, the checkbox is ALWAYS showing as checked, even if both boolean results should return null.
Example
{!! Form::checkbox('groups[]', $group->id, $user->groups->has($group->id) ? null : null,  ['id' => $group->name,]) !!}
Whether or not the user is in the group, it should return null (see third argument), but the box shows as checked (checked="checked" in the html).
I tested the logic elsewhere, and it works fine, but for some reason, with this form helper, it is not working as I would expect.

michaeldyrynda

 michaeldyrynda
2 years ago(121,490 XP)
Have you tried passing just $user->groups->has($group->id) (which ought to return a boolean) as the third parameter, rather than null out of curiosity?


thevelement

thevelement
2 years ago(1,050 XP)
I had a fix for this issue back in 4.2, but I guess since the HTML and Form builders were on their way out of core, it wasn't considered a priority:


nolros

nolros
2 years ago(62,880 XP)
@rookwood
your 3rd parameter needs to be false to have them checked off
{!! Form::checkbox('terms', null, false, [ 'class'        =>  'form-control']) !!}
so it should be something like:
{!! Form::checkbox('terms', null, $user->groups->has($group->id) ? : false , [ 'class'  =>  'form-control']) !!}


rookwood

rookwood
2 years ago(17,870 XP)
Null or False doesn't matter. The issue ended up being a problem with form-model binding. The User object had a relation on it named groups for obvious reasons. When using form-model binding, the function checks to see if the input name has a matching model property. Since $user->groups would always return an Eloquent collection (even if empty), the actual logic to check if the relation existed wasn't evaluated. I could have put false, null, or anything else there, and the box would still show as checked.
I would never have figured this out without xdebug. That is seriously the best damn thing ever.


bestmomo

 bestmomo
2 years ago(366,760 XP)
I've had the same issue and turned it as simple Form::open(), and manualy populated my fields.


rookwood

rookwood
2 years ago(17,870 XP)
That would work, obviously; I was just trying to use form-model binding to make it easy to have the form partial shared between create/edit actions.


thevelement

thevelement
2 years ago(1,050 XP)
It's an incredibly useful way to manage relationship assignments, and I am surprised it still hasn't been fixed in the official package (laravelcollective/html). Guess it's time for another pull request...


slick

 slick
1 year ago(2,400 XP)
Hi all. Did anyone found a solution for this issue? In my case everything was working fine until I upgraded to Laravel 5.1 (from L5.0).
In my sample project, I did Laravel 5 fundamentals lessons from Laracasts with Many-to-many relation between articles and tags. Now, when I create new article obviously all checkboxes are unchecked by default but for existing articles (when I hit edit route) all are checked even when they are not assigned to this article (no relation in the article_tag table).
As @rookwood said: null or false doesn't matter. I tried to hardcode 3rd argument for testing but all checked.
This is my original part that was working before L5 upgrade.
@foreach($tags as $tag_id => $tag_name)
    <section class="col-md-2">
        {!! Form::checkbox('tag_list[]', $tag_id, '', ['id' => 'tag_' . $tag_id]) !!}
        <label for="{!! 'tag_' . $tag_id !!}">{!! $tag_name !!}</label>
    </section>
@endforeach
Currently it's an empty string. This is common for all article routes (both create and edit). Any help how to fix this?


pmall

 small
1 year ago(574,545 XP)
How is your relation named ?


slick

 slick
1 year ago(2,400 XP)
@pmall, do you mean how my two models (Article and Tag) are related?
Tag.php
/**
 * Get the articles associated with the given tag.
 *
 * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
 */
public function articles()
{
    return $this->belongsToMany('Darwin\Article');
}
Article.php
 * An article can have many tags.
 * Get the tags associated with the given article.
 *
 * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
 */
public function tags()
{
    return $this->belongsToMany('Darwin\Tag')->withTimestamps();
}

/**
 * Gets a list of tags associated with this article.
 *
 * @return array
 */
public function getTagListAttribute()
{
    return $this->tags->lists('id');
}   ```


pmall

 pmall
1 year ago(574,545 XP)
As mentioned above the problem is your model tag_list attribute has the same name as your checkbox (tag_list[]).
Here you should remove the getTagListAttribute and add a boolean method hasTag($tag_id). Then :
@foreach($tags as $tag_id => $tag_name)
    <section class="col-md-2">
        {!! Form::checkbox('tag_list[]', $tag_id, $article->hasTag($tag_id), ['id' => 'tag_' . $tag_id]) !!}
        <label for="{!! 'tag_' . $tag_id !!}">{!! $tag_name !!}</label>
    </section>
@endforeach
So there is no conflict anymore.


slick

 slick
1 year ago(2,400 XP)
@pmall, could you just tell me how does "hasTag" method inside Article.php model should look like? I'm passing tag ID and I get FatalErrorException: Method name must be a string.
Thank you.


pmall

 pmall
1 year ago(574,545 XP)
hasTag($tag_id) should return whether the article is associated with the tag or not


slick

 slick
1 year ago(2,400 XP)
Thanks @pmall.
in_array($tag_id, $tags_assigned)
placed inside foreach loop did the trick.

No comments:

Post a Comment