J3.x

J3.x:Developing an MVC Component/Adding a Modal

From Joomla! Documentation

Revision as of 22:23, 7 June 2018 by Robbiej (talk | contribs) (input fields descriptions)
Joomla! 
3.x
Tutorial
Developing an MVC Component



This is a multiple-article series of tutorials on how to develop a Model-View-Controller Component for Joomla! VersionJoomla 3.x.

Begin with the Introduction, and navigate the articles in this series by using the navigation button at the bottom or the box to the right (the Articles in this series).



This tutorial is part of the Developing an MVC Component for Joomla! 3.2 tutorial. You are encouraged to read the previous parts of the tutorial before reading this.

In this step we add a modal.

Under Construction!

Introduction

Modals, or modal windows, are the pop-up-like windows which appear in several places within Joomla. Examples are:

  • When as an administrator you create a new menu item, and select a Menu Item Type of Articles / Single Article, then when you click to select the article, a pop-up appears displaying the details of the articles available and allowing you to select one.
  • If you enabled the Administrator Multilanguage Status module as part of making your site multilingual in the previous step, when when you click on the "Multilingual Status" button at the bottom left of each admin page, a pop-up appears which shows the status of the multilingual functionality on your site.

Of course, these modals aren't really browser pop-up windows - users often disable browser pop-up windows - but rather use CSS and Javascript to give the appearance of pop-ups, and Joomla currently uses the Bootstrap Modal framework. The HTML elements which comprise the modal are already within the HTML of the page, but hidden. When you click on an appropriate button (such as the Select button to select an article in the first example above), then the javascript code behind the button click does the following:

  • the HTML hidden elements of the modal are made visible
  • a new div element covering the whole of the browser window is created; this backdrop is coloured black, and has partial opacity, so this gives the appearance of greying out what's behind the modal
  • in the CSS the z-index is defined so that the modal appears above the backdrop.

When you close the modal by selecting eg an article, or by clicking the close button, then the HTML elements of the modal are made hidden once more, the backdrop div is removed and any item you selected is passed by javascript to the appropriate field on the main form.

Each modal consists of 3 parts:

  • a modal header at the top, with the title and an X button to close the modal,
  • a modal body, where the main information appears, and,
  • a modal footer, with buttons such as Close, Save, etc, depending upon the context.

In general within Joomla the modal body is an iframe element, and hence is an embedded HTML page within the overall page, and HTTP requests can be made to obtain the data to display in the iframe, filter and sort it, and so on.

Functionality

Currently within our helloworld component we have the ability to create a new menu item with a menu item type of "hello world" which will display a single helloworld message on our site. However, in choosing the message to display we have only a simple list field, and we have to scroll down the list of greetings to select the appropriate one.

In this step we change the user interface so that when selecting the helloworld message for a new menu item, the administrator is presented with a modal with all the details of the helloworld records, similar to what is shown when the administrator clicks on Components / Hello World. In this way the administrator is presented with a much nicer interface, with ordering, search and filter functionality.

Approach

The definition of what is displayed when an admin selects a menuitem of type Hello World is defined in the xml file of the site helloworld layout file, ie site/views/helloworld/tmpl/default.xml. We need to change the field definition in that file to be a custom field type which we'll call modal_helloworld.

We need to provide Joomla with the code for our custom field, and we'll provide a new file to generate the html to display our custom input field.

Finally we need to consider the display of the helloworld records within our modal. We'll use a presentation similar to that shown when the administrator navigates to Components / Hello world, so we'll reuse our helloworlds view, but we'll generate a new layout file instead of our current admin views/helloworlds/tmpl/default.php.

Helloworld menuitem definition

We change the custom field for selecting the Helloworld record to be displayed.

site/views/helloworld/tmpl/default.xml

<?xml version="1.0" encoding="utf-8"?>
<metadata>
	<layout title="COM_HELLOWORLD_HELLOWORLD_VIEW_DEFAULT_TITLE">
		<message>COM_HELLOWORLD_HELLOWORLD_VIEW_DEFAULT_DESC</message>
	</layout>
	<fields
			name="request"
			addfieldpath="/administrator/components/com_helloworld/models/fields"
			>
		<fieldset name="request">
			<field
					name="id"
					type="modal_helloworld"
					required="true"
					label="COM_HELLOWORLD_HELLOWORLD_FIELD_GREETING_LABEL"
					description="COM_HELLOWORLD_HELLOWORLD_FIELD_GREETING_DESC"
					/>
		</fieldset>
	</fields>
</metadata>

Field definition

The field definition for our new custom field goes into the admin/models/fields directory. Because the field type is of the form modal_helloworld, with an underscore in between, Joomla will look in a /modal subdirectory for a file helloworld.php. Hence our new custom field is defined in

admin/models/fields/modal/helloworld/php

<?php

defined('JPATH_BASE') or die;

/**
 * Supports a modal for selecting a helloworld record
 *
 */
