Concrete5

JavaScript, jQuery and concrete5

concrete5's rich user interface is one of the things that sets it apart from some of its open source competitors. With its emphasis on in-context editing, overlays, slide-down panes, AJAX form submission and the ability to add large amounts of content (including files) without ever leaving the page, concrete5 frequently behaves more like a desktop application than a typical web application. In this how-to, I'm going to show you how you can integrate a wide variety of rich JavaScript functionality into the front-end of your applications and blocks.

Note: this isn't meant to be the one tutorial to rule them all: this is merely an overview to show you some of the ways concrete5 integrates and works well with JavaScript, how you can start exploring further.

jQuery: concrete5's JavaScript Framework

Much of concrete5's interaction model would simply not be possible without jQuery. One of the most popular extensions to JavaScript available, jQuery relieves much of the incompatibility and verbosity that has typically plagued the scripting of complex web applications.

We didn't always use an intermediary JavaScript framework (and – gulp – for a little while we flirted with Prototype), but jQuery makes so much about complex scripting easier than it used to be. And even better: jQuery's plugin model has been used to create great additions to the core library, including effects libraries and new widgets. Several of these plugins are also included in concrete5 core.

An Overview of All jQuery Components Included in concrete 5.4

The following jQuery libraries included with concrete 5.4:

  • jQuery Core (1.4)
  • jQuery UI (1.7.2)
  • jQuery Scroll-To (1.3.3)
  • jQuery Rating (3.0)
  • jQuery Metadata
  • jQuery LiveUpdate
  • jQuery Form (2.12)
  • jQuery Cookie
  • jQuery Color Picker
  • jQuery UI

The following jQuery UI components are included in jquery.ui.js.

  • jQuery UI Draggable
  • jQuery UI Droppable
  • jQuery UI Selectable
  • jQuery UI Sortable
  • jQuery UI Accordion
  • jQuery UI Slider
  • jQuery UI Progressbar
  • jQuery UI Effects
  • jQuery UI Datepicker

How to include jQuery and its Components in your Block or Single Page

Single Pages

Including jQuery in your single pages or pages of a certain type is as easy as calling addHeaderItem in your page's controller method. Let's say you have a page accessed at

http://www.yoursite.com/index.php/your_special_page/

That means you have a controller here at

controllers/your_special_page.php

or

controllers/your_special_page/controller.php

And within that controller file, in your view() method, the following will include the jQuery UI library, its required stylesheet, the concrete5 dialog class (more on this in a moment) and its required stylesheet.

public function view() {
   $html = Loader::helper('html');
   $this->addHeaderItem($html->css('jquery.ui.css'));
   $this->addHeaderItem($html->css('ccm.dialog.css'));
   $this->addHeaderItem($html->javascript('jquery.ui.js'));
   $this->addHeaderItem($html->javascript('ccm.dialog.js'));
}

The HtmlHelper class takes care of locating the correct file and printing out the proper tag. The Controller::addHeaderItem() function makes sure only one instance of a given header item is added, and is properly output in the HEAD tag of the page.

Blocks

Calling CSS and JavaScript in your blocks is even easier. If you'd like a particular block's view layer to require certain JavaScript and CSS, simply add it to the block controller's on_page_view() function:

public function on_page_view() {
  $html = Loader::helper('html');
  $this->addHeaderItem($html->css('jquery.ui.css'));
  $this->addHeaderItem($html->css('ccm.dialog.css'));
  $this->addHeaderItem($html->javascript('jquery.ui.js'));
  $this->addHeaderItem($html->javascript('ccm.dialog.js'));
}

Note: this is not the block's view() function. on_page_view() runs before the page is rendered, ensuring that the CSS and JavaScript files are added in the HEAD tag.

Once jQuery and jQuery UI are included in your page (and jQuery is typically included in themes by default) you'll be able to use all the great functionality described at jQuery.com.

How to use the built-in concrete5 Dialog Class

The built-in concrete5 dialog class is responsible for rendering all modal and non-modal dialog windows (not the slide-downs) that are positioned in the middle of the screen. This library is not the same as the jQuery UI dialog class. It was created before jQuery UI Dialog, and as such, currently conflicts with it.

Example 1: Loading an external url into a dialog using on-link attributes

The easiest way of calling a dialog window is by specifying certain attributes in the link you wish to use to call the window, and then calling the .dialog() function on that link.

