Wix: Data-Dropdown, multiple collections and repeater

Hi, I created a two collections; a months collection (3 fields: a value field to sort the months, Months and Monthy Cycle) and a calendar collection (4 fields: Product, DayorWeek, Task and Month).
Also I created a dropdown displaying the months and a text field displaying the monthly cycle from the months collection and a repeater displaying the fields from the calendar collection.
I would like the dropdown: months linked to the cycle field and also to the months field of the other collection. See image below:


Then, I do have some code but it is re-used from previous dropdown using a single collection therefore it obviously has errors for this example…


Any help is apricated! Thank you in advance…

@hortiray It looks like you have done a good job trying to repurpose the previous code. So you have a couple of things to do.

  1. It looks like there is an issue with your refresh call on line 65. Here is the documentation to look at:
  1. For the Monthly Cycle you should take a look at the way data collections work
// Runs a query on the "recipes" collection
 wixData.query("recipes")
 // Query the collection for any items whose "Name" field contains
 // the value the user selected in the dropdown
    .contains("course", $w("#searchList").value)
    .find()  // Run the query
    .then(res => {
        // Set the table data to be the results of the query      
        $w("#resultsTable").rows = res.items;
    }); 

The query needs to use the Months data collection and instead of using the contains() filter we will use the .eq() again. When you retrieve the item from your collection you need to extract the Monthly Cycle field and assign it to the value (or text if this is a text box) property for your Monthly Cycle element. Here’s what your code should look. Now you know I may introduce minor bug in this but by now you are getting the hang of how to spot them and fix them :slight_smile:

One last point, we will use a technique called chaining to make efficient use of the Promises that are returned from the api calls we make. If you look at the refresh() documentation you will see the syntax for the refresh call looks like this:

Notice that the return is a Promise. We access the Promise by attaching a .then() function call to any function that returns a Promise. If we use another function inside of a .then() that returns a Promise then we can return the result of the function and chain a new .then().

So this is how the code from lines 62 - 66 should look:

