Adding multiple products to WooCommerce Cart - AJAX - javascript

I am trying to find a way to select multiple item variations from a Vue frontend inside a WooCommerce website.
I installed the WC Ajax Cart plugin in order to have some sort of endpoint that I can call using AXIOS.
Each option I select ads and I to an array of called selectedProducts
When the user presses on checkout what I do is:
this.textCheckout = "Checking you out..."; //No pun intended actually xD
this.products.forEach((item, i) => {
if(this.selectedProducts.includes(item.variation_id)) {
let adder = new FormData();
adder.append('attribute_alege-zona', Object.values(item.attributes)[0]);
adder.append('attribute_tratament', Object.values(item.attributes)[1]);
adder.append('quantity', 1);
adder.append('product_id', item.variation_id);
adder.append('variation_id', item.variation_id);
console.log(adder);
let me = this;
let apel = setTimeout(function(){
axios({
method: "post",
url: "https://HOST/?wc-ajax=add_to_cart",
data: adder,
headers: { "Content-Type": "multipart/form-data" },
})
.then(function (response) {
})
.catch(function (response) {
console.log(response);
});
},1000)
}
});
let goCart = setTimeout(function() {
window.location.href = "https://HOST/cart";
}, 1500 * this.alese.length);
You might ask why I added those setTimeout functions. In my head it made sense that in case the call takes longer I am sure I will add all the products to the cart. The max I can add is two.
If let's say I select 5 products, when I get redirected to my cart I can only see 2 of them, sometime 3 (it is really really random)
Do you have any idea how can I add all the selected products to the same cart session ?
Thanks in advance!

Ok so after inspecting the network tab this is the solution that works:
this.toBeAdded.forEach((item, i) => {
let adder = new FormData();
adder.append('attribute_alege-zona', Object.values(item.attributes)[0]);
adder.append('attribute_tratament', Object.values(item.attributes)[1]);
adder.append('quantity', 1);
adder.append('product_id', item.variation_id);
adder.append('variation_id', item.variation_id);
console.log(adder);
let me = this;
let send = setTimeout(function() {
axios({
method: "post",
url: "https://HOST/?wc-ajax=add_to_cart",
data: adder,
headers: { "Content-Type": "multipart/form-data" },
withCredentials: true,
})
.then(function (response) {
if(i == 0) {
document.cookie = document.cookie + " woocommerce_items_in_cart=1; path=/";
}else if (i==me.toBeAdded.length-1){
window.location.href = "https://HOST/cart";
}
})
.catch(function (response) {
console.log(response);
});
}, i*500);
});
Basically if it is the fist product I am adding the WC items in cart cookie.
If it is the last product I redirect to cart.
Each call is delayed by i*500, where i is the index of the array of products

Related

Make sure to wait until all Ajax calls are completed before displaying the result

I make do with a problem after several tries and I rely on you to find a solution.
What I want to do is to add in a table the number of viewers of a specific streamer via the twitch api.
So I do my ajax call well:
viewerCount(streamer){
let viewers = [];
let streamerList = streamer;
for (let i = streamer.length-1 ; i >=0 ; i--){
$.ajax({
type: 'GET',
url: 'https://api.twitch.tv/helix/streams?user_login='+streamerList[i]+'',
dataType:'JSON',
headers: {
"Client-ID": 'bgbezb2vov7jc4twxauhw3yh30ubbx',
"Authorization": "Bearer "+this.auth
},
success: function(data) {
viewers.push([i, streamerList[i], data['data'][0]['viewer_count']])
},
error: function(data){
console.log(data)
}
})
};
}
Then I push the result in my table and at the end when I do console.log I have the index, the name of the streamer and the number of viewers in my table.
The problem is that before I want to display the result, I'd like all the ajax calls to be completed before displaying. I tested with promise.all but unfortunately I didn't get anywhere.
Thank you in advance for your help.
using the condition below, you will be able to check if all calls are completed: (check code comments):
viewerCount(streamer){
let viewers = [];
let streamerList = streamer;
let completedCalls = 0; // init a new variable to base on
for (let i = streamer.length-1 ; i >=0 ; i--){
$.ajax({
type: 'GET',
url: 'https://api.twitch.tv/helix/streams?user_login='+streamerList[i]+'',
dataType:'JSON',
headers: {
"Client-ID": 'bgbezb2vov7jc4twxauhw3yh30ubbx',
"Authorization": "Bearer "+this.auth
},
success: function(data) {
viewers.push([i, streamerList[i], data['data'][0]['viewer_count']]);
completedCalls++; // increase completedCalls
},
complete: function() { // <-- here the new code
if(completedCalls === streamer.length) {
displayYourResult();
}
},
error: function(data){
console.log(data)
}
})
};
}

Change button using CSS or by using scripts