class JFormFieldModal_Helloworld extends JFormField
{
	/**
	 * Method to get the html for the input field.
	 *
	 * @return  string  The field input html.
	 */
	protected function getInput()
	{
		// Load language
		JFactory::getLanguage()->load('com_helloworld', JPATH_ADMINISTRATOR);

		// $this->value is set if there's a default id specified in the xml file
		$value = (int) $this->value > 0 ? (int) $this->value : '';
        
		// $this->id will be jform_request_xxx where xxx is the name of the field in the xml file
		$modalId = 'Helloworld_' . $this->id;

		// Add the modal field script to the document head.
		JHtml::_('jquery.framework');
		JHtml::_('script', 'system/modal-fields.js', array('version' => 'auto', 'relative' => true));

		// our callback function from the modal to the main window:
		JFactory::getDocument()->addScriptDeclaration("
			function jSelectHelloworld_" . $this->id . "(id, title, catid, object, url, language) {
			window.processModalSelect('Helloworld', '" . $this->id . "', id, title, catid, object, url, language);
			}
			");

		// if a default id is set, then get the corresponding greeting to display it
		if ($value)
		{
			$db    = JFactory::getDbo();
			$query = $db->getQuery(true)
				->select($db->quoteName('greeting'))
				->from($db->quoteName('#__helloworld'))
				->where($db->quoteName('id') . ' = ' . (int) $value);
			$db->setQuery($query);

			try
			{
				$title = $db->loadResult();
			}
			catch (RuntimeException $e)
			{
				JError::raiseWarning(500, $e->getMessage());
			}
		}
        
		// display the default greeting or "Select" if no default specified
		$title = empty($title) ? JText::_('COM_CONTENT_SELECT_A_HELLOWORLD_GREETING') : htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
		$html  = '<span class="input-append">';
		$html .= '<input class="input-medium" id="' . $this->id . '_name" type="text" value="' . $title . '" disabled="disabled" size="35" />';

		// html for the Select button
		$html .= '<a'
			. ' class="btn hasTooltip' . ($value ? ' hidden' : '') . '"'
			. ' id="' . $this->id . '_select"'
			. ' data-toggle="modal"'
			. ' role="button"'
			. ' href="#ModalSelect' . $modalId . '"'
			. ' title="' . JHtml::tooltipText('COM_CONTENT_SELECT_HELLOWORLD_MODAL_TITLE') . '">'
			. '<span class="icon-file" aria-hidden="true"></span> ' . JText::_('JSELECT')
			. '</a>';

		// html for the Clear button
		$html .= '<a'
			. ' class="btn' . ($value ? '' : ' hidden') . '"'
			. ' id="' . $this->id . '_clear"'
			. ' href="#"'
			. ' onclick="window.processModalParent(\'' . $this->id . '\'); return false;">'
			. '<span class="icon-remove" aria-hidden="true"></span>' . JText::_('JCLEAR')
			. '</a>';

		$html .= '</span>';

		// url for the iframe
		$urlSelect = 'index.php?option=com_helloworld&amp;view=helloworlds&amp;layout=modal&amp;' . Session::getFormToken() . '=1&amp;function=jSelectHelloworld_' . $this->id;
        
		// title to go in the modal header
		$modalTitle    = JText::_('COM_CONTENT_SELECT_HELLOWORLD_MODAL_TITLE');
        
		// html to set up the modal iframe
		$html .= JHtml::_(
			'bootstrap.renderModal',
			'ModalSelect' . $modalId,
			array(
				'title'       => $modalTitle,
				'url'         => $urlSelect,
				'height'      => '400px',
				'width'       => '800px',
				'bodyHeight'  => '70',
				'modalWidth'  => '80',
				'footer'      => '<a role="button" class="btn" data-dismiss="modal" aria-hidden="true">' . JText::_('JLIB_HTML_BEHAVIOR_CLOSE') . '</a>',
			)
		);

		// class='required' for client side validation.
		$class = $this->required ? ' class="required modal-value"' : '';

		// hidden input field to store the helloworld record id
		$html .= '<input type="hidden" id="' . $this->id . '_id" ' . $class 
			. ' data-required="' . (int) $this->required . '" name="' . $this->name
			. '" data-text="' . htmlspecialchars(JText::_('COM_CONTENT_SELECT_A_HELLOWORLD_GREETING', true), ENT_COMPAT, 'UTF-8') 
			. '" value="' . $value . '" />';

		return $html;
	}

	/**
	 * Method to get the html for the label field.
	 *
	 * @return  string  The field label html.
	 */
	protected function getLabel()
	{
		return str_replace($this->id, $this->id . '_id', parent::getLabel());
	}
}

When the field definition class extends JFormField, Joomla expects 2 methods to be present in the class: getInput() which should return the html for the input field elements and getLabel() which should return the html for the label.

There are several core Joomla components which use modal fields to allow the administrator to select items, including com_content and com_contact. These components have similar field definitions, and the file above is a cut-down version of these equivalents, just supporting the functionality which we need for this tutorial step.

There are a number of key html input elements returned by the getInput() method above.

  1. The input field which displays the current greeting selected, or the text "Select" if none has been selected
  2. The Select button - this appears if no greeting has been selected. Note the attribute data-toggle="modal" indicating that clicking on this button will cause the modal to appear
  3. The Clear button - this appears if a greeting has been selected. When clicked it runs a javascript function within the system/modal-fields.js code which replaces the selected greeting the "Select" text, makes visible the Select button, and hides the Clear button
  4. The modal elements - these are output through including sections of the Bootstrap html and javascript code. When the modal appears the iframe is created and populated through an HTTP GET to the URL in $urlSelect.
  5. A hidden field which stores the id of the helloworld record selected, and which will be used to pass it in the HTTP POST parameters when the form is submitted.

In our case the callback function will be called jSelectHelloworld_jform_request_id, and this is the function we want to be called from the modal to pass the id and greeting of the selected helloworld record. Knowing this, window.processModalSelect will set the input fields for the greeting and id, and set the visibility of the Select and Clear buttons.

The modal knows that this is the name of the function to call because it's passed as the &function=jSelectHelloworld_jform_request_id parameter within the URL of the iframe.

Contributors