top of page

Container Box #myRatingChangeBox

Working Example

Screenshot 2018-09-10 at 13.41.43.png
Screenshot 2018-09-10 at 13.42.05.png
Tip...
Small Title
Default Email
Not Logged In

Repeater #productSelector

Black Shoes

Black Shoes

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Cactus Plants

Cactus Plants

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Eye Shadow

Eye Shadow

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Grey Backpack

Grey Backpack

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Perfume

Perfume

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Scarf

Scarf

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Wooden Bowl

Wooden Bowl

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Black Strapped Watch

Black Strapped Watch

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Desk Lamp

Desk Lamp

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Glasses

Glasses

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Grey Hoodie

Grey Hoodie

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Photo Frame

Photo Frame

I'm a product description. I'm a great place to add more details about your product such as sizing, material, care instructions and cleaning instructions.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Sneaker

Sneaker

Available in Mint, Tan or Purple.

Ratings DIsplay #myRatingsDisplay

average rating is 3 out of 5, based on 150 votes, Product ratings

My Rating

Button #changeMyRatingBtn

average rating is 3 out of 5
Change

Ratings DIsplay #myRating

Example Overview

This example shows how to make use of the Rating Display element in conjunction with wix Stores.

 

The key features are:

  • A custom data collection (ProductRatings) used to collect ratings for each product in the stores product list

    • Key column properties:

      • productId - used to store the _id of the Product being rated

      • userEmail - used to record the email of the person rating the product

      • starRating - used to record the number of stars awarded by the user rating the product

    • Other columns could be used

  • A Product data collection generated from wix Stores. This is set up and maintained using the wix My Store dashboard

  • A Repeater displaying the Product data collection with two RatingDisplay elements:

    • #myRating - used to show the rating of the currently logged in (or default) user

    • #myRatingsDisplay - used to display the average rating of all ratings in the
      ProductRatings data collection for the related repeater item

 

When the page is loaded a repeater is populated after the datasets for the repeater are ready. This is controlled by the onReady functions of each dataset. The main repeater fields for Product Name, Image description etc are connected to the repeater using wix Editor binding from the dataset. The $w.Repeater.forEach() function is used to populate the rating values. As each repeater item is loaded wix-data is used to look up the ratings from the ProductRatings data collection for the current product _id. In addition a Change button is added that, when clicked (changeMyRatingBtn_click), will display a dialogue box that includes the Product item name, id and image. The dialogue box also displays two group of stars (grey for unrated, green for rated). Each star has an Id which maps to its rating value (a number from 1 to 5). When a star is clicked a generic rating function (starMouseClick) is executed. This function determines which stars need to be set to grey and which ones need to be green (see comments for the function below).

 

The dialogue box his hidden without change if Cancel is clicked (cancelMyRatingBtn_click). Otherwise if update is selected (updateMyRatingBtn_click) the rating is added or updated to the ProductRatings data collection. 

 

If the email address changes or the rating value is updated the Repeater fields are updated with the new ratings by updating the $w.RatingDisplay element values.

Example Overview
Page Code

Working Page Code

// For full API documentation, including code examples, visit http://wix.to/94BuAAs

import wixData from 'wix-data';

import wixUsers from 'wix-users';

import wixWindow from 'wix-window';

 

let ratings = []; // Array of all ratings available from the ProductRatings data collection

let myEmail = null; // Used to default the email used to record rating values

let mouseIn = false; // Used to prevent mouse hover flicker

 

$w.onReady(function () {

    //Configure dynamically loaded content

    loadMouseEventHandlers();

    clearMyRating(1); // Clear all star values starting at position 1

    setMyEmail(); // Get the current logged in user for capturing ratings

    // Make sure that all data sets are loaded before we update dynamic repeater content

    $w('#productDataset').onReady(() => {

        $w('#productRatingsDataset').onReady(() => {

            // Don't try to populate repeater items until we know that the datasets are ready for us

            loadProductRatings();

        });

    });

});

 

// Function to set up the default email for this page session

function setMyEmail() {

    // Use wix-users to get the current user information

    let myInfo = wixUsers.currentUser;

    if (myInfo.loggedIn) {

        myInfo.getEmail()

        .then((emailAddress) => {

            myEmail = emailAddress;

            $w('#defaultEmailDisplay').text = myEmail;

        });

    } else {

        // There isn't a logged in user so set up a default. The user can change this in the rating dialogue

        myEmail = 'Not Logged In';

        $w('#defaultEmailDisplay').text = myEmail;

    }

}

 

// Function that takes a product Id and uses it in conjunction with the myEmail value to search the ratings data collection

