Custom Element but In Repeater `need answer`

Hello guys I wanted to create my own “my orders” page with custom elements and repeaters. To create this:


I needed to insert an custom element into repeater and I did it I also made the custom element dynamic. I’m sending JSON string array via Velo setAttribute then I’m making the String array back to normal array via JSON.parse and it gets the data but the problem is custom elements is showing the last data on each repeater item even if I use $item selector inside repeater. Here is my codes:

Frontend Orders Page:

import wixMembers from 'wix-members';
import { getOrders } from 'backend/Wix-Data/orders.jsw'

$w.onReady(function () {
    initPage()
});

let memberId;
let memberOrders;

async function initPage() {
    memberId = await getMemberId();
    await getOrderData()
    await ordersRepeater()
}

function getMemberId() {
    return wixMembers.currentMember.getMember()
        .then((memberData) => {
            return memberData._id;
        })
        .catch((err) => {
            console.error(err)
        })
}

function getOrderData() {
    return getOrders(memberId)
        .then((orderDatas) => {
            return memberOrders = orderDatas;
        })
        .catch((err) => {
            console.error(err)
        })
}

function ordersRepeater() {
    $w("#myOrdersRepeater").data = memberOrders;

    $w("#myOrdersRepeater").onItemReady(($item, itemData, index) => {
        $item("#orderDate").text = itemData.orderDate.toLocaleDateString();
        $item("#orderId").text = itemData.orderNo.toString();
        $item("#orderTotal").text = itemData.totals.total.toString();

        $item("#expandDetails").onClick((event) => {
            if ($item("#orderDetailsBox").collapsed === true) {
                $item("#orderDetailsBox").expand()
            } else {
                $item("#orderDetailsBox").collapse()
            }
        })

        console.log(itemData.lineItems)
        $item("#orderLineItems").setAttribute('data', JSON.stringify(itemData.lineItems));
    })
}

Backend getOrders function:

import wixData from 'wix-data'

export function getOrders(memberId) {
    let options = {
        "suppressAuth": true,
    };

    return wixData.query("Stores/Orders")
        .eq("buyerInfo.id", memberId)
        .find(options)
        .then((orders) => {
            if (orders.items.length > 0) {
                let items = orders.items;

                let frontendData = items.map((order, index) => {
                    if (order.billingInfo.address != undefined) {
                        return {
                            "_id": order._id,
                            "formattedAddress": order.billingInfo.address.formatted,
                            "memberEmail": order.buyerInfo.email,
                            "memberLastName": order.buyerInfo.lastName,
                            "memberFirstName": order.buyerInfo.firstName,
                            "memberId": order.buyerInfo.id,
                            "memberPhone": order.buyerInfo.phone,
                            "orderDate": order._dateCreated,
                            "orderNo": order.number,
                            "lineItems": order.lineItems.map((product) => {
                                return {
                                    "productName": product.name,
                                    "productQuantity": product.quantity,
                                    "productSku": product.sku,
                                    "tax": product.tax,
                                    "productPrice": product.priceData.price,
                                    "totalPrice": product.priceData.totalPrice,
                                    "options": product.options,
                                    "customTextFields": product.customTextFields,
                                    "productImage": product.mediaItem.id,
                                    "discount": product.discount
                                }
                            }),
                            "paymentStatus": order.paymentStatus,
                            "totals": order.totals
                        }
                    } else {
                        return {
                            "_id": order._id,
                            "formattedAddress": "Adres bulunamadı!",
                            "memberEmail": order.buyerInfo.email,
                            "memberLastName": order.buyerInfo.lastName,
                            "memberFirstName": order.buyerInfo.firstName,
                            "memberId": order.buyerInfo.id,
                            "memberPhone": order.buyerInfo.phone,
                            "orderDate": order._createdDate,
                            "orderNo": order.number,
                            "lineItems": order.lineItems.map((product) => {
                                return {
                                    "productName": product.name,
                                    "quantity": product.quantity,
                                    "sku": product.sku,
                                    "tax": product.tax,
                                    "productPrice": product.priceData.price,
                                    "totalPrice": product.priceData.totalPrice,
                                    "options": product.options,
                                    "customTextFields": product.customTextFields,
                                    "productImage": product.mediaItem.src,
                                    "discount": product.discount
                                }
                            }),
                            "paymentStatus": order.paymentStatus,
                            "totals": order.totals
                        }
                    }
                })

                console.log("Calisti")
                return frontendData;
            }
        })
        .catch((err) => {
            console.error(err)
        })
}

Custom element JS code:

const createStyle = () => {
    const styleElement = document.createElement('style');
    styleElement.innerHTML = `
    * {
        font-family: Poppins;
    }

    p {
        padding: 0;
        margin: 0;
        font-size: 16px;
    }

    .grid {
        display: grid;
        width: 875px;
        grid-template-columns: 1fr;
        grid-row-gap: 2em;
        justify-items: center;
        align-items: center;
    }

    .orderTab {
        display: grid;
        grid-template-columns: 3fr 1fr;
        grid-auto-rows: minmax(100px, auto);
    }

    .productDetails {
        display: grid;
        grid-template-columns: 1fr 8fr;
        align-items: center;
        justify-items: start;
    }

    .productImage {
        margin-right: 20px;
        align-items: center;
    }

    .productTexts {
        display: grid;
        grid-template-columns: 1fr;
        grid-gap: 5px;
        align-self: start;
    }

    .greyText {
        color: #828282;
    }

    .productName {
        margin-bottom: 10px;
    }

    .productDownload {
        display: grid;
        grid-template-columns: 1fr;
    }

    .productQuantity {
        display: grid;
        grid-template-columns: 1fr 1fr;
    }

    .price {
        justify-self: end;
    }

    .downloadProduct {
        justify-self: right;
        align-self: end;
        margin-bottom: 10px;
    }

    .downloadButton {
        width: 220px;
        height: 40px;
        border: 0px;
        background: #ec0038;
        border-radius: 999px;
        color: white;
        font-size: 16px;
        transition: 0.3s;
    }

    .downloadButton:hover {
        cursor: pointer;
        opacity: 70%;
    }
  `;
    return styleElement;
};

let number = 0;

let innerHtml = (index) => {
    let html = `
<div class="orderTab">
            <div class="productDetails">
                <div class="productImage">
                    <img id="productImage${index.toString()}" src="./normal.jpg" width="160px" height="100px" alt="">
                </div>
                <div class="productTexts">
                    <p id="productName${index.toString()}" class="productName">Yayıncı Paketi</p>
                    <p id="productSku${index.toString()}" class="greyText">Ürün Kodu: yayıncı-paketi-v1.09</p>
                    <p id="productPrice${index.toString()}" class="greyText">Fiyatı: 174.99₺</p>
                </div>
            </div>
            <div class="productDownload">
                <div class="productQuantity">
                    <p id="productQuantity${index.toString()}">Adet: 1</p>
                    <p id="productPriceTwo${index.toString()}" class="price">Fiyat: 174.99₺</p>
                </div>
                <div class="downloadProduct">
                    <a target="_blank" id="downloadProduct${index.toString()}"><button class="downloadButton">Son Sürümü İndir</button></a>
                </div>
            </div>
        </div>
`;

    return html;
}

const createGrid = () => {
    const grid = document.createElement('div')
    grid.className = "grid";
    return grid;
}

const createOrderTab = (data, index) => {
    const orderTab = document.createElement('div');
    orderTab.innerHTML = innerHtml(index);
    orderTab.querySelector(`#productImage${index.toString()}`).src = `https://static.wixstatic.com/media/${data.productImage}`;
    orderTab.querySelector(`#productName${index.toString()}`).innerText = data.productName;
    orderTab.querySelector(`#productSku${index.toString()}`).innerText = `Ürün Kodu: ${data.productSku}`;
    orderTab.querySelector(`#productPrice${index.toString()}`).innerText = "Fiyat:" + data.productPrice + "₺";
    orderTab.querySelector(`#productQuantity${index.toString()}`).innerText = `Adet: ${data.productQuantity}`;
    orderTab.querySelector(`#productPriceTwo${index.toString()}`).innerText = data.productPrice + "₺";
    orderTab.querySelector(`#downloadProduct${index.toString()}`).href = data.downloadProduct;
    console.log(`Deneme${index}`)
    console.log(orderTab.querySelector(`#productImage${index.toString()}`))
    return orderTab;
}

let data;

class OrderTab extends HTMLElement {
    constructor() {
        super();

        this.showInfo = true;

        this.attachShadow({ mode: 'open' });
        this.shadowRoot.appendChild(createStyle());
        this.shadowRoot.appendChild(createGrid())
    }

    getData() {
        return data = JSON.parse(this.getAttribute('data'))
    }

    setupRepeater() {
        data.forEach((element) => {
            console.log(number)
            console.log(element)
            this.shadowRoot.querySelector('.grid').appendChild(createOrderTab(element, number))
            number++;
        })
    }

    async connectedCallback() {
        await this.getData()
        this.setupRepeater()
    }

    disconnectedCallback() {
        //
    }
}

window.customElements.define('order-tab', OrderTab);

I tried to created different ID each time I create the HTML and it works every custom element has their own IDs. But like I said problem is every custom element shows the same data.

My questions:
Can I create this logic with custom element or setAttribute API will refresh the data each time I pass the data? Even if I use $item to pass the data via attribute. Or is there any other way to create this logic?

Thanks!

I have done it guys just passing 3 attributes and I change attributes based on itemData on repeater :slight_smile:

Hello, LoeiX, I would like also to create my custom "My Order” page but cannot find the correct data to connect.

The idea is, I will create a "Collection Page”, where I will show all the purchased product of the customer. I would like to access the image of the purchased product, the product name, its brand and price but I cannot find them by the Data Manager.

Where did you get the data for your custom My Order page”? Thanks in advance. :slight_smile:

Hi!

You can use wixData with Store databases. (Stores/Orders database).

Example:


Go to Orders database. You will see lineItems field in this field you can find mediaItems of every product for selected order:

lineItems[index].mediaItem.src;

this will give you the URL of image/video and you can use this. If you have videos in your products you can check type with if statement so make sure that you will get an image.

OR:

You can use productId (again in lineItems) and you can query the id for Products database and you can get any info of selected product/s.

OR:

You can create your own orders database and you can use onNewOrder or similar event API to insert customized item into your custom database. This is very useful but remember each time you make an update in your orders Orders database will be updated so you should also update your database too otherwise you might see old data for some orders.

When you code there is multiple ways to do something choose which is best for your project.

If you want to use last way that I told (custom database with onNewOrder API) you should use Events - Velo API Reference - Wix.com these APIs to handle updates.

great post loeix…thank you so much!! i was thinking how this could be tackled (repeaters with custom elements) to implement hover effect on mobiles… you opened my eyes. YOU ARE A SAVIOUR! :heart_eyes: