Ajax: get partial blade template

Hello.

I’m trying to define various modal (popup) templates in my new WP Sage environment and then get them via Ajax call based on the provided “type” ID.

For example, modal with “type” 1 would have a centered title, image on the left and text on the right. The one with “type” 2 would have a title on a different place, then text on left, image on right, and another row the other way around. Modal type is selected when adding a modal in backend (custom post type) - based on this, proper content fields appear and must be filled in.

What I’m trying now is this:

  1. When you click a certain button with a given data attribute, Ajax call is made to WP Rest API, which gets all the content for the modal which will be opened. You also get modal type (1,2,3 …). This works as expected already. (data from ACF fyi).

  2. Based on the given modal type, I’d like to fill in the existing modal already loaded on page (just the wrapper) with the modal template in views/ajax/modal1.blade.php (or modal2.blade.php etc.). I want to keep the modal as blade template because i includes certain files / components already and also it’s probably better to keep it that way, no ?

  3. Now I insert the received HTML/strings to the template via Javascript to display it to the user, and I make the modal visible.

How do I go about compiling the ajax-received modal templates before getting it?
“I should probably make a controller” I though, but there’s no actual ‘view’ for which this controller would work; it’s just for the partial templates for this modals.

Any suggestions/help appreciated. Perhaps I’m even complicating things.
First time Sage user, hopefully, this is not a total misunderstanding of the concepts here :slight_smile:

Thanks!

Hi @trainoasis,

Take a look at this thread and see if that gets you headed in the right direction:

Hey @mmirus, thanks for your time.

I saw that and it actually seemed helpful indeed at first, but I’m a bit at a loss : what ajax url to call to get the appropriate data? How do I expose a certain URL (my controller that has a public static method inside which returns Template(…)). Certainly not just “…/views/ajax/modal1.blade.php” or something like that? This of course just gets the actual data within.

Still did not wrap my head around this 100% it seems!

Hey @trainoasis,

I think your best bet is to do a wp_ajax call. https://codex.wordpress.org/AJAX_in_Plugins

The action you hook onto that call should prepare any data your partial needs and then call Template() passing in the partial path and the data. See the thread I linked to above and @MWDelaney’s post about passing data.

The action could be a static method on a controller (the app controller, for example).

Does that help get you any further in the process?

3 Likes

That indeed helped a lot, thanks.

Got it working by using a separate controller for ajax stuff (to store ajax calls in the future also).

Just as a reference or help to someone else, what I did:

In setup.php:

/**
 * Ajax stuff
 */
add_action( 'wp_ajax_get_modal', 'Ajax::getModalTemplateHTML' ); 
add_action( 'wp_ajax_nopriv_get_modal', 'Ajax::getModalTemplateHTML' );

In a new file called ajax.php under app/controllers/:

namespace App;

use Sober\Controller\Controller;

class Ajax extends Controller
{
	public static function getModalTemplateHTML($popup_type = 1) {
		if(isset($_POST['popup_type'])) {
			$popup_type = (int)$_POST['popup_type'];
		}
		echo Template('ajax.modal'.$popup_type, ['data' => 'stuff']);
    	wp_die();
	}
}

And lastly in common.js (or any other) got the data via $.ajax:

$.ajax({
            url: GLOBAL_OBJECT.ajax_url,
            method: 'POST',
            data: { action: 'get_modal', popup_type: data.popup_type },
          }).done(function (data) {
            console.warn("FILL THE DATA IN THE MODAL WITH what you got from ajax");
        $('#modal-content').html(data); // this way I get a modal template; now I  can fill it with the data provided by a previous ajax to the REST API :) 
      });

If there’s a better/more correct approach to this, please do let me know :slight_smile:

4 Likes

Glad you got it working, and thanks for sharing.

Speculatively, another way of accomplishing this would be to move your code out of controllers and setup.php and put it in app/ajax.php.

The reasons I explored this:

  1. Using a controller here that’s run via separate actions unrelated to how the controller normally functions feels a little strange / out of place to me. Moving it out of there makes it clear that the ajax controller isn’t run as part of the normal template loading process like the other controllers are.
  2. Currently if you want to add more AJAX hooks you would need to edit setup.php every time on top of your ajax.php file. Moving the add_action calls to it make it a little more self-contained.

But at the end of the day if your current setup is working and fits with how you want to organize your theme I don’t know if there’s any real advantage to doing it this way.

Here’s how you would set this up…

Create app/ajax.php and put your AJAX-related code in there:

<?php

namespace App;

class Ajax
{
    public function __construct()
    {
        add_action('wp_ajax_get_modal', [$this, 'myMethod']);
        add_action('wp_ajax_nopriv_get_modal', [$this, 'myMethod']);
    }

    public function myMethod()
    {
        echo 'AJAX response here';
        wp_die();
    }
}

new Ajax();

Add ‘ajax’ to the array of files loaded by Sage in functions.php:

/**
 * Sage required files
 *
 * The mapped array determines the code library included in your theme.
 * Add or remove files to the array as needed. Supports child theme overrides.
 */
array_map(function ($file) use ($sage_error) {
    $file = "../app/{$file}.php";
    if (!locate_template($file, true, true)) {
        $sage_error(sprintf(__('Error locating <code>%s</code> for inclusion.', 'sage'), $file), 'File not found');
    }
}, ['helpers', 'setup', 'filters', 'admin', 'ajax']);

Your JS shouldn’t need to change.

11 Likes

Hello @mmirus.

That’s indeed a nicer approach there! Using it now and it feels much cleaner and better organised, which is the whole point of using Sage :smiley:.

Thanks for your help, very much appreciated, love Sage so far! Gotta move to Bedrock too!

1 Like

Hi,

What data should I pass to populate the template with its content?

if I understand correctly, the “partials/content-single” is used within the loop. I’m having an ajax call, which properly echos the template but of course without any content.

echo template("partials.content-single");

I tried passing the post Object data, but didn’t help:

$data = get_post( 50 );	
echo template("partials.content-single", $data);    

my content-single.blade:

  <div class="entry-content">
	<div class="heading">{!! get_the_title() !!}</div>

	   @foreach (SingleBookCategoryPost::acf()->modules as $module)
	   		<div class="module_{{$loop->index+1}} module_id_{{$module->id}}" >
	   			
	   			@if ($module->id === "a")
	   				<img src="{{$module->image}}" />
				
				@endif
	   		
	   		</div>
	   @endforeach
		
	     

  </div>

    <footer>
  <div class="navigation">{!! next_post_link() !!}</div>

  </footer>

any thoughts?

1 Like

Hey @Jiannis_Sotiropoulos.

If you are passing data (whatever you wish actually) you have to properly use it in your used template, and also take care of what you are passing to the template on the first place.

So $data in your case is an Object (came from get_post()).
If you pass an array get_post(50, ARRAY_A) (check docs) then you can immediately use the keys as variables in your template.

So for example you could access $ID or $post_author etc.

Let me know if this is works for you.

Hey,

thanks! I thought as much :slight_smile:

I was wondering which $data can I pass to use the template as is (e.g. get_the_title() is not working neither is the custom Controller “SingleBookCategoryPost”).

Is it possible or should I create another template?

Heya,

indeed, get_the_title() needs setup_postdata() called prior or another way of initialising the main query loop.

If you use it, then you should probably use wp_reset_postdata() as well.

Also, you usually don’t need to use returning functions, since Blade echoes stuff for you.
So {!! get_the_title() !!} could become {{ the_title() }} or {!! the_title() !!} if you need html data visible.

thanks! I installed barba.js to get my ajax calls running :stuck_out_tongue:

1 Like

Hi @Jiannis_Sotiropoulos! I was wondering if you could walk us/me through how you set up barbra.js in Sage 9.0? I’m somewhat new to Sage (I’ve used sage 5.0 for a while and moving to 9.0 with blade) and would love to know how you set it up.

1 Like

Hi there !

i basically edited main.js like this:

import 'jquery';
import './autoload/**/*'
import Router from './util/Router';
import common from './routes/common';

const routes = new Router({
 common
 });

   jQuery(document).ready(() => routes.loadEvents());

so now the main.js runs only once.

the common.js sets up barba and initializes all other templates. Basically adding all Barba code in the init method:

    import Barba from 'barba.js/dist/barba.min';
    import Router from '../util/Router';
    import home from './home';
    import singlePost from './singlePost';
    import templateAbout from './templateAbout';

    export default {

    init() {

    Barba.Dispatcher.on('transitionCompleted', function() {
    // reload home, or singlePost JS
    var routes = new Router({ home,  singlePost, templateAbout});
    routes.loadEvents();
    	});

Barba.Pjax.start();
	Barba.Prefetch.init();
    },

    finalize() {

    },
    };

Hope this helps :slight_smile:

Thanks for posting your example :grinning:

Hi @trainoasis @mmirus and to all

on Sage 10 there isn’t Template function, used in the code here:

echo Template('ajax.modal'.$popup_type, ['data' => 'stuff']);

What should we use on Sage 10?

There are a couple ways AFAIK, but I’d usually do something like this:

echo \Roots\view("ajax.modalr.{$popup_type}", ['data' => 'stuff']);