Updating the cart number in Shopify with AJAX - javascript

Apologies if this is somewhere else but I can't find a better way of doing this.
I want to update the cart total number in realtime with AJAX anytime a product is added, removed, or quantity is changed.
I have it working but there has to be a better way. It feels wrong to poll constantly for changes and I certainly don't want to end up with a memory leak.
Right now I have the item count being fetched from JSON and then changing the number in the div by polling every second, giving the user the illusion that the number is being updated when they change something.
I've tried adding a listener to the add to cart button (works) as well as listening on the quantity selector (doesn't work).
I'm sure I'm just being a noob so any help is appreciated. Code below:
// Fetch the cart in JSON and change cart quantity on the fly after first product added to cart
function doPoll(){
setTimeout(function() {
$.getJSON('/cart.js', function(cart) {
$('.cart-count').html(cart.item_count);
doPoll();
}, 1000);
}
Update
So the fix was actually very simple. The reason I couldn't attach a listener on a particular element within the cart was the cart wasn't loaded yet via ajax (duh!)
So all I did was remove the constant polling and instead ran my function anytime the ajax on the page was fully loaded:
$( document ).ajaxComplete(function() {
//Do stuff here
});

Actually it's even simpler. If an ajax driven cart adjustment happens Timber gives you an overridable hook. Just override ShopifyAPI.onCartUpdate
e.g.
ShopifyAPI.onCartUpdate = function(cart){
//do something new with the cart contents
$('.cart-count').html(cart.item_count);
};
Other than that your cart count is also available on page load via liquid so if you combine the two you're covered:
<div class="cart-count">{{ cart.item_count }}</div>

If you have not implemented any other way to add to cart, it's very simple.
If you look in the AJAX functions file you have mentioned, especially in functions at lines 101 and 113, you can see that those function are related to cart update. Add your code $('.cart-count').html(cart.item_count); before callback(cart); and you'll be good to go.

A more up to date solution without Jquery. You can use fetch and then use the data returned to populate the cart count.
fetch('/cart.js')
.then(response => response.text())
.then((responseText) => {
data = JSON.parse(responseText);
var counterEl = document.querySelectorAll('.js-cart-item-count');
counterEl.forEach((element) => {
element.innerHTML = data.item_count
})
})

Related

How to define where function is slows down?

I have report view where a bunch of controls located, there is an example code:
#Html.DevExpress().DropDownEdit(
settings =>{
settings.Name = "SomeList";
settings.Width = 100;
settings.SetDropDownWindowTemplateContent(c =>{
#Html.DevExpress().ListBox(
...
).BindList(ViewData["SomeList"]).Render();
some code...
).GetHtml()
When it render first time it works fine, but in business logic there is another drop down let's call it dropdown #2. When user select some values in dropdown#2 JS handle this action send request to the server to get actual value for first dropdown, then clear all rendered values from dropdown #1 and insert new items.
And problem in performance lies in how devexpress create new items on client side. From backend there is now 8000 new items can be added to dropdown #1 and it keep growing. And user must wait 5-10 seconds when browser render it. I tried to research what is happening when new item is creating but there are a lot of devexpress function which call another functions.
JS code look's like:
SomeList.BeginUpdate();
l = data.SomeList.length;
while (x < l) {
SomeList.AddItem(data.SomeList[x].Name, data.SomeList[x].Id);
x++;
}
SomeList.EndUpdate();
BeginUpdate and EndUpdate is devexpress functions that prohibbit browser render control while adding new items. Without this function it takes eternity to finish adding new items.
I know start point of problem it is - item creating code SomeList.AddItem(...).
So my question: is there way to do something like console.trace(SomeList.AddItem(...)) and see full trace which will be executed on first code pass?
Also is there way to determine which function call take alot of time to execute?

Update HTML data without creating more elements on the webpage

Have an interval that loads the html every 3 secs, but want to refresh it and not keep adding more under the already made code.
async function Products(){
setInterval(async function() {
const response = await fetch('http://localhost:3000/api/products');
const data = await response.json();
const mainContainer = document.getElementById("myData");
for (let obj of data) {
const div = document.createElement("div");
div.innerHTML = `${obj["sku"]}: ${obj["name"]}`;
mainContainer.appendChild(div);
}
}, 10000)
}
When I click a start button everything works, but how do i make it refresh the already made HTML rather than repeatedly recreating it with the interval. Trying to figure out a good approach to this. Thanks
Create and append a <div> immediately, and in the interval, assign to its innerHTML:
function Products() {
const container = document.getElementById("myData").appendChild(document.createElement("div"));
setInterval(async function () {
const response = await fetch('http://localhost:3000/api/products');
const data = await response.json();
container.innerHTML = '';
for (let obj of data) {
container.innerHTML += `${obj["sku"]}: ${obj["name"]}`;
}
}, 10000)
}
I think what you are trying to do is implement a Serie of observable elements that you can look only for those who have changed instead of all the data, something like React does with the virtual DOM.
Considering the code tout already posted, refreshing elements at a set interval is a bad idea. What if you have 1000 user refreshing at the same time? What if it cause your response tone to be more then 3 seconds?
That said, if you really want to work on creating something like that, you have to find a way to load not all the products from the api, but only the ones that are different. Again, if you want to keep it that way, here are, in my opinion, what you could do:
Start by loading all the product on the page, but set an interval to check a new Endpoint which would tell you what products have been added after the last one.
Use either an index or a key to identify which product is which, so tout know the ones you have.
You need a way to know quick product was updated since the last load.
That's a start. You can implement these in different way. I would suggest having a timestamp for the time created and updated, so you can then query only those who fall after this timestamp.
Add a dynamic ID to your product elements, (e.g. <div id=${sku} >**Product Here**<\div>
That way, you can track your product and recreate only the ones who changed/are new.
That's obviously a complicated way of implementing an open connection, if you want another solution, you could also open a Socket with your api, which would send event for data updated and created and would ultimately make your 3 second ping obsolete, resulting in a better scalability in my opinion.

Ag-grid: duplicate node id 107 detected from getRowNodeId callback , this could cause issues in your grid

I am going to do live data streaming on ag-grid datatable, so I used DeltaRowData for gridOptions and added getRowNodeId method as well which return unique value 'id'.
After all, I got a live update result on my grid table within some period I set, but some rows are duplicated so I can notice total count is a bit increased each time it loads updated data. The question title is warning message from browser console, I got bunch of these messages with different id number. Actually it is supposed not to do this from below docs. This is supposed to detect dups and smartly added new ones if not exist. Ofc, there are several ways to get refreshed data live, but I chose this one, since it says it helps to persist grid info like selected rows, current position of scroll on the grid etc. I am using vanilla js, not going to use any frameworks.
How do I make live data updated periodically without changing any current grid stuff? There is no error on the code, so do not try to speak about any bug. Maybe I am wrong with current implementation, Anyway, I want to know the idea or hear any implementation experience on this.
let gridOptions = {
....
deltaRowDataMode: true,
getRowNodeId = (data) => {
return data.id; // return the property you want set as the id.
}
}
fetch(loadUrl).then((res) => {
return res.json()
}).then((data) => {
gridOptions.api.setRowData(data);
})
...
If you get:
duplicated node warning
it means your getRowNodeId() has 1 value for 2 different rows.
here is part from source:
if (this.allNodesMap[node.id]) {
console.warn("ag-grid: duplicate node id '" + node.id + "' detected from getRowNodeId callback, this could cause issues in your grid.");
}
so try to check your data again.
if u 100% sure there is an error not related with your data - cut oof the private data, create a plinkr/stackblitz examples to reproduce your issue and then it would be simpler to check and help you.

Add Event Listener to Array index

I have an array that is being filled on page load by D3. I want to access that same array at another time but I still need to confirm that it has been loaded.
I don't know the propper way to do this so I just guessed a few time and I kept getting "not a function" errors
First Code (populate my svg, snapshot of current values)
d3.tsv("file.txt").then( function(data) { Loaded_Data[1]=data; document.getElementById("valve1").innerHTML = data[data.length-1]['OnOff']; //etc
});
much later, but still in a relevant timescale of async, I want to do some d3 graphing with this data, but I want to make sure that there is data in my array.
Later Code (populate graphs, weeks of data)
Loaded_Data[1].addEventListener('load', () => {console.log("success"); //d3.graphing; });
I basically want a:
while (array[1] is undefined){ Listen; }
when done => graph;
Or would it just be easier to load the entire set of documents again in another d3.tsv().then()? it seems like a waste of resources to reload the entire data. What makes this difficult is the number of sources I have to load in, and consolidating my data into one array will be (hopefully) more convenient.
just graph in the then call after you read the data:
d3.tsv("file.txt")
.then(data => graph(data)); //data is available to be used at this point
also, if I'm reading this correctly, you are listening for the array to be populated with the load event, but that is only fired when the whole page is loaded and isn't relevant to the populating of a data structure. If you need the data stored somewhere in addition to graphing, you could modify the above to:
d3.tsv("file.txt")
.then(data => {
graph(data);
myArray = data;
});

Sitecore 8 Speak UI QueryDataSource in Javascript

I am trying to access QueryDatasource results in Javascript. Everything is setup correctly. I get the items by using
var destData = this.regionQueryDatasource.get("items");
My issue is that,
I want to get those items on page load. I put this code inside initialize() then it doesn't return anything. If I call it in some button click function, then it returns data.
initialized: function () {
var destData = this.regionQueryDatasource.get("items");},
I want the querying to happen in synchronous manner. Sometimes, the items are returned as empty. I want to wait till the items are loaded.
Any help would be great. Thanks in advance!
Have you tried refreshing the DataSource before calling Get items?
E.g
this.regionQueryDatasource.refresh()
Then you can check viewModel.hasItems()
Id recommend using this.regionQueryDatasource.viewModel.items() rather than this.regionQueryDatasource.get("items");

Categories