i like to give the clients an option to add more product in the woocommerce shopping cart widget.
so far i managed to find this code:
add_filter( 'woocommerce_widget_cart_item_quantity', 'add_minicart_quantity_fields', 10, 3 );
function add_minicart_quantity_fields( $html, $cart_item, $cart_item_key ) {
$product_price = apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $cart_item['data'] ), $cart_item, $cart_item_key );
return woocommerce_quantity_input( array('input_value' => $cart_item['quantity']), $cart_item['data'], false ) . $product_price;
}
the code make the quantity select visible in the shopping cart.
but the issue is when we change the quantity, no changes take place...
If i understand correctly i should add a Ajax request to change the product's quantity when the user add or remove products.
I also wanted to add quantity buttons to the Woocommerce shopping cart widget. I achieved this by combining your code with a modified version of this answer: https://stackoverflow.com/a/69218525/4234891
We will send the quantity via AJAX, update the quantity in the backend, and send the cart subtotal back.
So first create the quanity buttons like you already did. In functions.php add:
add_filter('woocommerce_widget_cart_item_quantity', 'add_minicart_quantity_fields', 10, 3);
function add_minicart_quantity_fields($html, $cart_item, $cart_item_key)
{
$product_price = apply_filters('woocommerce_cart_item_price', WC()->cart->get_product_price($cart_item['data']), $cart_item, $cart_item_key);
return woocommerce_quantity_input(array('input_value' => $cart_item['quantity']), $cart_item['data'], false) . $product_price;
}
Next, register the JavaSript. In functions.php add:
add_action('wp_enqueue_scripts', function () {
wp_register_script('custom-cart-widget', get_stylesheet_directory_uri() . '/js/cart-widget.js', ['jquery'], '1.0', true);
wp_localize_script('custom-cart-widget', 'cart_widget_qty_ajax', ['ajax_url' => admin_url('admin-ajax.php')]);
wp_enqueue_script('custom-cart-widget');
});
Next, add the function which will be called via AJAX. In functions.php add:
function ajax_change_widget_cart_qty()
{
// Set item key as the hash found in input.qty's name
$cart_item_key = $_POST['hash'];
// Get the array of values owned by the product we're updating
$cart_item_values = WC()->cart->get_cart_item($cart_item_key);
// Get the quantity of the item in the cart
$product_quantity = apply_filters('woocommerce_stock_amount_cart_item', apply_filters('woocommerce_stock_amount', preg_replace("/[^0-9\.]/", '', filter_var($_POST['quantity'], FILTER_SANITIZE_NUMBER_INT))), $cart_item_key);
// Update cart validation
$passed_validation = apply_filters('woocommerce_update_cart_validation', true, $cart_item_key, $cart_item_values, $product_quantity);
// Update the quantity of the item in the cart
if ($passed_validation) {
WC()->cart->set_quantity($cart_item_key, $product_quantity, true);
}
wp_send_json_success(['subtotal_html' => WC()->cart->get_cart_subtotal()]);
}
add_action('wp_ajax_change_widget_cart_qty', 'ajax_change_widget_cart_qty');
add_action('wp_ajax_nopriv_change_widget_cart_qty', 'ajax_change_widget_cart_qty');
Last, create a file in your_theme/js/cart-widget.js and add:
jQuery(document).ready(function($) {
jQuery(document).on('change', '.woocommerce.widget_shopping_cart input.qty', function(){
var value = $(this).val();
if (!isNaN(value) && value > 0) {
var holder = $(this).closest('li');
if (holder.length) {
var removeButton = $(holder).find('.remove_from_cart_button');
if (removeButton.length) {
var itemHash = removeButton.attr('data-cart_item_key');
if (itemHash) {
holder.block({
message: null,
overlayCSS: {
opacity: 0.6
}
});
$.ajax({
type: 'POST',
dataType: 'json',
url: cart_widget_qty_ajax.ajax_url,
data: {
action: 'change_widget_cart_qty',
hash: itemHash,
quantity: value,
},
success: function(response) {
if (response.data.subtotal_html) {
var miniCart = $(holder).closest('div.widget_shopping_cart_content');
if (miniCart.length) {
var subTotalHolder = $(miniCart).find('.woocommerce-mini-cart__total .woocommerce-Price-amount.amount');
if (subTotalHolder) {
subTotalHolder.replaceWith(response.data.subtotal_html);
}
}
}
},
complete: function() {
holder.css( 'opacity', '1' ).unblock();
},
});
}
}
}
}
});
});
Related
I'm trying to add ajax to add to cart button on front page.
The setup is using Divi. Divi's woo product module does not display Add-to-cart button. I use the below to display add-to-cart button on front page. That works but the only issue is the Ajax is not working on front page. I've enabled "Enable AJAX add to basket buttons on archives" from Woocommerce settings.
add_action('template_redirect', 'work_only_on_front_page', 10);
function work_only_on_front_page(){
if ( is_front_page() ) {
add_action('woocommerce_after_shop_loop_item_title', 'woocommerce_template_loop_add_to_cart', 10);
}
}
Below works well on other pages other than the front page.
(function ($) {
$(document).on('click', '.single_add_to_cart_button', function (e) {
e.preventDefault();
var $thisbutton = $(this),
$form = $thisbutton.closest('form.cart'),
id = $thisbutton.val(),
product_qty = $form.find('input[name=quantity]').val() || 1,
product_id = $form.find('input[name=product_id]').val() || id,
variation_id = $form.find('input[name=variation_id]').val() || 0;
var data = {
action: 'woocommerce_ajax_add_to_cart',
product_id: product_id,
product_sku: '',
quantity: product_qty,
variation_id: variation_id,
};
$(document.body).trigger('adding_to_cart', [$thisbutton, data]);
$.ajax({
type: 'post',
url: wc_add_to_cart_params.ajax_url,
data: data,
beforeSend: function (response) {
$thisbutton.removeClass('added').addClass('loading');
},
complete: function (response) {
$thisbutton.addClass('added').removeClass('loading');
},
success: function (response) {
if (response.error && response.product_url) {
window.location = response.product_url;
return;
} else {
$(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $thisbutton]);
}
},
});
return false;
});
})(jQuery);
function woocommerce_ajax_add_to_cart_js() {
if (is_product() || is_product_category() || is_shop() || is_front_page()) {
wp_enqueue_script('woocommerce-ajax-add-to-cart', get_stylesheet_directory_uri() . '/assets/js/ajax-add-to-cart.js', array('jquery'), '', true);
}
}
add_action('wp_enqueue_scripts', 'woocommerce_ajax_add_to_cart_js', 99);
add_action('wp_ajax_woocommerce_ajax_add_to_cart', 'woocommerce_ajax_add_to_cart');
add_action('wp_ajax_nopriv_woocommerce_ajax_add_to_cart', 'woocommerce_ajax_add_to_cart');
function woocommerce_ajax_add_to_cart() {
$product_id = apply_filters('woocommerce_add_to_cart_product_id', absint($_POST['product_id']));
$quantity = empty($_POST['quantity']) ? 1 : wc_stock_amount($_POST['quantity']);
$variation_id = absint($_POST['variation_id']);
$passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity);
$product_status = get_post_status($product_id);
if ($passed_validation && WC()->cart->add_to_cart($product_id, $quantity, $variation_id) && 'publish' === $product_status) {
do_action('woocommerce_ajax_added_to_cart', $product_id);
if ('yes' === get_option('woocommerce_cart_redirect_after_add')) {
wc_add_to_cart_message(array($product_id => $quantity), true);
}
WC_AJAX :: get_refreshed_fragments();
} else {
$data = array(
'error' => true,
'product_url' => apply_filters('woocommerce_cart_redirect_after_error', get_permalink($product_id), $product_id));
echo wp_send_json($data);
}
wp_die();
}
Another thing to check is to ensure that the function woocommerce_ajax_add_to_cart_js() is being called and the script is being enqueued on the front page.
You can check the browser's console to see if there are any errors related to the Javascript code not being loaded.
I currently am working on a site within shopify. I have setup the order form i wish to use and currently everything works fine however i also need to add in a customer text field that relays the teams name to the shopping cart.
Shopify.itemsToAdd = [];
Shopify.addItemstoTheCart = function() {
if (Shopify.itemsToAdd.length) {
var item = Shopify.itemsToAdd.pop();
$.ajax({
url: '/cart/add',
dataType: 'json',
type: 'post',
data: item,
success: Shopify.addItemstoTheCart,
error: Shopify.addItemstoTheCart
});
}
else {
window.location.href = '/cart';
}
};
jQuery(function($) {
$('table .quantity:first').focus();
$('.add-to-cart-order-form').click(function() {
$('.add-to-cart-order-form').addClass('disabled').attr('disabled','disabled');
// Resetting.
Shopify.itemsToAdd = [];
$('.quantity').each(function() {
var quantity = parseInt($(this).val(), 10);
if (this.checked) {
Shopify.itemsToAdd.push( { id: $(this).attr('data-id'), quantity: quantity} );
}
});
if (Shopify.itemsToAdd.length) {
Shopify.addItemstoTheCart();
}
else {
alert('All quantities are set to zero.');
$('.add-to-cart-order-form').removeAttr('disabled').removeClass('disabled');
}
});
});
as such i need to send the name from the following inputs name as the variable and the input from the customer as the variable data.
<input type="text" class="tname" name="properties[tname]">
it needs to accompany at the end of this line in the code above.
Shopify.itemsToAdd.push( { id: $(this).attr('data-id'), quantity: quantity} );
the data that is sent to the shopping cart is id, quantity, and properties[tname]
i have tried to input this to no success.
Shopify.itemsToAdd.push( { id: $(this).attr('data-id'), quantity: quantity, properties[tname]: "walker"} );
listing "walker" as test data for the variable. it continues to error i believe do to the [] but they have to be there for the shopify system to acknowledge the listing and populate the to the cart.
if you can provide any help i would be grateful.
EDIT
ok i can get the teamname to post in the cart but i can only get the first text field to be sent. i have aligned the text fields id tag to match the check boxes data-id tag. and my function works but only for the first pair. after that everything else is sent to the cart without the team names.
jQuery(function($) {
$('table .quantity:first').focus();
$('.add-to-cart-order-form').click(function() {
$('.add-to-cart-order-form').addClass('disabled').attr('disabled','disabled');
// Resetting.
Shopify.itemsToAdd = [];
$('.quantity').each(function() {
var quantity = parseInt($(this).val(), 10);
var did = $(this).attr('data-id')
var idname;
if (did == $('.tname').attr('id')) {
var idname = $('.tname').val();
};
if (this.checked) {
Shopify.itemsToAdd.push( { id: $(this).attr('data-id'), quantity: quantity, properties: { tname: idname}} );
}
});
if (Shopify.itemsToAdd.length) {
Shopify.addItemstoTheCart();
}
else {
alert('All quantities are set to zero.');
$('.add-to-cart-order-form').removeAttr('disabled').removeClass('disabled');
}
});
});
Should be:
Shopify.itemsToAdd.push( { id: $(this).attr('data-id'), quantity: quantity, properties: { tname: "walker"}} );
I've been struggling with this and I don't find anyway to get this right.
I am using jtable and the thing is everything works correctly except with child elements.
I added a child table to a button so that when it opens it displays data related with the previous id.
The thing is listAction, updateAction and createAction are working okay, but when it comes to deleteAction it is not sending any information via post, so there is no way I can delete the exact row I want to.
This is the jquery (jtable) code:
asignaturas: {
title: '',
width: '5%',
create: false,
edit: false,
display: function (asignaData) {
//Create an image that will be used to open child table
var $img = $('<img src="metro/list_metro.png" style="opacity:0.4;"title="Editar Asignaturas" />');
//Open child table when user clicks the image
$img.click(function () {
$('#crudTable').jtable('openChildTable',
$img.closest('tr'),
{
title: asignaData.record.nombre + ' - Assignatures',
actions: {
listAction: 'index.php?ex=jprofesor&action=prepara&prepara=list&id=' + asignaData.record.dni,
deleteAction: 'index.php?ex=jprofesor&action=prepara&prepara=delete&id=' + asignaData.record.dni,
updateAction: 'index.php?ex=jprofesor&action=prepara&prepara=update&id=' + asignaData.record.dni,
createAction: function (postData) {
//console.log("creating from custom function...");
return $.Deferred(function ($dfd) {
$.ajax({
url: 'index.php?ex=jprofesor&action=prepara&prepara=create&id=' + asignaData.record.dni,
type: 'POST',
dataType: 'json',
data: postData,
success: function (data) {
$dfd.resolve(data);
$('#crudTable').jtable('reload');
$('.ui-dialog-titlebar-close').click();
},
error: function () {
$dfd.reject();
}
});
});
}
},
fields: {
dni: {
type: 'hidden',
defaultValue: asignaData.record.dni
},
codigo: {
title: 'Asignatura',
width: '80%',
// list: false,
options: "index.php?ex=jprofesor&action=prepara&prepara=asignaturas"
}
}
}, function (data) { //opened handler
data.childTable.jtable('load');
});
});
//Return image to show on the person row
return $img;
}
//acaba child
}
},
And this is the php code that deletes:
$query = $query." and p.dni = '".$dni."'";
$dni = $conn->real_escape_string($_GET['id']);
$codigo = $conn->real_escape_string($_POST['codigo']);
$conn->query("delete from prepara where dni = '".$dni."' and asignatura = '".$codigo."'");
retornarPrepara($conn, $query);
function retornarPrepara($conn, $query, ) {
$rs = $conn->query($query);
$num_rows = $rs->num_rows;
$rows = array();
if($rs) {
while($row = $rs->fetch_assoc())
{
$rows[] = $row;
}
$jTableResult = array();
$jTableResult['Result'] = "OK";
$jTableResult['Records'] = $rows;
$jTableResult['TotalRecordCount'] = $num_rows;
print json_encode($jTableResult);
}
}
As far as I know the code is okay, but there must be something I'm missing in jquery to send the field codigo via post, but I thought jtable did this automatically as it is doing it with the parent jtable without any problem.
I'm gonna answer my question as I finally got this to work... it was a silly mistake but at the same time it is good to be aware about it.
The thing is this a school exercise in which we received some database tables that don't have id's so I wasn't really treating the fields as if there was a primary key, anyway deleteAction only sends the key in ajax post so the only way to get it is putting key:true, into the field you want to get.
Once I added key:true, everything worked correctly.
I am using the cascading dropdown jquery plugin. ( https://github.com/dnasir/jquery-cascading-dropdown)
I have two dropdowns. 'Client' and 'Site'.
Based on which client you select, the sites list should be appropriately reduced to only show the sites for the selected client. I have setup two php scripts returnClientList.php and returnSiteList.php that successfully return json arrays with a label/value pair.
My problem is that I cannot reduce the site list, after selecting the client. The event successfully fires but I only get the full list back. As you will see, the code is using the getJSON request which i know from the manual sends a HTTP GET. Looking at the network panel of chrome reveals that there is no GET value actually being sent.
Hopefully something obvious but I am new to jquery so help appreciated.
My code:
JS
$('#edit-shift').cascadingDropdown({
selectBoxes: [
{
selector: '.clients',
source: function(request, response) {
$.getJSON('returnClientList.php', request, function(data) {
var selectOnlyOption = data.length <= 1;
response($.map(data, function(item, index) {
return {
label: item.label,
value: item.value,
selected: selectOnlyOption // Select if only option
};
}));
});
}
},
{
selector: '.sites',
requires: ['.clients'],
source: function(request, response) {
$.getJSON('returnSiteList.php', request, function(data) {
var selectOnlyOption = data.length <= 1;
response($.map(data, function(item, index) {
return {
label: item.label,
value: item.value,
selected: selectOnlyOption // Select if only option
};
}));
});
}
},
{
onChange: function(event, value, requiredValues){}
}
]
});
PHP
//this script returns a json array for use in jquery autocomplete fields for site lists...
header('Content-type: application/json');
require("connect.php");
$client_id = $_GET['?'];
//do the query for sites that are active
$sql = "SELECT * FROM site WHERE active=1 AND client_id='$client_id' ORDER BY site_name ASC";
$result = mysql_query($sql) or die('Error: ' . mysql_error());
//loop the results and create php array
while($row = mysql_fetch_array($result)){
$arr[] = array('label' => $row['site_name'], 'value' => $row['id']);
}
echo json_encode($arr);
Ended up writing my own solution to this and scrapped the plugin. Enjoy.
//dynamically returns the sites once the user chooses a client - edit/add shift form
$('.client-id').change(function () {
var selectedClient = $(this).val();
if (selectedClient != null && selectedClient != '') {
$.getJSON('returnSiteList.php', { id: selectedClient },
function (Sites) {
var siteSelect = $('.site-id');
siteSelect.empty();
$.each(Sites, function (index, site) {
siteSelect.append($('<option/>', {
value: site.value,
text: site.label
}));
});
});
}
});
I am developing a website where I use a custom build jQuery widget to load data into multiple divs.
This is the code for the widget:
(function ($, window, document, undefined) {
$.widget ("my.contentloader", {
options: {
loading_message: true
},
_create: function () {
var that = this;
$.ajax ({
type: "POST",
url: that.options.url,
data: {data: that.options.formdata, limit: that.options.limit, offset: that.options.offset},
beforeSend: function (html) {
if (that.options.loading_message) {
$(that.options.target_area).html ("<div id='loading'>Loading</div>");
}
},
success: function (html) {
if (that.options.loading_message) {
$('#loading').remove ();
}
$(that.options.target_area).html (html);
},
error: function (html) {
$(that.options.error_area).html (html);
}
});
},
_setOption: function (key, value) {
this.options[key] = value;
$.Widget.prototype._setOption.apply (this, arguments);
}
});
})(jQuery, window, document);
I load data using the widget like this:
$('#targetdiv').contentloader ({
url: '<?php echo $action_url; ?>',
target_area: '#popup_box',
formdata: {'username' : username_email, 'password' : password}
});
I am having problems loading multiple instances on the same page.
Is there a way to not instantiate the widget on a specific div like this?
$('#targetdiv').contentloader
I think you need to assign each instance to a variable. That way, you can control each instance, or write a function that iterates over an array of instances.
var contentLoaders = [];
$('.target-div').each(function(i, data) {
contentLoaders[i] = $.widget("my.contentloader", { ... });
});
So then you should be able to operate on each loader independently, like:
for (var i in contentLoaders) {
var contentLoader = contentLoaders[i];
contentLoader.option( ... );
}
Also, you're using the DOM ID $('#loading') for multiple instances of the widget. This is wrong. You need to either use separate loaders for each widget, or else check to see if the ID exists and only insert the new node if it doesn't exist. And same for removing it.
** I've added this example block, hope it helps: **
//
// This is a way to do it if you want to explicitly define each contentloader.
// Below that, I'll write out a way to define the contentloaders in a loop.
//
var contentLoader1 = $('#targetdiv1').contentloader ({
url: '<?php echo $action_url; ?>',
target_area: '#popup_box',
formdata: {'username' : username_email, 'password' : password}
});
contentLoader1.option('url', 'http://google.com');
var contentLoader2 = $('#targetdiv2').contentloader ({
url: '<?php echo $action_url; ?>',
target_area: '#popup_box',
formdata: {'username' : username_email, 'password' : password}
});
contentLoader2.option('url', 'http:/apple.com');
// Push each widget instance into an array of widget objects
var contentLoaders = [];
contentLoaders.push(contentLoader1);
contentLoaders.push(contentLoader2);
for (var i in contentLoaders) {
console.log(i, contentLoaders[i].option('url'));
}
// Should print:
// 0 http://google.com
// 1 http://apple.com
//
//
// How to set a bunch of widgets at once from an array of content loader data
//
//
var contentLoaderData = [
{
divid: '#targetDiv1',
url: 'google.com',
formdata: {
username: 'joeshmo',
password: 'joeshmo1'
}
},
{
divid: '#targetDiv2',
url: 'apple.com',
formdata: {
username: 'plainjane',
password: 'plainjane1'
}
}
];
// Array of widget instances
var contentLoaders = [];
$.each(contentLoaderData, function(index, value) {
var contentLoader = $(this.divid).contentloader({
url: this.url,
target_area: '#popup_box',
formdata: {'username' : this.formdata.username, 'password' : this.formdata.password}
});
// Push each contentLoader instance into the contentLoaders array
contentLoaders.push(contentLoader);
});
for (var i in contentLoaders) {
console.log(i, contentLoaders[i].option('url'));
}
// Should print:
// 0 http://google.com
// 1 http://apple.com