(Code added) HELP! Stuck and confused about repeaters, collections and localstorage

#repeaters #wixstorage #collections
note: links are broken (i guess b/c my account is pretty new)

Okay I have hit my head against this wall for days, maybe someone else can be of assistance. I feel like it’s a somewhat trivial task but I also know my js-knowledge is middling at best and there could be some nuance to wix/velo that I’m just not getting.

Here’s the idea: it’s a pseudo-wishlist for a store, but unlike a wishlist - you don’t need to be logged in as a member to use it. It also has bulk add to cart with some validation to make sure a minimum number of items is added before enabling the bulk add to cart.

My current plan for the app:

  1. store an array of objects (the content of the object is a single field, productId) in localstorage under a single key
  2. on every page load, pull the contents of the localstorage into memory so it’s easy to work with - i can also forsee using that data in more places over time, so I think it makes sense to store functions in masterPage.js
  3. on product page, have a button that onClick, grabs the product id with getProduct() and adds it (push function) to the end of the array of objects
  4. every time the array of objects is changed it is saved to localstorage (overwriting what was there)
  5. on a new page have a repeater, connected to a collection that has only one field - productId.
    • put the productIds from array of objects in memory (loaded from localstorage on page load) into the repeater’s collection
    • use the productId in this collection as a reference to Products collection used by store
    • show Product Name, Description, Main Media and Price in repeater
      have the following functions on that page
    • Add All To Cart - takes all the productIds in the repeater and adds them to cart (while accounting for multiple instances of a single product - which would, i assume, up the quantity value), then clears the collection
    • Clear Personal Collection (wipes it all away)
    • nice to have: a delete button inside the repeater that removes a productId by its index in the array (which hopefully matches its index in the repeater, plus or minus one depending how repeater indexes work)