I am using data tables to show data and a button is present opposite to each entry of my data table.
That button has onclick which captures the parameter present in a row and save in array.
I want to change color when certain entry is selected and reset on deselect.
Here is what I am doing,
function select(name){
var property = document.getElementById('checkRule');
if( rArray.includes(name) ){
property.style.backgroundColor = "#FFFFFF"
const index = rArray.indexOf(name);
if (index > -1) {
rArray.splice(index, 1);
}
}else{
rArray.push(name);
property.style.backgroundColor = "#28a0ff"
}
console.log('ARRAY >> ', rArray);
}
This code is only changing color of very first element of of data table.
How shall I make it work?
Assuming that you want to send the ajax request after you select a radio button and you click on go.
You do not need to change the html, but you will have to change the javascript file.
$("#go").click(() => {
var result = $("input:radio[name='doamin']:checked").val();
console.log("result > ", result);
var Data = {
result1: result,
};
console.log("postData > ", Data);
$.ajax({
url: "import",
headers: {
Accept: "text/plain",
"Content-Type": "application/json",
},
type: "POST",
dataType: "html",
data: JSON.stringify(Data),
success: function (data) {
console.log(data);
},
error: function (err) {
console.log(err);
},
});
});
Ideally, you want to send a POST request, as there is a body attached.

How to re-render PayPal Smart Buttons with different values?

I'm using PayPal Smart button on my web site, and after each payment is made the user is able to make another new payment with PayPal smart button. The problem is that once I've created my PayPal button I'm unable to set the new createOrder in it with new orderID.
So in my website I have the following function which init the PayPal button if there is yet a pending order or it's called when a new order is made and I get a new orderID.
function initPayPal(orderID, publicKey) {
var paymentID = $("#paypal-button-container").data("payment-id")
var PAYPAL_SCRIPT =
"https://www.paypal.com/sdk/js?client-id=" + publicKey + "&currency=EUR&intent=capture&commit=false&vault=true";
var script = document.createElement("script");
script.setAttribute("src", PAYPAL_SCRIPT);
script.onload = function () {
paypal
.Buttons({
style: {
shape: "rect",
color: "gold",
layout: "horizontal",
label: "paypal",
tagline: false,
height: 52,
},
createOrder: async function () {
$(".body-payments").hide();
$(".loader-payments").show();
const res = await fetch(
"/api/payment/paypal/" + paymentID + "/" + orderID,
{
method: "post",
headers: {
"content-type": "application/json",
},
credentials: "include",
}
);
const data = await res.json();
return data.id;
},
onApprove: async function (data) {
const res = await fetch(
"/api/payment/paypal/capture/" + paymentID + "/" + data.orderID,
{
method: "post",
headers: {
"content-type": "application/json",
},
credentials: "include",
}
);
if (localStorage.STBOrder) {
localStorage.removeItem("STBOrder");
}
$("#modalPayments").modal("hide");
$("#modalSuccess").modal("show");
$(".body-payments").show();
$(".loader-payments").hide();
},
onCancel: function (data) {
$(".body-payments").show();
$(".loader-payments").hide();
},
})
.render("#paypal-button-container");
};
document.head.appendChild(script);
}
Client id is set dynamically as each user on its own page will get the payment on its own account. The issue here is that the button is initiated the first time all works fine, but if I dismiss the order and I call this function again it will create another button instead of setting new values to existing (already rendered) one.
There should be no need to init the button more than once.
Initialize the button one time (on page load).
Then, inside createOrder, ensure paymentID and orderID reference non-local variables or are replaced with function calls that will return the correct value at the time you want them to, e.g. some kind of getOrderID() of yours, and $("#paypal-button-container").data("payment-id") , in place of your existing local variables
In the unlikely event that you really did need to render the buttons more than once, you could save a reference to myButtons = paypal.Buttons({..}) before calling .render() --- and then, later myButtons.close() before saving a new reference to and rendering a new buttons object. But again, there should be no reason to do that here.

AngularJS calling a function once I've made an array

