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.

Rendering Semester & Year in Symfony Form

Introduction

In a form I’m working on recently, I needed to show the following info (see screenshot below) in a Symfony form:

render_semester_year

It’s from a paper form, and I’m converting to electronic. Since the semester is supposed to be circled, this means using a drop-down list allowing selecting only one item; and instead of filling in the year, have a drop-down list with a reasonable range of years.

Form Type Code

In Symfony, you normally create Form classes, and because the above dates don’t map back to an Entity attribute, I set ‘mapped’ to false in the FieldTypes that I use. So for the above, I used a ChoiceType for the semester, and a DateType for the year. I happen to show the form elements in a table, and because the elements need to appear on the same row, I need to use a CSS style of “float:left”. The resultant code looks something like this:

class ApplicationType extends AbstractType
{
   public function buildForm(FormBuilderInterface $builder, array $options)
   {
      $builder
         ...
         ->add('target', ChoiceType::class, array(
            'mapped' => false,
            'expanded' => false,
            'multiple' => false,
            'label' => 'Target final semester at Taft College: (If unsure, approximate.)',
            'choices' => array(
               'Fall' => 'fall',
               'Spring' => 'spring',
               'Summer' => 'summer',
            ),
            'attr' => array(
               'style' => 'float:left',
            ),
         ))
         ->add('target_date', DateType::class, array(
            'mapped' => false,
            'format' => 'dd MMM y',
            'label' => 'year:',
            'label_attr' => array(
               'id' => 'target_date',
               'style' => 'float:left;margin-left:8px',
            ),
            'years' => range(2025,2017,1),
            'attr' => array(
               'style' => 'float:left',
            ),
         ))
      ...
      ;
   }
}

You’ll notice that for the format option I had to use “dd MMM y”. If you use something like “yyyy” instead, you get this kind of Symfony exception error message:

The “format” option should contain the letters “y”, “M” and “d”. Its current value is “yyyy”.

So In other words, so you have to use each of year, month, and date placeholders for the format specification.

Presenting Properly in Twig

Unfortunately, the above code will also show the date and the month of the “target_date” form element. The best thing to do is hide both of them. This can be done by using CSS “display:none” again.

Here is the Twig code I used to render correctly:

<tr><td>{{ form_label(form.target) }}</td></tr>
   {{ form_widget(form.target_date['day'],{'attr':{'style':'display:none'}}) }}
   {{ form_widget(form.target_date['month'],{'attr':{'style':'display:none'}}) }}
<tr><td>{{ form_widget(form.target) }}
   {{ form_label(form.target_date) }}{{ form_widget(form.target_date['year']) }}</td></tr>

You’ll notice in the above code, I rended the “target” label in one row, then I hide the day and month “target_date” widgets by setting each of their attributes, and then on another row I show the “target” widget and then the “target_date” label followed by only the “target_date” year. This effectively shows the semester and year side by side.

The result is like in the following screenshot:

result

 

Symfony Form Rendering Woes

Introduction

Recently for Taft College, I’ve been working on a TRIO program application which is built with Symfony PHP framework. I have various forms that needs to be filled out and submitted by students, and this post is about how I had rending issues of one of the forms and how I solve the problem.

The Form

In one of the forms I created, I used a Form class to create the form. On the form, there is a checkbox that when selected needs to show a date selection. There are two checkboxes by the High School area, namely “Diploma” and “G.E.D.” test. Below is a sample of the code I started with:

->add('hs_diploma', ChoiceType::class, array(
   'mapped' => false,
   'expanded' => true,
   'multiple' => true,
   'choices' => array(
      'Diploma' => 'diploma',
   ),
))
->add('hs_dip_date', DateType::class, array(
   'mapped' => false,
   'format' => 'dd MMM y',
   'label' => 'date:',
   'years' => range(2017,1960,1),
))
->add('ged', ChoiceType::class, array(
   'mapped' => false,
   'expanded' => true,
   'multiple' => true,
   'choices' => array(
      'G.E.D.' => 'ged_checked',
   ),
))
->add('ged_date', DateType::class, array(
   'mapped' => false,
   'format' => 'dd MMM y',
   'label' => 'date:',
   'years' => range(2017,1960,1),
))

And here is what the rendering looks like:

1st_render

The elements are in a html table and notice the way that the date label appears on one row and on the next row is the date picker, followed by the G.E.D. checkbox (and label and date picker). So the problem is that I need both of the checkboxes, labels and date pickers to all appear in one row.

Fixing in Twig?

Then my next thought would be could this be fixed in the Twig template? Twig is a great templating engine which allows you to customize the form rendering using certain markup. I figured I could customize it by specifying in this format:

Diploma checkbox widget:Diploma date label:Diploma date picker widget:GED checkbox widget:GED date label:GED date picker widget

All the above in one html table data () element, within a table row (

). This would look like the following code in Twig:

<tr><td>{{ form_widget(form.hs_diploma) }}{{ form_label(form.hs_dip_date) }}
 {{ form_widget(form.hs_dip_date) }}{{ form_widget(form.ged) }}{{ form_label(form.ged_date) }}
 {{ form_widget(form.ged_date) }}</td></tr>

However this doesn’t even change the way it renders as in the image above. I used FireFox browser tools and right-clicked on the table element and selected Inspect Element, which shows that within the element, each checkbox and date picker is with a

tag and the labels are outside of the div tags. This is why they don’t render properly.

So the end answer to my question is: It would be possible to fix this in Twig, but it would be a lot of code, and I was looking for an easier way.

Setting Attributes of Symfony Field Types

Symfony has a number of different Field Types, and in my form I use ChoiceType and DateType (as per the above code). I also use other types (of course), but in this blog I only will reference changes to these types.

In FireFox, I experimented with the browser tools by using the Inspector tab and adjusting the style attribute of the

tags, and then I found that if I used style=”float:left”, this fixed the problem of rendering on different rows in the table.

This is the code I used properly render the checkboxes:

->add('hs_diploma', ChoiceType::class, array(
   'mapped' => false,
   'expanded' => true,
   'multiple' => true,
   'choices' => array(
      'Diploma' => 'diploma',
   ),
   'attr' => array(
      'style' => 'float:left',
   ),
))

Notice that I add an attribute of style and set to “float:left”. Then for a date picker I use the following code:

->add('hs_dip_date', DateType::class, array(
   'mapped' => false,
   'format' => 'dd MMM y',
   'label' => 'date:',
   'label_attr' => array(
      'style' => 'float:left;margin-left:8px',
   ),
   'years' => range(2017,1960,1),
   'attr' => array(
      'style' => 'float:left',
   ),
))

Notice there are attributes for both the label and the date picker. Both attributes are needed. Notice also for the label_attr, I added a “margin-left:8px”; this was to add a space between (for example) Diploma and the label date.

Below is a render of what the changes look like after adding the styling changes:

2nd_render

Notice everything all appears on one row exactly as I need it.

Since I set the attributes in the form class, it’s very easy to support, since I merely only need to change the class file should I need to have to make any changes. I should mention that this would really be a lot of work if I had to make the changes in Twig; so I highly recommend you use the field type attributes when it is possible.

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