Things I’m stuck on:

  • sometimes the product isn’t added to the object (and then saved to the local storage) when Add to Personal Collection is clicked on a Product Page (https://closetgeekshow.wixsite. com/my-site-1/product-page/series-c-square), it works on my test page (https://closetgeekshow.wixsite. com/my-site-1/localstorage-test) where a product id is hardcoded to a button but its weird on Product Page
  • sometimes a blank object is added to the array when the Add to Personal Collection is clicked on a Product Page
  • the page that loads in the repeater doesn’t work and I have no idea why. I feel like i’m fundamentally misunderstanding how Repeaters, Collections and Datasets work together in code. It works fine if I fill in a bunch of productIds in my collection in the editor, but the filling it in with code doesn’t work and I have no idea why.

masterPage.js
https://jsfiddle. net/3gvzakf1/

script on Product Page:
https://jsfiddle. net/dh4y3gjp/

script on page showing the Personal Collection repeater:
https://jsfiddle. net/k6m4osfc/

You have alot of steps. Lets try and break it down into chunks and see if we can work on a chunk at a time. I can’t guarantee I can get all the steps but am willing to try.

I’ve looked at the scripts on the product page. My recommendations is that you create an ‘addToPairBuilder_click()’ on_click() event using the Wix editor for the object. Then after you’ve added the event function, insert the code into it after Wix creates the funciton for you. Click on your addToPairBuilder object, and at the right bottom of the Wix editor, select the on_click() event to create it.

Something like this:

import wixLocation from 'wix-location';
import wixData from 'wix-data';
import {addToPersonalCollection} from 'public/masterPage.js'

let product;

// Current product.
$w.onReady(async function () {
    product = await $w('#productPage1').getProduct();
});


export function addToPairBuilder_click() {
     addToPersonalCollection(product._id);
}

This keeps the onReady function clean and only contain initialization code for the page.
I only import the functions I am going to use in the page code.

I looked at your functions in masterPage.js and can’t figure out where you actually load the personalcollection.
I can not see where you call the addToPersonalCollection function to add items to the PersonalCollection. It looks like the functions are fully completed yet, based on the comments I see.

I don’t know if this is overly complicating things. But what if you could take the list of productids and then in your repeater page, modify the dataset to filter only the matching productids, using the ‘hasAll( )’ filter option.

I think the whole json stringfy etc is overkill. I think you just need a simple string array to keep track of the product ids. Then you would use this array in the hasAll() parameter of the dataset filter.

I still don’t have a clear picture on how a user would go to your website to use it if they didn’t log in.

Where do they start, how do they add items to the productid array?
Don’t you also need to keep track of a quantity, along with the productId?

You state “but its weird on Product Page”…

In your code, you are not handling the returned Promise from the call to getProduct. You have this:

 product = $w('#productPage1').getProduct();

But you need to handle the Promise. Use an await like this:

product = await $w('#productPage1').getProduct();

Note, that you will need to add an async clause to your onReady() function, like this:

$w.onReady(async function () {

@paul krzyz: thank you for the direction, you’re right I’m not loading the collection array into the product page, which does not make sense. I guess I thought I could store it in the personalCollectionObj variable in masterPage.js . But I’m not doing actually that, I wrote an update after posting this to pull in that variable on the Product Page (https://jsfiddle. net/t4ckqorm/) but I’m not sure if even that is right.

And you’re probably right about using the editor function to create the functions that fire on button click, I think I’d tried that earlier but it seemed to not be working, but I probably did it wrong.

For the hasAll() filter option, which if I understand correctly, would be a filter applied to .query of the Products db that would filter said db to only show productIds that are present in my personal collection list? Is that right? What do I need to do in the editor with my dataset and repeater and stuff? Currently the repeater’s contents is a collection I created called pairBuilderList, the collection has one column only - for a product id reference. And then this dataset is connected to the Products collection, so I can pull out Product info based on the id in the collection to display in the repeater. In the script for my custom page (https://jsfiddle. net/k6m4osfc/), I’m trying to load in the collection data on line 15. But I don’t think that is how I load in data to a repeater with code, very confused about that stuff and what the heck I need to do.

In terms of how the feature works for non-members, this is my intent:

  1. user comes to a product page and clicks “Add to my Personal Collection”
  2. product id for that product is added to the personalCollectionObj and then the personalCollectionObj is stringified and pushed into localstorage. The stringify operation is necessary as localstorage can only store strings.
  3. each time the user clicks “Add to my Personal Collection”, a new product id is added to the collection and every time the personalCollectionObj is modified, it is saved in localstorage.
  4. when user goes to View My Personal Collection, the data is pulled in from localstorage and applied to the repeater. I’ve attempted to keep the data in localstorage in the same format that the repeater requires so I can directly apply that data without modifying its structure.

As for why this doesn’t store quantities, I feel it’s easier to do the quantity calculations while viewing the data in a repeater versus having to look up a product id every time it’s added personal collection to determine whether or not I need to create a new object in the array or edit the quantity value for an existing object.

@yisrael-wix : Thank you! I think I had await in there in some earlier version but I must have removed it somewhere in my total confusion. That code for the Product Page looks much much cleaner, thank you. I don’t think I’ve used Promises before and I definitely haven’t created my own library to import so I think most of my problems here stem from me using a thing without really understanding what I’m doing.


I did some more research yesterday looking at localstorage todo list example apps because I think fundamentally, it’s essentially the same thing. A list of things, stored in localstorage that can be modified. I think looking at those examples coupled with the suggestions I’ve been given here will help me get this script to function the way I intend.

Perhaps the wix-storage example will help a little.

See the following for more information about Promises:

This’ll be a HUGE help, I had not stumbled upon your site yet so thank you for this example that uses a repeater!

I’ve made some changes so far today. I think , I have the “Add to Personal Collection” button on Product Page working. If I monitor the localStorage in inspector, I can see new productIds getting appended to the personalCollection key. So I think that part is working now.

I’m now going to dig into your GET example and see how I can display the contents of the personalCollection array in a repeater.


State of my code

Product Page script:
I’m now only pulling in the loadCollection and addToPersonalCollection functions from masterPage.js.
On page load, the localStorage data is pulled into personalCollectionObj and that is passed to addToPersonalCollection in addition to the product._id.

import wixLocation from 'wix-location';
import wixData from 'wix-data';

import {
    loadCollection, addToPersonalCollection
} from 'public/masterPage.js'

let personalCollectionObj = loadCollection();
let product;

// Current product.

$w.onReady(async function () {
    product = await $w('#productPage1').getProduct();
});


export function addToPairBuilder_click(event) {
    addToPersonalCollection(personalCollectionObj, product._id);
    personalCollectionObj = loadCollection();
}

masterPage.js script:
I’ve updated the addToPersonalCollection function to take in the collection array. It appends the productid to the list and saves it in localstorage.
loadCollection now returns an empty array or the value in localstorage.

import {local} from 'wix-storage';

export function loadCollection () {
        return local.getItem("personalCollection") ? JSON.parse(local.getItem("personalCollection")) : []; 
}

// storeCollection(personalCollection);
export function storeCollection (data) {
    local.setItem("personalCollection", JSON.stringify(data));
}

// addToPersonalCollection("000-000")
export function addToPersonalCollection (collection, productId) {
    // create object for productId
    const collectionRecord = { "productId" : productId};

    collection.push(collectionRecord);
    storeCollection(collection);
}

export function removeByArrayIndex(collection, index) {
    collection.splice(index, 1);
    storeCollection(collection);
}

export function clearPersonalCollection() {
    local.removeItem("personalCollection");
}

@yisrael-wix : what would I need to do to adapt this repeater into one that pulls from another collection? I’ve removed my existing repeater connections and collections and datasets to start over from scratch there.

So I currently have a single item in the repeater, it currently does not have any dataset connection or anything. Is the idea with repeaters that you can throw just about any array of objects at it and it’ll repeat for every records, and then run the OnItemReady code for each to figure out what object fields in the array get assigned to what elements (if any)?

If that’s true, then why is the productId field in my personalCollectionObj variable not appearing in the repeater data?

After I get this bit working I intend to use this productId reference field to pull in values from the Product db, but I’m totally lost on how I approach that.

Thanks for all the help, I was down in the dumps yesterday but today I’m in a much better place now that I see some progress again.

Code on personal collection page (for now)

import wixLocation from 'wix-location';
import wixData from 'wix-data';
import {local} from 'wix-storage';
import {
    loadCollection
} from 'public/masterPage.js'

let personalCollectionObj = loadCollection();

$w.onReady(function () {
    $w("#pairbuilderRepeater").onItemReady( ($item, itemData, index) => {
        $item("#repeaterTitle").text = itemData.productId;
        /*$item("#repeaterDesc").text = itemData.description;
        $item("#repeaterPrice").text = itemData.price;
        $item("#repeaterId").text = itemData.productId;
        $item("#repeaterImage").src = itemData.mainMedia;*/
    } );
    $w('#arrayDebug').text = JSON.stringify(personalCollectionObj);
    $w('#pairbuilderRepeater').data = personalCollectionObj;
});

I was missing an _id field in my dataset, it’s apparently needed for the repeater to load. So I’ve updated the addToPersonalCollection function in masterPage to include an _id value (i just used Date.now.tostring for ease).

export function addToPersonalCollection (collection, productId) {
    // create object for productId
    const collectionRecord = { "_id": Date.now().toString(), "productId" : productId};

    collection.push(collectionRecord);
    storeCollection(collection);
}

Now my array data appears in the repeater page (https://closetgeekshow.wixsite. com/my-site-1/build-pair) , time to figure out how to make it pull data from Products db based on that Id.

Getting some weirdness from my loadCollection() function, it returns a console error if the localStorage value doesn’t exist been manually entering the first one to get around that for now. I thought local.getItem(“personalCollection”) sufficient to check if the value exists but perhaps not, getting conflicting information in my searches about it (yay).