Bootstrap Modals


Bootstrap Modals with PHP?

Bootstrap has, through the use of "modals", the ability to pop up dialogs for a variety of uses, on mouse clicks (on buttons, anchor tags, etc.). You can see all the official documentation by the folk at Twitter here: [Bootstrap Modals].

While Bootstrap modals are really rather cool looking, unfortunately they're designed to work with JavaScript and are a front-end tool, not really all that useful for working with PHP. Among other things, a Bootstrap Modal does not stop processing any code, which to a Windows developer this is kind of misleading. I normally expect a modal dialog to halt everything until the dialog is closed.

That said, I have found a few places they can be handy. They are tricky to work with, and if my whole app were JavaScript (using JSON or something for back-end processing, etc.) I might use them more.

What I found useful is with a web page with a lot of information, a modal can be useful to bring up more information. One example is with a user name. If you have a user profile set up for your users, you can display the user name, and then when the user clicks on the name, have more information appear. This is what I will focus on here. There are also (or will be) two places I discuss image galleries here, I found that I could use the Bootstrap modal forms to give me a nice popup for larger images, too. That will be discussed elsewhere on this site, however.

To make this work you need a combination of HTML, CSS and JavaScript. Combined with the PHP code to output the information, you can do some interesting things.

The HTML

The first thing you need is the HTML necessary to display the modal form:

               <div id="userModal" class="modal fade" tabindex="-1" role="dialog">
                  <div class="modal-dialog modal-lg" role="document">
                     <div class="modal-content">
                        <div class="modal-header">
                           <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
                           <h3 class="modal-title" id="userModalLabel"></h3>
                        </div> <!-- / modal-header -->
                        <div class="modal-body">
                           <!-- some text will be inserted here -->
                        </div> <!-- / modal-body -->
                        <div class="modal-footer">
                           <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                        </div> <!-- / modal-footer -->
                     </div> <!-- / modal-content -->
                  </div> <!-- /modal-dialog -->
               </div> <!--/ modal -->
               <!-- end of modal form -->
            

If you were to run code to open this modal dialog, it would be really boring looking. But there we are.

In my CMS that I use this with, I keep the modal form in its' own file, and include it after I load the header, so the form is stored at the top of the document:

               <?php include( $sRootPath . "forms/modals/user_profile.php" ); ?>
            

The other part of the HTML aspect is that you have to output the user name as an anchor tag (a link), with some specific attributes that are then used with the JavaScript (below) to load the information into the form.

               <a href="" class="userprof"
                  data-title="User Name: ken" data-body="Whatever we want to display in the body"
                  data-toggle="modal" data-target="#userModal" >
                  ken
               </a>
            

That doesn't look like it does a lot, and honestly, this is a very simplified version of what I'm actually doing -- I have a bunch of PHP code that looks at the user profile, determines if there is a profile image, a first and last name, an email address, and more, and builds a strong for the title and another string for the body, storing the information in variables. We'll come back to that.

The Styles (CSS)

There is also a bit of CSS that defines the appearance of the username, in this case it is pretty simple (stored in my primary CSS file):

               .userprof
               {
                  border-bottom: thin dotted;
                  text-decoration: none;
                  color: black;
               }
               .userprof:hover,
               .userprof:visited,
               .userprof:hover,
               .userprof:focus
               {
                  text-decoration: none;
               }
            

Turning off the text-decoration (default of underline, etc.) means that the text doesn't get the standard underline when hovered over, etc.

The JavaScript

You need to have some code that will actually open the dialog, and place information into it that you need. This gets tricky because it is a combination of different things going on. This is where the Bootsrap documentation starts to fail rather badly. I got a lot of assistance when I started working with this from some developers on Stack Overflow.

I found that there are some sticky issues with these, mostly because you are using one form, possibly for multiple users (in this case). The default method in JavaScript of assigning the a value to part of a Bootstrap modal is text(), but this does not evaluate HTML, and what comes out is a string with any embedded HTML looking like HTML tags, not displaying bold text if you use a strong tag, etc. There is another method called append() which strangly does evaluate the HTML properly, and your tags do what you expect them to do.

What is tricky about that, is if you open the modal dialog once, everything is fine. If you open it again the append method does exactly what its name implies, it appends the information to what was already in memory for that part of the dialog.

Clearly, that is a problem. So, what you need to be able to do is when the dialog closes, empty out the text that is there (oddly, using the text() method), and then when it opens again you append new text to "nothing", and all is well.

The JavaScript, which works great, looks like this:

               // code to open the modal with the caption and description:
               $('#userModal').on('show.bs.modal', function (event)
               {
                  var button = $(event.relatedTarget); // Button that triggered the modal
                  var title = button.data('title'); // Extract info from data-* attributes
                  var body = button.data('body'); // Extract info from data-* attributes
                  var modal = $(this);
                  modal.find('.modal-title').text( title );
                  modal.find('.modal-body').append( body );
               });
               
               // when modal closes, clear out the body:
               $('#userModal').on('hidden.bs.modal', function ()
               {
                   $(this).find(".modal-body").text('');
               });
            

The code shown above uses code in the Bootstrap Javascript file that is loaded when you set up your website to use Bootstrap. The data() method looks in the HTML tag for the object clicked for anything that starts with "data-", and then you pass what you want. In the code above we are looking for "data-title" and "data-body".

If you examine the HTML for the anchor tag, you will see there are these two attributes. The code assigns the values to variables, and then uses two other methods to assign values: text() and append().