// For a rating that may already exist - we only want one rating per email.

function getMyRatingForProduct(productId) {

    let promisedResult = Promise.resolve(null); // Nothing to return

    if (productId && myEmail) {

        // We have a productId and an Email - search for a matching record

        let ratingsDataCollectionQuery = wixData.query('ProductRatings').eq('productId', productId).eq('userEmail', myEmail);

        // Tee up the query promise

        promisedResult = ratingsDataCollectionQuery.find()

        .then ((ratingResults) => {

            // Assume we didn't find a record

            let queryPromisedResult = Promise.resolve(null);

            console.log('ratings for productId['+productId+'] and email:['+myEmail+'] ='+ JSON.stringify(ratingResults));

            if (ratingResults.totalCount === 1) {

                // If we have one and only one record we have a value to return

                queryPromisedResult = Promise.resolve(ratingResults.items[0].starRating);

            }

            // The result of this function is a promise so return what we have set up

            return queryPromisedResult;

        });

    }

    // Return the promise we have determined

    return promisedResult;

}

 

// This function looks up all ratings in the ratings data collection for a given productId and creates an average rating for it

// The result is delivered in a promise. We return the promise so that the caller can process another .then() or a .catch() based on any

// Query issues. The result is sufficient to configure a $w.RatingsDisplay element

function getRatingForProduct(productId) {

    // Set up and return the wixData query

    return wixData.query('ProductRatings').eq('productId', productId).find()

    .then ((ratingList) => {

        // Initialise our calculation variables

        let ratingCumulative = 0;

        let ratingCount = ratingList.totalCount;

        // Cycle through each result in the returned items list and calculate the rating

        ratingList.items.forEach((ratingRecord) => {

            ratingCumulative += ratingRecord.starRating;

        });

        // Calculate the average rating

        let ratingsAverage = (ratingCount > 0 ? ratingCumulative/ratingCount : 0);

        // Return the result object in a promise

        return Promise.resolve({'average':ratingsAverage, 'totalCounted':ratingCount});

    })

}

 

// Given a context selector and product Id this function will set up the rating information for a repeater Item

function setRatingForProduct($selector, productId) {

return getRatingForProduct(productId)

    .then((ratingResult) => {

        // If we have a set of ratings then configure the ratingsDisplay accordingly

        if (ratingResult.totalCounted > 0) {

            $selector('#ratingsDisplay').numRatings = ratingResult.totalCounted;

            $selector('#ratingsDisplay').rating = ratingResult.average;

        } else {

            // No ratings

            $selector('#ratingsDisplay').numRatings = 0;

            // This method of resetting the RatingsDisplay using 'undefined' currently throws a wix SDK warning

            // which has been reported to Wix

            $selector('#ratingsDisplay').rating = undefined;

        }

    }); 

}

 

// *************************

// ** Convenience functions

// *************************

 

// Set the stars in the My Rating dialogue box to the unset grey colored state

function clearMyRating(start) {

    for (var i = (start<1?1:start); i <= 5; i++) {

        setGrey(i);

    }

}

 

// Color the rating stars green that satisfy the rating value parameter

function setMyRating(value) {

    clearMyRating(value); //set unused to grey

    for (var i = 1; i <= value; i++) {

        setGreen(i);

    }

}

 

// Simple function to show a green star and hide its grey counterpart

// Each element is named the same with a numeric suffix for the star rating value

// There are fice of each as follows:

// $w('#greyStar1'), $w('#greyStar2'), $w('#greyStar3'), $w('#greyStar4'), $w('#greyStar5')

// $w('#greenStar1') ,$w('#greenStar2') ,$w('#greenStar3') ,$w('#greenStar4') ,$w('#greenStar5')

function setGreen(index) {

    $w('#greyStar'+index.toString()).hide();

    $w('#greenStar'+index.toString()).show();

}

 

// Simple function to show a grey star and hide its green counterpart

// Each element is named the same with a numeric suffix for the star rating value

// There are fice of each as follows:

// $w('#greyStar1'), $w('#greyStar2'), $w('#greyStar3'), $w('#greyStar4'), $w('#greyStar5')

// $w('#greenStar1') ,$w('#greenStar2') ,$w('#greenStar3') ,$w('#greenStar4') ,$w('#greenStar5')

function setGrey(index) {

    $w('#greyStar'+index.toString()).show();

    $w('#greenStar'+index.toString()).hide();

}

 

// Function to detect a change to the email input text box and update the default email variable as needed

export function emailAddress_change(event) {

    // Set email address

    myEmail = $w('#emailAddress').value;

    $w('#defaultEmailDisplay').text = myEmail;

    loadProductRatings();

    getMyRatingForProduct($w('#productId').text)

    .then((rating) => {

        setMyRating(rating);

    });

}

 

function loadProductRatings() {

    // Reload the repeater by reloading the current page

    $w('#productSelector').forEachItem(($itemSelector, itemData, index) => {

        updateProductRating($itemSelector, itemData);

    });

}

 

function updateProductRating($itemSelector, itemData) {

    // Calculate the ratings for this $itemSelector context using the itemData record

        setRatingForProduct($itemSelector, itemData._id);

        // Use 'itemData' to find any existing rating that I may have given to the product

        getMyRatingForProduct(itemData._id)

        .then((rating) => {

            // If I have rated this product then there will be a value returned otherwise this will be null

            $itemSelector('#myRating').rating = rating;

        });

}

 

// Function to process the change rating repeater button "Change"

export function changeMyRatingBtn_click(event, $w) {

    

    let selector = $w.at(event.context); // Get the selector we need from the repeater's event context

    // Using the selector for this repeater element get the related product record

    let thisProduct = selector('#productDataset').getCurrentItem();

    // Get my rating to display in the My Rating dislogue

    getMyRatingForProduct(thisProduct._id)

    .then((rating) => {

        // Enable the rating change dialog

        activateChangeDialog(selector, rating);

    })

    .catch((err) => {

        showError('ERROR: '+err.message);

    });

}

 

// Function displays the 'My Rating' dialogue to change the rating for a given product

function activateChangeDialog(selector, rating) {

    

    // Get the product record for this change request

    let productRecord = selector('#productDataset').getCurrentItem();

    // Disable Change button

    selector('#changeMyRatingBtn').disable();

    // Update the product name in the 'My Rating' dialogue.

    $w('#productName').text = productRecord.name;

    // Update the product id in the 'My Rating' dialogue.

    $w('#productId').text = productRecord._id;

    // Update the product image in the 'My Rating' dialogue.

    $w('#productImage').src = productRecord.mainMedia;

    // Update the product rating in the 'My Rating' dialogue from my rating

    setMyRating(rating);

    // Set he email address related to this rating

    $w('#emailAddress').value = (myEmail && myEmail !== 'Not Logged In'?myEmail:null);

    // Show the change dialog

    $w('#myRatingChangeBox').show();

    $w('#ratingChangeBoxAnnotation').show();

}

 

// Loads an error message in red when called

function showError(error) {

    $w('#errorText').text = error;

    $w('#errorText').show();

}

 

// Hides the error message

function clearError () {

    $w('#errorText').hide();

}

 

// Function called when the update button in the My Ratings dialogue is clicked.

// This will update the record for the selected product using the email address in the input box.

// NOTE: if a new email address is added to the dialogue box the default email address used in this example will be changed.

export function updateMyRatingBtn_click(event, $w) {

    // Get the new ratings

    let rating = getStarRating();

    // Use exception handling to validate the new information

    if (rating === 0) {

        // No rating was selected

        showError("ERROR: Must have a rating!");

    } else if (!myEmail || myEmail === 'Not Logged In' ) {

        // No email is available need to add one

        showError("ERROR: You must enter an email to add a rating");

    } else {

        // Save the new rating value and then reset the change buttons

        let productId = $w('#productId').text;

 

        saveProductRating(productId, rating, myEmail)

        .then((savedRecord) => {

            // Hide the 'My Rating' dialogue on sucess!

            $w('#myRatingChangeBox').hide();

            $w('#ratingChangeBoxAnnotation').hide();

            // Re enable the repeater change button by walking the repeater looking for disabled buttons and enable them

            $w('#productSelector').forEachItem(($item, itemData, index) => {

                if (itemData._id === $w('#productId').text || !$item('#changeMyRatingBtn').enabled) {

                    $item('#changeMyRatingBtn').enable();

                }

                // Update the repeater rating content if the itemData matches the productId

                if (productId === itemData._id) {

                    updateProductRating($item, itemData);

                }

            });

            

        })

        .catch((err) => {

            showError(err.message);

        });

    }

    

}

 

// Save a newly created rating.

// This function will first chjeck that the product ratings datacollection doesn't have a record for this product from this emailid

// If it does it will change this value.

// If it doesn't a new record will be created and added to the data collection

