Display Checkbox Group Properly in Twig

Introduction

In a form I’ve recently worked on using Symfony, I had to show a group of selectable checkboxes. In this case, the user can select any number of checkboxes an also in one group, two of the checkboxes I needed to use Javascript to detect when the box was selected. I wrote about that in my Javascript Code Refactoring article.

There are two types of problems when using a ChoiceType built-in Field Type set to element type of checkboxes in Symfony:

  1. Rending in Twig is not easy.
  2. Setting attributes is not easy, and may not work as you expect.

The above two reasons are why I decided to write this article.

Example Code

Below is an example checkbox group that I created in a form class for some employment questions that are asked in a form:

public function buildForm(FormBuilderInterface $builder, array $options)
{
   $builder
      ...
      // Employment
     ->add('employ', ChoiceType::class, array(
           'mapped' => false,
           'required' => false,
           'expanded' => true,
           'multiple' => true,
           'label' => 'Employment',
           'choices' => array(
                   'I have a job. # of hours/week:' => 'have_job',
                   'I am work study eligible' => 'work_study',
                   'I need assistance in finding a job' => 'find_work',
                   'I need to learn interviewing skills' => 'interview',
                   'I have no employment needs at this time' => 'no_needs',
                   'I volunteer for a non-profit organization' => 'non_profit',
                   'I need assistance with my resume' => 'resume',
                   'I need assistance finding an internship' => 'intern',
                   'I am undecided about my career or major' => 'major',
                   'Other:' => 'other',
            ),
      ))
      ...
}

Then when I tried to render in a single html table row in Twig, using code like this:

<tr><td>{{ form_widget(form.employ) }}</td></tr>

Then this appears like this rendering in a web page:

TwigRender

Notice the problem in that each checkbox appears inline directly after each other. I tried to use the Twig nl2br filter together with “\n” in my choice values, but that didn’t work since I really need another table row to show each of the checkboxes and labels.

Adding Checkboxes to TR

Since a ChoiceType in a Symfony form is an array of checkboxes, then we can use a Twig for loop to properly show the widget and label in a HTML table row. The Twig code needed is as follows:

{% for i in 1..8 %}
   <tr><td>&emsp;{{ form_widget(form.employ[i]) }}{{ form_label(form.employ[i]) }}</td></tr>
{% endfor %}

The unfortunate part of the above code is, in order to add an attribute like a Javascript onchange, this has do be done in Twig on the particular element like so:

<tr><td>&emsp;{{ form_widget(form.employ[0],{'attr':{'onchange':'changeJobHours()'}}) }}
   {{ form_label(form.employ[0]) }}&emsp;
   {{ form_label(form.job_hours) }}{{ form_widget(form.job_hours) }}</td></tr>

The above adds an onchange attribute for the first checkbox. I also have a job hours and label, but those are hidden and shown when a onchange event is triggered and box is selected.

Resultant Code

The overall resultant code looks like the following:

<table id='6th' style='border: 1px solid; margin-left: auto; margin-right: auto; width: 50%; display: none;'
   background="{{ asset('images/indneeds_assess/employment2.png') }}">
   <tr><td colspan="2"><b>{{ form_label(form.employ) }}</b></td></tr>
   <tr><td>{{ form_widget(form.employ) }}</td></tr>
   <tr><td>&emsp;{{ form_widget(form.employ[0],{'attr':{'onchange':'changeJobHours()'}}) }}
      {{ form_label(form.employ[0]) }}&emsp;
      {{ form_label(form.job_hours) }}{{ form_widget(form.job_hours) }}</td></tr>
   {% for i in 1..8 %}
      <tr><td>&emsp;{{ form_widget(form.employ[i]) }}{{ form_label(form.employ[i]) }}</td></tr>
   {% endfor %}
   <tr><td>&emsp;{{ form_widget(form.employ[9],{'attr':{'onchange':'changeEmploy()'}}) }}
      {{ form_label(form.employ[9]) }}&emsp;
      {{ form_label(form.employ_other) }}{{ form_widget(form.employ_other) }}</td></tr>
</table>

The above code puts each checkbox in on HTML tr elements. The resultant web page looks like the following and appears much better than the original.

Employment

Javascript Code Refactoring

Introduction