modal.find() is looking for specific classes in the modal dialog, in this case "modal-title" and "modal-body". The text() method overwrites whatever is in the class. Interestingly, the modal form uses an H3 tag with the modal-title class. This means that whatever is placed in the tag defined by the class, will be formatted however H3 is defined. I left this as the default Bootstrap H3 tag, and it looks pretty good in the title of the dialog.

For the body, we're using a div tag, and the append() method to place the contents in there. We use this method because it actually evaluates the HTML in the text passed to it, where the text() method does not. The fun part of that is as noted elsewhere on this page, append does exactly what it sounds like. If there is text already there (and opening the modal and closing it leaves text in memory ...), we add to the end of it. Note: there is code that executes when the form is closed (on('hidden.bs.modal')) that uses the text() method to blank out the text displayed in the modal-body div tag. That way when the form opens again, the append() method is appending to an empty string, and we don't end up with two profile images, and two sets of text and ...

Similar to the file that contains the modal form, I also include the JavaScript file only as needed, at the bottom of the page, before the </body> tags:

               <script src="<?php echo $sRootPath ?>jscode/userprofile_openmodal.js"></script>
            

Putting It Together

All of these elements come together to make this work. Using the simple example from above, we might see something like:

This is some text written by user: ken

A quick note, to center the modal dialog (which by default opens at the top of the page) there is some more CSS, some of the style code below is also to do better formatting of the text in the dialog:

               /* both of the next two -- center modal vertical */
               #userModal .modal-dialog
               {
                  overflow-y: initial !important;
                  display: inline-block;
                  text-align: left;
                  vertical-align: middle;
               }
               
               #userModal.modal:before
               {
                 content: '';
                 display: inline-block;
                 height: 100%;
                 vertical-align: middle;
                 margin-right: -4px; /* Adjusts for spacing */
               }
               
               /* center modal horizontal */
               #userModal.modal
               {
                 text-align: center;
                 padding: 0!important;
               }
               
               #userModal .modal-body
               {
                  max-height: 700px;
                  overflow-y: auto;
               }
               
               .modal_text
               {
                  overflow-y: auto;
               }
               
               /* items in the modal form */
               .modal_profile_image
               {
                  max-width: 200px;
                  max-height: 300px;
                  vertical-align: text-top;
                  padding-right: 10px;
                  padding-bottom: 10px;
                  float: left;
               }
               .modal_profile_name
               {
                  font-weight: bold;
               }
            

Output via PHP

The trickiest part of all this is getting it to work via PHP -- pulling data out of the tables, creating the strings that actually might appear in the dialog, and so on.

The following is an extract of a larger set of code, that assumes you have found the user profile record for the individual, and are building the strings used for inserting the title and body into the form. Note that the delimiters (quotes and apostrophes) are very carefully laid out here:

               // build all the data that goes into the modal form here:
               $modal_title = "User: " . $user_row['user_name'];

               // define the content for the modal dialog based on
               // the user's profile. The body will be the tricksy part:
               $modal_body = ""; // empty it out:
               
               // first let's see about an image:
               if( $up_row['profile_image'] != "" )
               {
                  $modal_body .= "<img src='" . $sRootPath . "profileimages/" . $up_row['profile_image'] . "' class='modal_profile_image' alt='Profile Image' />";
               }
               // then we check for other fields:
               if( $up_row['firstname'] != "" && $up_row['lastname'] != "" )
               {
                  $modal_body .= "<div class='modal_profile_name'>" . $up_row['firstname'] . " " . $up_row['lastname'] . "</div>";
               }
               if( $up_row['gender'] != "" && $up_row['gender'] != "Decline" )
               {
                  $modal_body .= "<div class='modal_gender'><strong>Identifies as:</strong> " . $up_row['gender'] . "</div>";
               }
               if( $up_row['allow_contact'] == 1 )
               {
                  $modal_body .= "<div class='modal_email'><strong>Email: </strong><a href='mailto:" . $user_row['email'] . "'>" . $user_row['email'] . "</a></div>";
               }
               if( $up_row['biography'] != "" )
               {
                  $modal_body .= "<div class='modal_bio'><strong>Biography:</strong> " . htmlspecialchars_decode( $up_row['biography'] ) . "</div>";
               }
               
               // Output the user name with the variables inserted into the attributes:
               echo '<a href="" class="userprof"
                  data-title="' . $modal_title . '" data-body="' . $modal_body . '"
                  data-toggle="modal" data-target="#userModal"
                  >' . $user_row['user_name'] .
                  '</a>';
            

If you read it carefully you will see there are two variables for rows, "up_row" is the user profile, and user_row is the main table of users. I have these split out into a 1 to 1 relationship, so there can only be one profile record for each user record. I allow the user to choose what information to provide.

Note that this uses a profile image as part of the display, which is fun. Based on the above, using data out of my test database when I built the thing, you could end up with a form that looks really interesting. I built the strings rather than making this read data out of the tables (which would mean providing them), but ...

Document created by (user): ken

(click the user name)

That's About It!

And frankly, isn't that enough? Actually, for my CMS, there is a bit more going on with the user name, I also have a Bootstrap Popover defined that shows the users' profile image and their first and last names only. But that's a different topic ... combining them made me a little crazy, too.

For my CMS the code to generate the information is stored in a PHP function file, which gets "included" as needed. I don't use it in all places in the site, but mostly the front-end. The Administrator doesn't really need to see all that. But it can be used in comments (if allowed on the site), and more. I'm pretty pleased with the way this turned out. If you choose to use it, be sure to throw some credit my way (a link in your source code, whatever ...).

The "dummy" text is taken from one of the Ipsum Lorem clones, called [Sagan Ipsum].