One thing I find myself frequently needing is a way of previewing what an item will look like in the site view from the admin list or edit view for the item. In J3 there is no built-in preview facility, and the Regular Labs Better Preview extension only works with the built-in components. 

After years of working around this by previewing using a second front-end tab and saving in admin and re-loading in the site window in order to see what it will look like I finally got around to working out how to make a Preview button on the admin page toolbar bring up a preview of the site view of the item in a modal window even if it is not published - much more convenient, and avoids the necessity to publish your work in progress before it is ready.

So the assumption is that you have a component with items that are edited and listed in admin (back-end) pages, and you have a site (front-end) view which will display a single item. The challenge is to display in a modal window overlaying the admin page what the current item (being edited, or selected in a list view) would look like on the site item page.

We want the modal window to nearly fill the available space and have a header with a title and a 'Close' button. There is no need for any footer as the only action will be to close the window.

The item view will be displayed in an iframe in the modal body. If the iframe content is taller than the maximum height for the modal area then we want to display it with a scroll bar, but if it is less then the maximum we want the modal body to shrink to the height of the iframe.

So we will need to set a max-height for the iframe which we can calculate in css by using the viewport height and subtracting the amount needed for the modal header and the margin above and below the modal window. We can do this in the parent window by setting a class for the modal area and making a css selector which gets the iframe in the modal-body area of the modal:

.xbpvmodal .modal-body iframe { max-height:calc(100vh - 180px);}

 180px works fine for the default modal position set in the template stylesheet (assuming it is derived from the prostar J3 standard template which sets a modal position to 5%. It allows for 35px for the modal header and leaves about 50px below the bottom of the modal.

In order to set the height for the iframe we will need to use a bit of jQuery to get the child document height.

  jQuery('.iframe-full-height').on('load', function(){
    this.style.height=this.contentDocument.body.scrollHeight+20 +'px';
});

The +20 is needed to avoid an unwanted scroll bar appearing in some browsers.We will need to place this script in the modal window template that is loaded by the ajax call. The max-height will be inherited from the css above in the main window (edit/list page).

On the edit page you need three additional bits and then you need an additional tmpl file for the modal view which loads the required site page in an iframe.

First on the admin edit (or list) page you need the css above wrapped in <style>...</style> tags at the top of the page

Then you need a bit of script at the bottom of the page to call up the modal using ajax JQuery.

/*js to create the modal window */
jQuery(document).ready(function(){
//for preview modal
    jQuery('#ajax-pvmodal').on('show', function () {
        // Load view vith AJAX
        jQuery(this).find('.modal-content').load(jQuery('a[data-target="#'+jQuery(this).attr('id')+'"]').attr('href'));
    })
    jQuery('#ajax-pvmodal').on('hidden', function () {
     //this would save and reload the page when the modal closes, but you don't really need it for this use.
     //Joomla.submitbutton('itemtype.apply');
    })
});

The JQuery calls look for a id of ajax-pvmodal. If other modal windows are used on the same page they would use different ids.

Now you need a placeholder at the bottom of the page to make the modal window. 

<!-- preview modal window -->
<div class="modal fade xbpvmodal" id="ajax-pvmodal" style="max-width:80%">
    <div class="modal-dialog">
        <div class="modal-content">
			<!-- Ajax content will be loaded here -->            
        </div>
    </div>
</div>

Note that the id for the outer div is ajax-pvmodal to match the JQuery calls in the script above and the class includes the xbpvmodal used to set the max-height for the iframe.

Now you need to create a new file to embed the relevant view page in an iframe and provide close (or save, if relevant) buttons. Call this modalpv.php to match the link that will be used on the button

<div class="modal-header">
	<button type="button" class="close" data-dismiss="modal" aria-hidden="true"
    	style="opacity:unset;line-height:unset;border:none;">&times;></button>
	<h4 class="modal-title">Preview Person</h4> 
</div> 
<div class="modal-body"> 
	<div style="margin:0 30px;"> 
		<iframe src="/<?php echo JURI::root(); ?>
			/index.php?option=com_componentnamee&view=itemview&layout=default&tmpl=component&id=
			<?php echo JFactory::getApplication()->input->getInt('id'); ?>" 
			title="Preview Person" 
			id="newg">
		</iframe> 
	</div> 
</div> 

Note here that in the header we are clearing the default opacity and line-height values for the header button which are set to odd values in the Joomla3 template and we are using the &times; entity to get a nice clear bold [X] button to close the modal.

Finally on the original page you need to place a button (or other link) to trigger the modal

<a href="/component/xbpeople/?view=person&layout=modalpv&tmpl=component&id='.$this-&gt;item-=&gt;id_'="
 data-toggle="modal" data-target="#ajax-pvmodal"
class="btn btn-small btn-primary"><i class="icon-eye"></i> '.Text::_('Preview').'</a>';

 

or alternatively you can add the button to the toolbar for the admin page in the view.html.php file addToolBar() function by using dhtml to create a custom button.

$bar = Toolbar::getInstance( 'toolbar' );
        if ($this->item->id > 0) {
			$dhtml = '<a href="/component/xbpeople/?view=person&layout=modalpv&tmpl=component&id='.$this-&gt;item-=&gt;id_'="
            	data-toggle="modal" data-target="#ajax-pvmodal"
            	class="btn btn-small btn-primary"><i class="icon-eye"></i> '.JText::_('Preview').'</a>';
            $bar->appendButton('Custom', $dhtml);