<a 
dialog-title="This is the dialog title." 
   dialog-modal="false"
   dialog-width="550" 
   dialog-height="380" 
   id="myDialog" 
   href="yourpage.php">This is the link</a>

Now, run the dialog function:

<script type="text/javascript">
   $(function() {
      $("#myDialog").dialog();
   });
</script>

This will load yourpage.php via AJAX, create a new 550x380 dialog window with the proper title, make it non-modal, and load the content into it.

Example 2: Loading an external url into a dialog from within a JavaScript function

The same operation above can be run from within a JavaScript function

<script type="text/javascript">
loadMyDialog = function() {
   jQuery.fn.dialog.open({
      title: 'This is my title',
      href: 'yourpage.php',
      width: 550,
      modal: false,
      height: 380,
      onClose: function() {
         alert('This will fire when the dialog is closed.');
      }
   });
}
</script>

Simply run loadMyDialog() and the proper dialog will open. Note the "onClose" function that will run when the dialog is closed.

Example 3: Loading the contents of an on-page element into a dialog from within a JavaScript function

If you'd rather load a dialog and populate it with in-page content, you can do so using the syntax below:

<div id="myDialogContent" style="display: none">
   This is the content of my dialog window.
</div>

<script type="text/javascript">
loadMyDialog = function() {
   jQuery.fn.dialog.open({
      title: 'This is my title',
      element: '#myDialogContent',
      width: 550,
      modal: false,
      height: 380,
      onClose: function() {
         alert('This will fire when the dialog is closed.');
      }
   });
}
</script>

AJAX

If you want to use jQuery to integrate some server-side processing into concrete5 (AJAX), it can be a little difficult to know where to start. There are basically two approaches available: 1) use a controller action and then exit from the action before rendering the page, or 2) use a tool script.

What's a tool script? A tool script is a simple PHP script either in your local tools/ directory or your block's tools/ directory. This script has no UI wrapped around it. It's lightweight, and simply loads a number of concrete5 objects and connects to the database. Otherwise, you're free to do what you wish inside it.

Using an Action for Server-Side Processing in a Block

From within the block's view layer, make the HREF of the link you wish to tie to the backend equal the value of an action method in your controller:

$action = $this->action('like');

This will create a link that will pass through to the block when the form is submitted, and then "action_like" action will be run.

Now, in HTML, we create the link:

<a href="<?=$action?>" id="myActionLink">Like!</a>

Now, in JavaScript, we make it so that the link in question submits to the backend:

$('#myActionLink').click(function() {        
   var url = $(this).attr('href');
   $.get(url, function(r) {
      alert('Success!');
   });
   return false;
});

Finally, the action_like() method in the block's controller processes, and then exits at the end.

Tools

Tools are another way to enable server-side processing. Using tools outside the block level is easy. For example, the JavaScript function ccm_dashboardRequestRemoteInformation runs every time the front page of the dashboard is hit:

ccm_dashboardRequestRemoteInformation = function() {
   $.get(CCM_TOOLS_PATH + '/dashboard/get_remote_information');
}

This simply loads the "dashboard/get_remote_information.php" tool through AJAX. The tools path takes care of making sure the script in the correct location is loaded.

Loading a block-specific tool is nearly as easy. From within a block's view layer, the following will automatically load information from a tool found in the block's tools/ directory:

<?php
$th = Loader::helper('concrete/urls'); 
$bt = $b->getBlockTypeObject();
?>

<script type="text/javascript">
$(function() {
   $.get('<?=$th->getBlockTypeToolsURL($bt)?>/load_information?>');
});
</script>

This will automatically find the "load_information.php" script within the block's tools directory, and load it silently. You can pass query string arguments to the script to obtain more functionality, and process them silently from within the script.

JSON

If you'd like to return JSON to your AJAX function, it's as easy as defining a simple PHP object within your tool or action function, instantiating the properties you want to pass back, and using the concrete5 JSON helper.

$obj = new stdClass;
$obj->favoriteColor = 'red';
$obj->name = 'Andrew';
$obj->isAwesome = true;
$js = Loader::helper('json');
print $js->encode($obj);
exit;

This will print out

{"favoriteColor":"red","name":"Andrew","isAwesome":true}

and exit, leaving you to use jQuery's $.getJSON function to parse the string into JavaScript objects that you can do with what you will.

Loading Conversation