$w('#dataset1').setFilter(datasetFilter)
.then(() => {
    return $w('#dataset1').refresh();
})
// We returned the refresh result above so we can chain a new then
.then(() => {
    // The refresh worked so now we fetch the monthly cycle
    // The result comes in a Promise so we return the query
    return wixData.query("Months")  // Make sure this is the correct name
    .eq("Month", dropdownValue)
    .find();
})
.then((result) => {
    // The result is a WixDataQueryResult
    // We should only have one Month record!
    if (result.length !== 1) {
        // We catch this error below
        throw Error("Internal Error? Month record look up for " +
            dropdownValue + " returned " +
            result.length.toString() + "records");
    }
    // We have a record
    let monthRecord = result.item[0];
    // let's assign the Monthly Cycle to our display element
    $w('#monthlyCycle').value = monthRecord.monthlyCycle;
})
.catch(...

The last line of code above before the catch uses ‘assumed’ values. You will need to change #monthlyCycle to be the ID of your Monthly Cycle box (for example if the box is an input it might have an id of input1not monthlyCycle). If the monthly cycle field in your data collection is not called monthlyCycle but is called monthCycle then you would change monthRecord.monthlyCycle to monthlyRecord.monthCycle.

Good luck

1 Like

Hi Mate, once again excellent work, very much appreciated! I inserted the text, not sure if you wanted to keep the .catch I had or the one you provided in the last line.
After changing the text I discovered the two lines (71 and 80) with dropdownValue had an error. (undefined, just like last time!)


I had to rename the month fields in both collections to calendarMonth since one had the title name still attached…

@hortiray OK the mistake is on line 55. The variable dropdownValue is set inside of an if statement. So it stops existing after the if statement. The way to fix it is move the definition to line 53 above the if statement and assign it to something that will fail the the fetch like “NoMonthSelected”. Then simply remove the let at the beginning of line 55.

So line from 53 the code should look like:

let dropdownValue = “NoMonthSelected”;
if ($w(‘#dataset1’).selectedIndex >= 0 ) {
    dropdownValue = $w(‘#dataset1’).value;
   ...

This should fix the error

@stevendc I replaced the code and received an error then I noticed that you used different punctuation marks, so I replaced them (image) that left me with another set of errors…
I see the code is opened in line 54 but not closed??? I also cannot discover any codes linking dataset 1 with dataset 2???


Would be good if this is going to work.
I successfully implemented your previous codes on other pages… Great success, thanks!

@hortiray Line 54 should be $(‘#dropdown1’) not $w(‘#dataset1’). Sorry my mistake.

If you hover your mouse over any red dot it will tell you what the error is. This should tell you that it doesn’t know about selectedIndex on a dataset. The selected index can only be used on a dropdown.
Take a look at the API documents for dropdown and dataset:

Cheers
Steve

@stevendc Hi Steve, thanks for the response. I had a look in the documents and discovered that line 55 had the same issue. I changed that also to dropdown1.
Then, line 89 (last) is giving an unexpected token issue. I believe I missed a closing punctuation but it is looking for a needle in a haystack! Can you see what we missed?

public/pages/p37y3.js: Unexpected token (89:2)
87 | console.log(error.message);
88 | });

89 | }
| ^

@hortiray Between Line 58 and 59 you need another }
Like this.

if ($w(‘#dataset1’).selectedIndex >= 0 ) {
    dropdownValue = $w(‘#dataset1’).value;
}

A trick to use to fix these needles in a haystack is to make sure that all blocks and function calls have matching start and end syntax AND use the indenting.

All function calls AND conditional statements must have matching open parenthesis “(” and closed parenthesis “)”;
All code blocks must have matching open braces “{” and closed “}” braces.

You will see most developers indent when they start a new code block, and then outdent when they end a code block. It is also good practise to do this with parentheses when you have a need to break a function call over multiple lines as with an event handler with an anonymous function.

// Simple block
{ // new Block
 -->// Indent
    // End block
} <-- // outdent

// Nested blocks
{
 --> { // New block 2
     -->// Indent again
        // End block 2
    } <--- // Outdent
} // <--- Outdent

// Event handler with embedded anonymous function call
$w.onReady(
--> // Inside parentheses -> indent
    // Add anonymous function
    () => { // New block
  -->  // Indent

        // End of anonymous function
    }  <-- // outdent
);  <-- // Outdent function parentheses.

You will notice that some time the block {} and the function parentheses are combined but indented and outdented in a similar way

$w.onReady(() => {  // Parentheses and open block on same line
----> indent code
}); <-- Outdent both end block AND parentheses.

So here is a bug! See if you can figure out what is wrong

function x() {
    let x = 1;
    if ( x &&
        x === 1 {
       console.log("Success");
   
}

Cheers
Steve

Oei that is not that easy! I understand the concept of opening and closing but where exactly is somewhat more difficult…

function x() {     
    let x = 1;     
    if ( x &&)
        }         
        x === 1 {        
        console.log("Success");     
        }

I exactly tried to close between 59 and 60 before, so I was close…
https://hortiray.wixsite.com/database/78dyu9e68shjlxbfy7si

You can see dropdown has got the months from dataset1, reset button working, but monthly cycle from dataset1 is not working.
Furthermore, the repeater does not show the tasks from dataset2.
The calendarMonth field (dataset1) is not refering to the calendarMonth field from dataset2.

@hortiray You have a couple of problems one is in the code on lines 81 -84
This code has two errors.

// We have a record
    let monthRecord = result.item[0];
    // let's assign the Monthly Cycle to our display element
    $w('#monthlyCycle').value = monthRecord.monthlyCycle;

result.item[0]; Should be result.items[0]; (plural ;-))

and your monthlyCycle field is not called monthlyCycle it is called cycle :slight_smile:
monthRecord.monthlyCycle; should be monthRecord.cycle.

So this is what your code needs to look like:

// We have a record
let monthRecord = result.items[0];
// let's assign the Monthly Cycle to our display element 
$w('#monthlyCycle').value = monthRecord.cycle;

The other is is in the if statement where you test the selectionIndex for the dropdown. After you assign the dropdown value to the variable dropdownValue you need to use it to set the filter for the dataset. You are missing line 57 from you original post as a result the dataset is never filtered:

datasetFilter = datasetFilter.eq('month', dropdownValue);

Also I noticed that you are trying to set the value of the monthlyCycle element in code AND through dataset binding in the editor using the disk icon:

You should un hook this (don’t use dataset binding) when changing elements in code. Otherwise strange things can happen.

Cheers
Steve

1 Like

That works exactly as how I imagined it to be… Steve, once again, thank you!
PROBLEM SOLVED!

1 Like

Would you be willing to post the completed WORKING code? Thanks!

@jim32 Sure mate, here we go:

import wixData from 'wix-data';

$w.onReady(function () {
	uniqueDropDown1();
	$w('#dropdown1').onChange(dropdownHasChanged);
	$w('#resetDropdownsButton').onClick(resetDropdownsButton_clicked);
	});
	function resetDropdownsButton_clicked(event) {
    	$w('#dropdown1').selectedIndex = undefined;
		$w('#dropdown1').resetValidityIndication();
		$w('#monthlyCycle').value = undefined;
    	dropdownHasChanged(); 
	}

	function uniqueDropDown1() {
		wixData.query("Months")
		.limit(1000)
		.find()
		.then(results => {
			const uniqueTitles = getUniqueTitles(results.items, 'calendarMonth');
			$w("#dropdown1").options = buildOptions(uniqueTitles);
		});
	}

	function getUniqueTitles(items, columnKey) {
		const titlesOnly = items.map(item => item[columnKey]);
 		// We need to return an array
 		let result = [];
 		// Make our list unique by adding it to a Set object
 		let uniqueSet = new Set(titlesOnly);
 		// Get the iterator to cycle through each record
 		let setIterator = uniqueSet.entries();
 		for (let entry of setIterator) {
 		// Add the primary value of each record to our array
 		let entryValue = entry[0];
 		if (entryValue) {
 		// Only use valid values
        result.push(entryValue);
        	}   
    	}
 	return result;
	}

	function buildOptions(uniqueList) {
		return uniqueList.map(curr => {
		return { label: curr, value: curr };
		});
	}

	function dropdownHasChanged(event) {
   	 	// Assume we will build a dataset filter
    	let datasetFilter = wixData.filter();
    	// Get the value of the dropdown if one is selected
    	// If the selectedIndex of the dropdown is >= 0 then a value exists
		let dropdownValue = "NoMonthSelected";
		if ($w('#dropdown1').selectedIndex >= 0 ) {
    	    dropdownValue = $w('#dropdown1').value;
       	    // If we get here either we found a value, in which case the filter will work.
    	    // or we didn't, which means the dropdown has been reset, in which case the filter is
    	    // Removed by using an empty query.
			datasetFilter = datasetFilter.eq('calendarMonth', dropdownValue);
		}
		$w('#dataset2').setFilter(datasetFilter)
		.then(() => {
    	    return $w('#dataset2').refresh();
		})
		// We returned the refresh result above so we can chain a new then
		.then(() => {
			// The refresh worked so now we fetch the monthly cycle
			// The result comes in a Promise so we return the query
			return wixData.query("Months")  // Make sure this is the correct name
			.eq("calendarMonth", dropdownValue)
			.find();
		})
		.then((result) => {
			// The result is a WixDataQueryResult
			// We should only have one Month record!
			if (result.length !== 1) {
				// We catch this error below
				throw Error("Internal Error? Month record look up for " +
					dropdownValue + " returned " +
					result.length.toString() + "records");
			}
			// We have a record
			let monthRecord = result.items[0];
			// let's assign the Monthly Cycle to our display element
			$w('#monthlyCycle').value = monthRecord.cycle;
		})
		.catch((error) => {
			// If we get here we have encountered an error so console.log() it for now
			console.log(error.message);
			});
	}
3 Likes

@stevendc I am continuing my project with an invoice system on: