0

I have a custom post type setup with a couple custom fields. I am creating a page where I display all of the custom post types, except I want to have multiple views. See the examples below. One is a list view, the other is a tiled project view with an image.

At the top there will be buttons to switch between templates. Is there a way to switch between page templates dynamically? I've never done anything like this and would appreciate any help on the matter! I would rather not have each view of the portfolio be a new page.

Thanks! enter image description here

enter image description here

0

My answer is something like an application of one of the @allenericr answer points + some cache.

I suggest to implement this only if the 3 templates are different in the content and not only in the aspect, otherwise a CSS + JS solution should be the best choice.

Create a main page template 'multi-template-page.php' that only contain the switch buttons and a get_template_part call to include one of the 3 different template files (named e.g. my-template-1.php, my-template-2.php and my-template-3.php) that you have to write for every template.

In the main template put something like:

<?php
/*
Template Name: Multi Template Page
*/

get_header(); ?>

<ul id="template_switches">
  <li><a href="#template-1" data-template="1" class="active">Template 1</a></li>
  <li><a href="#template-2" data-template="2">Template 2</a></li>
  <li><a href="#template-2" data-template="3">Template 3</a></li>
</ul>
<div id="template-target"><?php get_template_part('my-template', '1'); ?></div>

<?php get_footer(); ?>

So, by default you'll have first template active and displayed.

Then, in functions.php enqueue a script for this template:

add_action('wp_enqueue_scripts', 'template_switch_script');

function template_switch_script() {
  if ( is_page_template('multi-template-page.php') ) {
    wp_enqueue_script('multi-template', get_template_directory_uri() . '/js/multi-template.js', array('jquery'));
    $data = array(
       'url' => admin_url('admin-ajax.php'),
       'id' => get_queried_object()->ID
    );
    wp_localize_script('multi-template', 'multiTemplateData', $data );
  }
}

Now you have to create the jsvascript file multi-template.js in js subfolder of your theme:

window.multiTemplateCache = {} // prepare a cache object

jQuery().ready( function($) {

  // cache the 1st template on load
  window.multiTemplateCache.template_1 = $('#template-target').html();

  $('#template_switches li a').click( function(e) {
    e.preventDefault();
    if ( $(this).hasClass('active') ) return false; // do nothing if click active link

    var template = $(this).data('template');

    if ( window.multiTemplateCache['template_' + template ] ) { // look in cache

       // in cache, just output to page
       $('#template-target').html( window.multiTemplateCache['template_' + template] );

    } else { // not in cache, retrieve via ajax

      // remove current template and put a loading message
      $('#template-target').html( 'loading...' ); 

      // css stuff
      $('#template_switches li a').removeClass('active');
      $(this).addClass('active');

      $.get(
        multiTemplateData.url,
        {
          postId: multiTemplateData.id,
          action: 'multi_template_template',
          which: template
        },
        function( data ) {
          // cache the template
          window.multiTemplateCache['template_' + template ] = data;
          // output the template to page
          $('#template-target').html( data );
        }
      );

    } // end if

  }); // end on click event

}); // on ready event

This script on click on one of the switch links, check in a cache object if the template is already loaded, and if so, output it. If not send an ajax request passing the template required.

Before putting the content to page, it is cached, so the ajax call is fired once per template. But only for templates 2 and 3 because template 1 is cached on load :)

So, now in your function.php you have to to add the ajax action and the relative function.

add_action('wp_ajax_multi_template_template', 'multi_template_template');
add_action('wp_ajax_nopriv_multi_template_template', 'multi_template_template');

function multi_template_template() {
  $template = isset($_REQUEST['which']) ? $_REQUEST['which'] : false;
  if ( ! $template ) die('Sorry, impossible to load content');
  $postid = isset($_REQUEST['postId']) ? $_REQUEST['postId'] : false;
  if ( $postid ) {
    global $post; // I'm setting global $post just-in-case the template make use of it
    $post = get_post($postid);
  }
  get_template_part('my-template', $template);
  die();
}

You can improve my code, adding the # feature, i.e. when you point to an address like http://example.com/multi-template-page/#template2 the template 2 is setted as active and loaded for first...

6
  • @g-m, thanks so much for this in-depth solution! This is EXACTLY what I need. I haven't had much experience using ajax, so forgive me for commenting about this trivial error, but I implemented everything and get Uncaught ReferenceError: multi_template_template is not defined – TechyDude Sep 23 '13 at 19:16
  • Thanks for getting back to me! I fixed that now I get console errors: GET siteurl.com/wp-admin/… – TechyDude Sep 23 '13 at 22:18
  • No, I just renamed the actual name. The console log error is: GET the_actual_url_of_my_site.com/wp-admin/… 404 (Not Found) jquery.min.js?ver=1.8.1:2 – TechyDude Sep 23 '13 at 22:25
  • Aha! That did it. But also, before it worked I found another error: <div id="#template-target"> needs to be <div id="template-target">. Currently battling many other issues since I am loading templates with the sorting plugin isotope.. BUT! Thanks for all your help! It's working! – TechyDude Sep 23 '13 at 23:06
  • Hey @g-m, If you don't mind I have another question. How would add the hash tag feature like you mentioned above? "You can improve my code, adding the # feature, i.e. when you point to an address like example.com/multi-template-page/#template2 the template 2 is setted as active and loaded for first..." If you cannot answer, can you point me in a direction? – TechyDude Sep 27 '13 at 14:40
3

You could handle this with separate page loads via a rewrite endpoint. This will send a request to the server for each button click, and you'd have to format the links yourself to append the endpoint to the permalink, not sure if that meets your requirements.

First register the endpoint for the page post type:

function wpa_view_endpoint(){
    add_rewrite_endpoint( 'view', EP_PAGES );
}
add_action( 'init', 'wpa_view_endpoint' );

Now for each page like:

http://example.com/some-page/

You can also have URLs like:

http://example.com/some-page/view/list/

The value of view will be available in your template via get_query_var:

$view = get_query_var( 'view' );

Then you can display whatever markup based on that value.

You could also filter the loading of page templates and load a separate template for each view that way:

function wpa_view_template( $template = '' ){
    global $wp_query;
    if( isset( $wp_query->query_vars['view'] ) ) {
        $template = locate_template( $wp_query->query_vars['view'] . '.php', false );
    }
    return $template;
}
add_filter( 'page_template', 'wpa_view_template' );

So for the example:

http://example.com/some-page/view/list/

This filter will load the template list.php instead of your default page template.

0

If you are just trying to avoid a page load, you could approach this from many ways. I've listed a few of them below.

However, first you should ask yourself if there is really a need to avoid the page load and if it's worth the extra work on your end, will there be some sort of smooth transition between templates or will it just swap suddenly, how will you handle this transition, etc?

Here are the various options I could think of (there are likely many of other possible solutions):

  1. Have CSS for various views loaded already and then apply a class on button click that changes the layout in the CSS
  2. Have view data stored as data- attributes of the original view and load view data on button click
  3. Use ajax to load a page template with the specified view into the page content, using jQuery's .load() method you can specify which parts of the page to load, so you could just replace the contents of a <div id="content"> with the contents of a <div id="content"> (or whatever your content section that will be changing is) from another url.
  4. Build the templates in a JS templating system and trigger a template change on click
  5. Add post information to page as a JSON object in a script tag, then on load you can build and populate the default template, and on button click you can build and populate the selected template
  6. Set up a WordPress ajax action that you can send a template slug to and it will call get_template_part('content', $slug); and then you can inject that content into your page, just send it a slug for the template you'd like to load when the buttons are clicked
  7. Have all of the different templates HTML loaded onto page and then just show and hide templates on button clicks

I don't know that any of these solutions is perfect, but I'd say some combination of 1 and 3 or 6 would be the way I would go. I think in most situations I would prefer 3 over 6 so that I've got actual pages that exist, though.

If the templates weren't vastly different in content and layout I'd say just use CSS classes to reconfigure it, but from the images above, it seems the templates will be very different in nature, and if you just used the CSS version with no JS, you'd be hiding and showing images without knowing if the user wants to see them, wasting some of their bandwidth for no reason.

I would probably set up a page for each view and tell it which template to use and then when the user clicks on the button I would pull the content from that other template and then inject it where the content of the current template resides.

That way your other views exist to crawlers (without having to set up HTML snapshots) and people who have browsers lacking JS/AJAX for whatever reason.

You can also benefit from any caching you have set up in your WordPress install and load the saved HTML.

And, if someone wants to link to one view in a Tweet or a Facebook post, other people who click on their link will be taken to that view, but can check out the other, views as well.

I would make the template buttons link to those pages and then just use an e.preventDefault() on the click event listener for the template button anchor tag.

You could also hook into the History API to inject the new states into the Browser's History, as well. That way the user can use their back/forward buttons while switching templates.

You could also probably hook up the History API without having separate pages and just change everything via the methods available in the History API, but if your templates exist as pages on their own you won't have to deal with the headache of making those other template states crawlable via HTML snapshots and hash fragments. (I've never hooked the History API onto a page where the states I was injecting also existed as standalone URLs on my site, though, so that might create some complications)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.