Search code examples
formslaravellaravel-bladequoteslaravelcollective

Prevent escaping single quotes when using laravelcollective/html in forms with blade templating


Problem description: I'm encountering some problem with laravel escaping quotes when using the laravelcollective/html component. I need the quotes to remain unescaped in order to keep the javascript code working.

Puropose of the code: The user needs to indicate when he wants to keep a form field empty. If they do not, some input is required. I don't want a user to indicate that a field is empty and that he submits some input at the same time. This behaviour is controlled by javascript by removing the checked status of a checkbox when hitting a radio button and vice versa.

There are multiple fields in the form (each with radio buttons and a checkbox if the field should be empty). All have to be checked by javascript before submitting the form. Each field has its own name.

The blade code for a field:

<div>
     {!! Form::checkbox('field_empty',1,null, ['id' => 'field_empty'] !!}
</div>
<div>
     {!! Form::radio('field', 0, null, ['onClick' => 'unselectCheckbox(this,'field_empty')'] !!}
     {!! Form::radio('field', 1, null, ['onClick' => 'unselectCheckbox(this,'field_empty')'] !!}
     {!! Form::radio('field', 2, null, ['onClick' => 'unselectCheckbox(this,'field_empty')'] !!}
</div>

The javascript code:

function unselectCheckbox(box,field)
{
    if (box.value >= 0)
    {
        document.getElementsById(field).checked = false;
    }
}

(I also have a javascript function for removing the checked radio button when clicking the checkbox, but I didn't include it in the code.)

When using the above code, I get a laravel error due to the single quotes around 'field_empty'. Removing the single quotes doesn't result in working js. Using double quotes results in '"' in the generated html.

Of course I could use plain html instead of blade - and so prevent using {!! !!} tags - , but I was wondering whether it's possible to do it in blade, or maybe by using another approach in javascript?


Edit: My form contains multiple items which have to be checked. Each item has its own name, with corresponding checkbox for confirmation of empty value:

<div>
     {!! Form::checkbox('field1_empty', 1, null, ['id' => 'field_empty']) !!}
</div>
<div>
     {!! Form::radio('field1', 0, null) !!}
     {!! Form::radio('field1', 1, null) !!}
     {!! Form::radio('field1', 2, null) !!}
</div>
<div>
     {!! Form::checkbox('field2_empty', 1, null, ['id' => 'field_empty']) !!}
</div>
<div>
     {!! Form::radio('field2', 0, null) !!}
     {!! Form::radio('field2', 1, null) !!}
     {!! Form::radio('field2', 2, null) !!}
</div>
<div>
     {!! Form::checkbox('field3_empty', 1, null, ['id' => 'field_empty']) !!}
</div>
<div>
     {!! Form::radio('field3', 0, null) !!}
     {!! Form::radio('field3', 1, null) !!}
     {!! Form::radio('field3', 2, null) !!}
</div>

Solution

  • The attribute value can be escaped by passing it as a HtmlString instance which will not be escaped.

    {!! Form::radio('field', 0, null, ['onClick' => new Illuminate\Support\HtmlString("unselectCheckbox(this,'field_empty')")]) !!}
    

    That being said, having onclick events defined inline on the element is generally considered a bad idea. You should instead handle the events on the JavaScript side, so you don't have to include the onClick properties in your element definition. So the HTML should look like this:

    <div>
         {!! Form::checkbox('field_empty', 1, null, ['id' => 'field_empty']) !!}
    </div>
    <div>
         {!! Form::radio('field', 0, null) !!}
         {!! Form::radio('field', 1, null) !!}
         {!! Form::radio('field', 2, null) !!}
    </div>
    

    And the JavaScript that handles it should look like this:

    (function () {
        var empty  = document.getElementById('field_empty');
        var radios = document.getElementsByName('field');
    
        // When clicking on any of the radio buttons, uncheck the field_empty checkbox
        for (var i = 0; i < radios.length; i++) {
            radios[i].onclick = function () {
                // The "if (box.value >= 0)" condition is useless here
                // because it will always be true, so I removed it
                empty.checked = false;
            };
        }
    
        // When clicking on the empty checkbox, uncheck the radio buttons
        empty.onclick = function () {
            for (var i = 0; i < radios.length; i++) {
                radios[i].checked = false;
            }
        };
    })();
    

    Here's a working example of the code above. As you can see there's no need for external functions like unselectCheckbox because all the logic is being handled in the event functions.


    There is also the jQuery version that is significantly shorter and more readable that the vanilla JS version:

    (function () {
        var empty  = $('#field_empty');
        var radios = $('[name=field]');
    
        radios.click(function () {
            empty.prop('checked', false);
        });
    
        empty.click(function () {
            radios.prop('checked', false);
        });
    })();
    

    And here's a working example of the jQuery version.