Background
I am making a angularJS app
I have an object called a Map. The Map has an array of Transforms IDs.
I need to fetch each transform in the Maps's array, then save these to an array of Transforms instead of an array of Transform IDs. So far, I have this done, except calling the function at the correct time.
I want to call a function: doStuffWithTransformArray using the completed array.
Question
How do I call a function only when the array is finished being filled?
Current Code (this only works sometimes)
$scope.makeTransformArray = function () {
$scope.transforms = [];
$scope.map.transforms.forEach(function(eachTransformID, index) {
Transform.get({uuid: eachTransformID.uuid}, function(transformResult) {
$scope.transforms[index] = transformResult;
if ($scope.transforms.length == $scope.map.transforms.length) {
$scope.doStuffWithTransformArray($scope.transforms);
return;
}
});
});
}
Problem
This current code will work only when the last Transform in the Map's array finishes last.
It will call the doStuffWithTransformArray twice if when the array is filled like this: (5) [y, y, y, empty, y]
The fourth one is not yet filled but the fifth finished.
EDIT 1
Here is my transform Service:
angular.module('serviceregistryApp')
.factory('Transform', function ($resource) {
var url = 'api/v1/transform/:uuid';
return $resource(url, {transform: '#transform', uuid: '#uuid'}, {
'save': {method: 'POST', headers: {"Authorization": "ABC"}},
'delete': {method: 'DELETE', headers: {"Authorization": "ABC"}},
'post': {method: 'POST', headers: {"Authorization": "ABC"}},
'get': {
method: 'GET', isArray: false,
headers: {
"Authorization": "ABC"
}, url: "api/v1/transform/:uuid",
transformResponse: function (data) {
return JSON.parse(data);
}
}
});
});
EDIT 2
As suggested in the comments, this seems to work, but I find it kinda ugly.
$scope.makeTransformArray = function () {
$scope.transforms = [];
var counter = 0;
$scope.map.transforms.forEach(function(eachTransformID, index) {
Transform.get({uuid: eachTransformID.uuid}, function(transformResult) {
$scope.transforms[index] = transformResult;
counter = counter + 1;
if (counter == $scope.map.transforms.length) {
$scope.doStuffWithTransformArray($scope.transforms);
return;
}
});
});
}
NgResource instances have an attached property named $promise that can be used for transforming results after they arrive from the server:
$scope.object = Transform.get({uuid: eachTransformID.uuid});
var promise = $scope.object.$promise;
var newPromise = promise
.then(function(object) {
var obj2 = transform(object);
$scope.object = angular.copy(obj2);
return obj2;
}).catch(function(errorResponse) {
console.log("ERROR", errorResponse.status);
throw errorResponse;
});
$scope.object.$promise = newPromise;
Use the promise for subsequent chaining or consolidation with $q.all.

Two requests in one time immediatly. ASP MVC + JQuery Ajax

MVC application (ASP.NET MVC, client: jquery).
Problem: The second ajax-request wait, when the first ajax request will done.
I need, when the first and the second ajax-requests executes immediatly in one time.
The page sends to server to determine the count of records (the first ajax-request), very long (~5-7 seconds).
The operator click the buttom to open the card to edit it (the second ajax-request, fast, get the Dto-model).
The user doesn't need to wait the first request, he wants to work immediatly.
As a result, in Chrome in network page, two requests in status 'pending'. The second waits the first.
Question, how can I send requests, to execute asynchronously ?
The first ajax-request:
`window.jQuery`.ajax({
type: 'POST',
url: Url.Action("GetCountBooks", "Book");
contentType: "application/json; charset=utf-8",
dataType: 'json',
data: JSON.stringify({ typeBook: "...", filter: "..." };),
success: function (data) {
// show in UI page the count of books by filter and params
},
error: function (data) {
//show error
}});
public class BookController : Controller
{
[HttpPost]
public NJsonResult GetCountBooks(string typeBook, Filter filter)
{
var data = DbProvider.GetCountBooks(typeBook, filter)
if (data.Result == ResultType.Success)
{
var count = data.Data;
return new NJsonResult
{
Data = new { Data = count }
};
}
return new NJsonResult
{
Data = new { Error = "Error while counting the books." }
};
}
}
The second ajax-request:
`window.jQuery`.ajax({
type: 'POST',
dataType: 'json',
contentType: "application/json",
url: Url.Action("GetBookById", "Book"),
data: JSON.stringify({ id: bookId }),
success: function (data) {
// show jquery dialog form to edit dto-model.
},
error: function (data) {
//show error
}});
public class BookController : Controller
{
[HttpPost]
public NJsonResult GetBookById(int id)
{
var data = DbProvider.GetBookById(id)
if (data.Result == ResultType.Success)
{
var book = data.Data;
return new NJsonResult
{
Data = new { Data = book }
};
return new NJsonResult
{
Data = new { Error = "The book is not found." }
};
}
return new NJsonResult
{
Data = new { Error = "Error while getting the book." }
};
}
}
I Cannot union ajax requests into one! The user can send various second request.
You need a fork-join splitter to fork 2 tasks and join based on some condition.
For example here is my implementation:
function fork(promises) {
return {
join: (callback) => {
let numOfTasks = promises.length;
let forkId = Math.ceil(Math.random() * 1000);
fork_join_map[forkId] = {
expected: numOfTasks,
current: 0
};
promises.forEach((p) => {
p.then((data) => {
fork_join_map[forkId].current++;
if (fork_join_map[forkId].expected === fork_join_map[forkId].current) {
if (callback) callback(data)
}
})
});
}
}}
Pass any number of async tasks (promises) into fork method and join when all are done. The done criteria here is managed by simple global object fork_join_map which tracks the results of your fork-join process (global is not good but its just an example). The particular fork-join is identified by forkId which is 0..1000 in this example which is not quite good again, but I hope you got the idea.
With jQuery you can create promise with $.when( $.ajax(..your ajax call) )
In the end you can join your promises like this
fork([
$.when( $.ajax(..your ajax call 1) ),
$.when( $.ajax(..your ajax call 2) )
]).join(() => {
// do your logic here when both calls are done
});
It's my own implementation, there may be already-written library functions for this in jQuery - I dont know. Hope this will give you a right direction at least.
The solution is to add attribute to Asp Controller: [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
http://johnculviner.com/asp-net-concurrent-ajax-requests-and-session-state-blocking/

Categories

Resources