Changing Custom HTML Widget into Tab. Using AJAX?

Not sure if this is appropriate or not, but I’m kinda lost on what to do here. Need some advice and clarification.

So, at this site http://iceiceicy.com/wingwah/, I’ve created a Custom HTML Widget (located above the product thumbnails)

    <div class="tagcloud">
	<ul class="tag-widget">
    <li>
    <a href="http://iceiceicy.com/wingwah/">All Watches</a>
    </li>
    <li>
    <a href="http://iceiceicy.com/wingwah/product-tag/general-sport/">General 
    Sport</a>
    </li>
    <li>
    <a href="http://iceiceicy.com/wingwah/product-tag/chronograph/">Chronograph</a>
    </li>
    <li>
    <a href="http://iceiceicy.com/wingwah/product-tag/military/">Military</a>
    </li>
    <li>
    <a href="http://iceiceicy.com/wingwah/product-tag/diver/">Diver</a>
    </li>
    <li>
    <a href="http://iceiceicy.com/wingwah/product-tag/dress/">Dress</a>
    </li>
    <li>
    <a href="http://iceiceicy.com/wingwah/product-tag/pilots/">Pilots</a>
    </li>
    <li>
    <a href="http://iceiceicy.com/wingwah/product-tag/racing/">Racing</a>
    </li>	
    </ul>	
    </div> 

Currently, the homepage is the Woocommerce product archive page.
My question is, how I want the widget options to act as tab. Meaning when the user click one of the link, the page is not reload, only the watches thumbnails are changing.

I’m thinking the only way is to using AJAX. What I want to know is it possible to make it and what are the basic logic steps to take?

Hi there!

Yes. AJAX is the way to go. However there are many different strategies…

Usually, the HTML is created by the server. WooCommerce is based on Wordpress which is based on PHP.
In order to really dynamically re-render the list of products, you need to get the raw data, so javascript (which also fires the request in the background to the server) can do the job to dynamically re-render the products.

Now I think your possibilities with the existing system are pretty numbered :/.
What you could do - which is not the most performance friendly way, is to get ask the URL of products in the background and then pick only the HTML that contains the product. But then you would also need to replace the pagination with that too.

You already use jQuery on that website, so lets go with that.

$('.tag-widget a').click(event => {
	event.preventDefault()
	$('.products-grid').load(`${event.target.href} .products-grid`)
})

While this “works” you still have the following problems on your hands:

  • Showing a loader/spinner
  • Handle pagination dynamically
  • Very very slow re-render