Recently, I have been working on a multi-step (has Back and Next buttons) html form (in Symfony) where the various sections of the form have the same “Other” checkbox. I use a “onchange” Javascript event to check when the checkbox has been check, I then display the “Other” Label and input field. The problem I had, was because there were multiple “Other” checkboxes, they all have different “id“s, and this is a problem because I needed to handle the different ids with one function.

Original Function

Here is the original function before code-refactoring showing how I get the elements and check if the Other checkbox is checked and then change the CSS styling.

function changeEmploy(){
   // Get elements.
   var employOther = document.getElementById( "ind_needs_assess_employ_9" );
   var otherLabel = document.getElementById( "employ_other_label" );
   var otherInput = document.getElementById( "ind_needs_assess_employ_other" );

   // Check if Other is selected.
   if (employOther.checked){
      otherLabel.style.display = "inline";
      otherInput.style.display = "inline";
   }
   else{
      otherLabel.style.display = "none";
      otherInput.style.display = "none";
   }
}

The if statement simply checks if the checkbox is checked, then I will set the CSS display value for the Other Label and input to either “inline” to show them, or “none” to make them hidden.

Code-Refactoring

Since I had a total of seven (7) of the “Other” checkboxes on my entire form, it didn’t make sense to have seven similar functions, that essentially do the same thing. So I decided to code-refactor and make one main function and pass in the id values for the elements in an Array.

The simplified code is like so:

function handleCheckbox( values ){
   // Get elements.
   var checkbox = document.getElementById( values[0] );
   var otherLabel = document.getElementById( values[1] );
   var otherInput = document.getElementById( values[2] );

   // Check if Other is selected.
   if (checkbox.checked){
      otherLabel.style.display = "inline";
      otherInput.style.display = "inline";
   }
   else{
      otherLabel.style.display = "none";
      otherInput.style.display = "none";
   }
}

In this case the values parameter is an Array passed in to the function. Then my “changeEmploy” function then becomes:

function changeEmploy(){
   var values = new Array(
      "ind_needs_assess_employ_9",
      "employ_other_label",
      "ind_needs_assess_employ_other"
   );

   handleCheckbox( values );
}

This is much simpler, and I can re-use the “handleCheckbox” function with my function “changePersonal” function like so:

function changePersonal(){
   var values = new Array(
      "ind_needs_assess_personal_5",
      "pers_other_label",
      "ind_needs_assess_pers_other"
   );

   handleCheckbox( values );
}

So I simply pass in an Array with the element ids as a parameter to the “handleCheckbox” function.

End Result

This now simplifies my code, makes my code more readable, and also more supportable and maintainable. Hopefully this helps someone.

Dynamically Formatting Phone Field with Javascript

Introduction

On a Symfony project I’m working on, I have two phone (one mobile) text input fields on a form that I would prefer to display to the user with the format “(888) 555-1000”, but yet allow the user to just input numbers. I accomplished this with Javascript, and this blog describes how I did it.

Symfony Form

In my controller as part of my “createFormBuilder” I added the following TextType built-in Field Type code:

->add('phone', TextType::class, array(
      'label' => 'Phone #:',
      'attr' => array(
            'placeholder' => '(xxx) xxx-xxxx',
            'size' => 13,
      ),
 ))

What this looks like in HTML is like so:  phonefield

Which displays how the format would look after fully entering the number. The way I wanted it to work, is the user clicks on the field, it automatically adds the first bracket, and then as then continuously enter in digits, the closing bracket / space / and dash are added.

Using JSFiddle to Experiment

Javascript is a terrible language to work with. It is easy to make mistakes, and there is very little feedback to tell you what might be wrong. So often you have to use the browser debugging tools to figure out why your script doesn’t work. Which is what I had to do for this particular script.

Fortunately you can use http://jsfiddle.net as a tool to create your html, css, and Javascript to fully test out your Javascript in a test environment to see if it works before deploying. Although, if you are new to JSFiddle, you will need to get familiar with it.

Here is a link to my public sample of this code: https://jsfiddle.net/alvinbunk/4bg6z1do/8/

Twig Code

In order to call each of the Javascript functions, I had to add attributes to my Twig code for the form rending of my TextType widgets. I also set the maximum length so that when the user enters more than 14 digits, nothing will happen (this is good).

Here is a sample of my Twig code:

<td>{{ form_label(form.phone) }}
    {{ form_widget(form.phone,{'attr':{
    'onfocus':'focusPhonePrefix("form_phone")',
    'onblur':'blurPhonePrefix("form_phone")',
    'onkeypress':'keyPress(event,"form_phone")',
    'maxlength':14
    }}) }}</td>

 

Using Modern Javascript

I saw some online posts using KeyboardEvent.charCode, but that is deprecated and it is recommend to us KeyboardEven.key instead. It’s surprising how much bad code you can find out there that works but really is not current at all.

And example of using the event to get this code would be like this:

if (e.key !== "Backspace") {

Where “Backspace” is a Key value. There are a lot of Key values, so this can be very useful when making your code (reduces errors).

Hope this helps!

Modal Content in Twig

Introduction

Recently for a Petition System that I’m working on for Taft College, I thought it would be a good idea to add a modal dialog to show the particular reference petition that a Pass Along is using as the ‘approved’ petition. In other words, you click on “Show Approved Course” and up pops a modal (html) dialog that show the original ‘approved’ petition. I needed to do this in Twig, as part of Symfony3 framework. This blog describes what I did to achieve the modal dialog in Twig.

Twig File

The below is an abbreviated version of the content of the Twig file I used. It starts with a else statement as part of a larger conditional if check, and then also shows the inclusion of the appropriate Javascript file.


{% if json['Equivalency']['C-ID'] %}
...
{% else %}
	<span>Sub Type: Pass Along</span><br/>
	<span>Approved Course: {{ json['Equivalency']['PassAlong_Value'] }}</span>
	<button onclick="openModal()">Show Approved Course</button>
	<div id="myModal" class="modal">
		<!-- Modal content -->
		<div class="modal-content">
			<div class="close" onclick="closeModal()">✖</div>
			<iframe height="99%" width="97%"
			src="{{ path('showApproved',{'schoolID': pet.getCourse.getSchool.getSchoolId,
				'course': pet.getCourse.getCorTitle}) }}"></iframe>
		</div>
	</div>
{% endif %}
...
{% block javascripts %}
	{{ parent() }}
	...
	<script src="{{ asset('script/modal.js') }}"></script>
{% endblock %}

 

You’ll notice in the twig file that button tag uses the “onclick” to call “openModal()” Javascript function. This will bring up the modal dialog, and “closeModal()” closes the modal dialog.

Javascript File

Here is the Javascript file used:

// script/modal.js

document.addEventListener(&quot;keydown&quot;, escapeModal, false);

function openModal(){
	var modal = document.getElementById('myModal');
	modal.style.display = &quot;block&quot;;
}

function closeModal(){
	var modal = document.getElementById('myModal');
	modal.style.display = &quot;none&quot;;
}

function escapeModal(evt){
	var keyCode = evt.keyCode;
	
	if(keyCode == 27){	// Escape key.
		closeModal();
	}
}

The Javascript is quite simple, just add functions to add and close modal dialogs, and a function to be able to press the ‘Esc’ key to close the modal window.

CSS File

Here is the CSS file:

/* The Modal (background) */
.modal {
    display: none; /* Hidden by default */
    position: fixed; /* Stay in place */
    z-index: 1; /* Sit on top */
    padding-top: 100px; /* Location of the box */
    left: 0;
    top: 0;
    width: 100%; /* Full width */
    height: 100%; /* Full height */
    overflow: auto; /* Enable scroll if needed */
    background-color: rgb(0,0,0); /* Fallback color */
    background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}

/* Modal Content */
.modal-content {
    background-color: #fefefe;
    margin: auto;
    padding: 10px;
    border: 1px solid #888;
    width: 95%;
    height: 85%;
}

/* The Close Button */
.close {
    color: #aaaaaa;
    float: right;
    font-size: 28px;
    font-weight: bold;
}

.close:hover,
.close:focus {
    color: #000;
    text-decoration: none;
    cursor: pointer;
}

These changes simply add styling changes to make it appear like a modal dialog is appearing. The close class is used to handle clicking the “x” to close the modal window. Hover & focus is used to show the hand when moving over the “x” when attempting to close the modal window. This CSS values can be changed based on your needs.

Sample Screenshot

Below is a screenshot of what the modal dialog looks like.

modal_screenshot