function saveProductRating(productId, rating, email) {

    return wixData.query('ProductRatings').eq('productId', productId).eq('userEmail', email).find()

    .then((results) => {

        

        let itemToSave = null; // record to save in our data collection

 

        // Cannot have more than one rating per product per email

        if (results.totalCount > 1) {

            throw Error('too many ratings for product ['+productId+'] from user ['+email+']');

        }

        // We have a record or need a new one

        if (results.totalCount === 0) {

            // Need a new one

            itemToSave = {};

            itemToSave.productId = productId;

            itemToSave.userEmail = email;

        } else {

            // Re use the found record - it will have a record Id which will prevent duplication upon save.

            itemToSave = results.items[0];

        }

        itemToSave.starRating = rating;

        return wixData.save('ProductRatings', itemToSave);

    })

    .catch((error) => {

        console.log('bubbling up error from wixData: '+error.message);

        throw error;

    });

}

 

// Convenience function that creates onClick handlers for all 10 colored star elements in My Ratings

function loadMouseEventHandlers() {

    for (var i = 1; i <= 5; i++) {

        $w("#greyStar"+i.toString()).onClick(starMouseClick);

        $w("#greenStar"+i.toString()).onClick(starMouseClick);

    }

}

 

// Function to handle clicks from colored rating stars

// The function determines the color of the star and its index and adjusts the rating view accordingly.

// Clicking on a green star will set that star and its successor stars to grey

// Clicking on a grey star will set that star and all of its predecessor stars to green

function starMouseClick(event) {

    // Get element name

    let starId = '#'+event.target.id;

    let starColorIsGreen = (starId.includes('green') ? true : false);

    // Get index of clicked on star. Adjust if star is green because we will make this grey!

    let starRatingNumber = parseInt(starId.substring(starId.length-1), 10) - (starColorIsGreen?1:0);

    

    // Cycle through the star elements up to starRatingNumber setting them to green

    for (var i = 1; i <= starRatingNumber; i++) {

        setGreen(i);

    }

    // Remaining stars should be grey

    for (;i <= 5; i++) {

        setGrey(i);

    }

}

 

// Function to get the star rating from the individual stars

// Each star has a color and a rating number from 1 to 5

// The rating stars are green we need the highest index value for a green star.

// We walk backwards through the green stars looking for one that is not hidden. That will be our rating.

function getStarRating() {

    // Walk the stars to find out which ones have been set

    let result = 0;

    for (var i = 5; i > 0 ; i--) {

        if (!$w('#greenStar'+i.toString()).hidden) {

            result = i;

            break; // Exit the loop

        }

    }

    return result;

}

 

// ******************************************************************************************************************

// Helper functions used to show information on the example page. These functions are not part of the active example.

// ******************************************************************************************************************

export function productDataSetBtn_click(event, $w) {

    // Load lightbox to show data collection contents

    wixWindow.openLightbox('DataCollectionDisplay', {'tableName':'Stores/Products'});

}

 

export function productRatingsDatasetBtn_click(event, $w) {

    // Load lightbox to show data collection contents

    wixWindow.openLightbox('DataCollectionDisplay', {'tableName':'ProductRatings'});

}

 

export function productRatingsDatasetBtn_mouseIn(event, $w) {

    //Show tool tip for this button

    showToolTip("Click to show Product Ratings Data Collection");

}

 

export function productRatingsDatasetBtn_mouseOut(event, $w) {

    //Remove tool tip

    hideToolTip();

}

 

export function productDataSetBtn_mouseIn(event, $w) {

    //Show tool tip

    showToolTip("Click to show Products Data Collection")

}

 

export function productDataSetBtn_mouseOut(event, $w) {

    //Hide tool tip

    hideToolTip();

}

 

function showToolTip(tip) {

    $w('#toolTipText').text = tip;

    $w('#toolTip').show();

}

 

function hideToolTip() {

    $w('#toolTip').hide();

}

 

export function cancelMyRatingBtn_click(event, $w) {

    // Hide ratings Change Box

    $w('#myRatingChangeBox').hide();

    // Reset change buttons on repeaters

    $w('#productSelector').forEachItem(($item, itemData, index) => {

        if (itemData._id === $w('#productId').text || !$item('#changeMyRatingBtn').enabled) {

            $item('#changeMyRatingBtn').enable();

        }

    });

}

setRatingForProduct
getRatingForProduct
getMyRatingForProduct
setMyEmail
$w.onReady
clearMyRating
setMyRating
setGreen
setGrey
emailAddress_change
loadProductRatings
updateProductRating
changeMyRatingBtn_click
activateChangeDialog
showError
clearError
updateMyRatingBtn_click
saveProductRating
loadMouseEventHandlers
starMouseClick
getStarRating
cancelMyRatingBtn_click

Container Box #myRatingChangeBox

My Rating

Small Title

Small Title

Bike on the Wall

Text #productName

Input #emailAddress

Cancel
Update

Pleasant Title

bottom of page