As you can see, jQuery’s load function (https://api.jquery.com/load/) can be used to load HTML from another URLs container.
The problem is, that all the surrounding HTML (layout, metadata etc) is also loaded EVERY SINGLE TIME.

The right way to do it, is to get the raw JSON data (maybe WooCommerce has a Rest API)
That you can use to query for the watches by category filter. Look for their documentation and in worst case: ask on their support channel.

Otherwise, look how WooCommerce is integrated into Wordpress.
Maybe they use pages to create products. So you could use Wordpress minimal Rest API: https://developer.wordpress.org/rest-api/

Please let me know how you are going to deal with this situation.
For my part, this is exactly what I’m currently writing about in https://www.wireupyourfrontend.com/
These are exactly those problems that as front-end developer you have to know how to talk with the server and get to the data in the first place.

Hope you have a nice day and find a nice way to implement this.

Best,
Martin

Thanks for the details reply.

Yeah agree with you. Although the jquery code works, but it’s not very practical since pagination is not working and also no loader icon.

So, I think I’m basically stuck with using AJAX. From here, https://woocommerce.github.io/woocommerce-rest-api-docs/#introduction , I can see that the WooCommerce support REST API and JSON.

The thing now is, I’ve no idea how to connect the widget URL link with the AJAX and JSON. So for now, I’m trying to understand what steps are required and the syntax/codes involve.

You can use JQuery . I always do using JQuery . Here is the example . Hopefully , it will help you a lot to understand to .Check this please :slight_smile:

Hopefully , you will find solution

The problem is not the Tabs UI @georgesimpson009. That is what @nzrink1 already built. What has to be done is fetching the products and re-render them on clicking the tab.
So jQuery only will not get you anywhere in this situation.

@nzrink1 Good job, there is an API for the products!

The most interesting is here: https://woocommerce.github.io/woocommerce-rest-api-docs/?shell#list-all-products.

Now it would be interesting to see this API in action. As you can see here, the woocommerce API is available: https://iceiceicy.com/wingwah/wp-json

Watch out: only v1 is available, so you need to follow these instructiions: https://woocommerce.github.io/woocommerce-rest-api-docs/wp-api-v1.html

For the nice automatic formatting of the JSON in the browser, I use JSONView.

So the URL to get the products filtered by category is the following:

https://iceiceicy.com/wingwah/wp-json/wc/v1/products?category=3

First of course, you need to know the IDs of categories, which you can find here:

https://iceiceicy.com/wingwah/wp-json/wc/v1/products/categories

Please note: You maybe need an authentication key to query the API.

The API is pretty verbose. A lot of properties are in there together with variations, attributes, etc.
Your mileage may vary. But this is the “raw data” I were speaking of. Maybe there is a property you can use to restrict the output of properties, but this is for later optimization.

So what you can do, when clicking on a category, is:

  1. Get the products, filtered by category with AJAX ($.get)
  2. Show a loading indicator
  3. Render the list of products with the properties you need.

Point three can be implemented in many different ways. The most comfortable, and most performant one, would be to use a proper templating engine with data binding to make rendering as performance-efficient as possible. The other alternative, is to use jQuery’s $(.product-grid).html() method.

Your HTML for one single product is quite heavy, so I guess even with the big API responses, you will get a faster page-render.

First: get the products for a category with your rest api password, when clicking on the category tab:

https://iceiceicy.com/wingwah/wp-json/wc/v1/products?consumer_key=123&consumer_secret=abc&category=3

function getProducts(category) {
    var API_SECRET = 'obtained via admin interface'
    var API_KEY = 'obtained via admin interface'
    $.get(
        `https://iceiceicy.com/wingwah/wp-json/wc/v1/products?consumer_key=${API_KEY}&consumer_secret=${API_SECRET}&category=${category}`, 
         renderProducts
    )
}

function renderProducts(products) {
    var productsHTML = products.map(product => { 
        return `
            <div class="product-cat-${categories[0].id} product first col-lg-4 col-sm-4 col-xs-6 wishlist-disabled ">
                <a href="${product.permalink}" class="woocommerce-LoopProduct-link"> </a>
                <div class="content-product">
                    <a href="${product.permalink}" class="woocommerce-LoopProduct-link">
                    </a>
                    <div class="product-image-wrapper hover-effect-swap">
                        <a href="${product.permalink}" class="woocommerce-LoopProduct-link">
                        </a>
                        <a class="product-content-image" href="${product.permalink}" data-images="${product.images.map(image => image.src)}">
                        </a>
                        <div class="label-product">

                        </div>
                        <img src="${product.images[0].src}" class="show-image">
                        <!-- added the link to product thumbnail -->
                        <a href="${product.permalink}">
                            <img width="450" height="340" src="${product.images[0].src}" class="attachment-shop_catalog size-shop_catalog wp-post-image"
                                alt="${product.images[0].alt}" title="${product.images[0].alt}"> </a>

                        <footer class="footer-product">
                        </footer>
                    </div>


                    <div class="text-center product-details">

                        <div class="product-title">
                            <a href="${product.permalink}">${product.name}</a>

                        </div>

                        <div class="products-page-cats reference_code">115264 JV</div>

                        <div class="products-page-cats">
                            <a href="http://iceiceicy.com/wingwah/product-category/${product.categories[0].slug}/" rel="tag">New (2018)</a>
                        </div>



                        <span class="price">
                            ${product.price_html}
                        </span>

                        <div class="product-excerpt">
                        </div>

                        <a href="#" class="button add-to-compare-button btn button btn-default theme-button theme-btn" data-product-id="${product.id}"
                            rel="nofollow">
                            <p class="add-to-compare-button-text">Compare</p>
                        </a>
                    </div>
                </div>
            </div>
        ` 
    }) 
    $(.product-grid).html(productsHTML.join(''))
)

Lastly: Put it together:

$('.tag-widget a').click(event => {
	event.preventDefault()
        // You could get the catagory, by assigning the ID to the button. E.g. via 
        // <button data-category-id="<?= $product->category_id?>"> or whatever PHP is used to output the cateogry ID
	getProducts(category)
})

It can take some time to master this, but when you get to deal with the raw data.
I hope you can get something to work with these pieces of code. Don’t give up and experiment and see if this gets you a better result.

Best,
Martin

Just noticed your reply. Yeah, I can’t view the https://iceiceicy.com/wingwah/wp-json/wc/v1/products/categories , got return error on authorization and actually I kinda lost with your example. So, I’m gonna understand them step by step.

First, I need to generate the WooCommerce api key right, since I can’t view the WC JSON file. I generated it, but I don’t know where to put it. I read the document and it shows the example as

<?php
$store_url = 'http://example.com';
$endpoint = '/wc-auth/v1/authorize';
$params = [
    'app_name' => 'My App Name',
    'scope' => 'write',
    'user_id' => 123,
    'return_url' => 'http://app.com',
    'callback_url' => 'https://app.com'
];
$query_string = http_build_query( $params );

echo $store_url . $endpoint . '?' . $query_string;
?>

Do I need to put it on theme folder/functions.php ?

And once I manage to view the WC JSON file with authorization, then only I used this below code right? And this jQuery code, I can just put it on the of the site right?

function getProducts(category) {
    var API_SECRET = 'obtained via admin interface'
    var API_KEY = 'obtained via admin interface'
    $.get(
        `https://iceiceicy.com/wingwah/wp-json/wc/v1/products?consumer_key=${API_KEY}&consumer_secret=${API_SECRET}&category=${category}`, 
         renderProducts
    )
}

By the way, putting aside your example, yesterday I read this article. https://premium.wpmudev.org/blog/load-posts-ajax/

Based on that, here what I got so far.

On the functions.php , I added

add_action( 'wp_enqueue_scripts', 'theme_enqueue_styles' );
function theme_enqueue_styles() {
	wp_enqueue_style( 'child-style', get_stylesheet_uri(), array( 'bootstrap', 'parent-style' ) );
	wp_enqueue_script( 'ajax-pagination',  get_stylesheet_directory_uri() . '/js/ajax-pagination.js', array( 'jquery' ), '1.0', true );
	global $wp_query;
	wp_localize_script( 'ajax-pagination', 'ajaxpagination', array(
		'ajaxurl' => admin_url( 'admin-ajax.php' ),
		'query_vars' => json_encode( $wp_query->query )
	));
}

add_action( 'wp_ajax_nopriv_ajax_pagination', 'my_ajax_pagination' );
add_action( 'wp_ajax_ajax_pagination', 'my_ajax_pagination' );

function my_ajax_pagination() {
	echo get_bloginfo( 'title' );

    die();
}

Then, I created ajax-pagination.js and inside there I put

(function($) {
	$(document).on( 'click', '.tag-widget a', function( event ) {
		event.preventDefault();
		$.ajax({
			url: ajaxpagination.ajaxurl,
			type: 'post',
			data: {
				action: 'ajax_pagination'
			},
			success: function( html ) {
				$('.products-grid').find( 'div' ).remove();
				$('.pagination-cubic').remove();
				$('.products-grid').append( html );
			}
		})
	})
})(jQuery);

Now, the result is, whenever I click the tag-widget link, the title gonna be print below it.

I’m thinking, can’t I replace the echo get_bloginfo( ‘title’ ); with one of the tag template? Then, the jQuery instead of target .tag-widget a , i will create several codes that target specifically each links. ( I know it’s not very practical since it’s gonna be repetitive)

Well if you need any suggestion then i have something for you The right way to do it, is to get the raw JSON data (maybe WooCommerce has a Rest API)
That you can use to query for the watches by category filter. Look for their documentation and in worst case: ask on their support channel.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.