Dependent Dropdown

I am trying to filter a “sub-category” dropdown menu based on another “category” dropdown menu based on this example: - YouTube

We have a “Category” database, and a “Sub-Category” database that has a reference field for “Category”.

Currently, when a user selects “Accessories”, they are not able to select a sub-category.

export function dropdown3_change(event, $w) {
subcategoryfilter();
}

function subcategoryfilter() {
$w(‘#dataset3’).setFilter(wixData.filter()
.contains(“category”, $w(“#dropdown3”).value)
);
}

Hi Dustin,

can you share a link to your site?

https://makeshark.wixsite.com/american-parts-depot/catalog

The code seems to be fine, however is a little bit of more code needed when working with references, compared to simpler field types.

The issue is that you are trying to filter Sub-Categories Dataset by title of Category. It is so because Category dropdown value is Title of the category, and filter requires it’s ID.

Here is updated snipped:

async function subcategoryfilter() {
  // retrieve all items from the Category dropdown
  const categories = (await $w("#dataset2").getItems(0, 20)).items
 
  // get ID of currently selected Category
  const selectedCategoryId = categories[$w("#dropdown3").selectedIndex]._id
 
  $w('#dataset3').setFilter(
    wixData.filter().eq("category", selectedCategoryId)
  )
}

Hope this helps!

Perfect, thank you! How then do I filter the products (dataset1) by all the dropdowns onclick?

You should first code a function which takes values from each dropdown and build a Products Dataset filter based on that. E.g.:

async function productFilter() {
  const categories = (await $w("#dataset2").getItems(0, 20)).items
  const selectedCategoryId = categories[$w("#dropdown3").selectedIndex]._id

  const subCategories = (await $w("#dataset3").getItems(0, 20)).items
  const selectedSubCategoryId = subCategories[$w("#dropdown4").selectedIndex]._id

  const filter = wixData.filter()
    .eq('model', $w('#dropdown1').value)
    .eq('year', $w('#dropdown2').value)
    .eq('category', selectedCategoryId)
    .eq('sub-category', selectedSubCategoryId)

  $w('#dataset1').setFilter(filter)
}

Next, you need to define onChange handlers for each dropdown and invoke the above function like this:

export function dropdown1_change(event) {
  productFilter();
}


@egidijus-jucevicius Thank you SO much!!!

@egidijus-jucevicius How do I make it so the user doesn’t have to select every dropdown?

For instance, if they only select “year”, nothing happens.

https://makeshark.wixsite.com/american-parts-depot/catalog

@dustin in that case productFilter must be refined a bit. The idea is to only apply a condition to Dataset filter if dropdown value is not empty:

async function productFilter() {
 let filter = wixData.filter()
 
 if ($w('#dropdown1').value) {
    filter = filter.eq('model', $w('#dropdown1').value)
  }

 if ($w('#dropdown2').value) {
    filter = filter.eq('year', $w('#dropdown2').value)
  }

 const selectedCategoryIndex = $w("#dropdown3").selectedIndex
 if (selectedCategoryIndex) {
 const categories = (await $w("#dataset2").getItems(0, 20)).items
 const selectedCategoryId = categories[selectedCategoryIndex]._id
    filter = filter.eq('category', selectedCategoryId)
  }

 const selectedSubCategoryIndex = $w("#dropdown4").selectedIndex
 if (selectedSubCategoryIndex) {
 const subCategories = (await $w("#dataset3").getItems(0, 20)).items
 const selectedSubCategoryId = subCategories[selectedSubCategoryIndex]._id
    filter = filter.eq('subCategory', selectedSubCategoryId)
  }

  $w('#dataset1').setFilter(filter)
}

However, this function is getting a bit clunky and repetitive. By creating several more generic functions, we can clean it up considerably. Let’s start with a function which extracts a value from dropdown and applies filter condition if the value exists:

function addConditionIfDropdownSelected(filter, dropdownId, fieldKey) {
 const value = $w('#' + dropdownId).value
 if (value) {
    return filter.eq(fieldKey, value)
 }
 return filter
}

Next, let’s do the same for dropdowns populated by references:

async function addConditionIfReferenceDropdownSelected(filter, datasetId, dropdownId, fieldKey) {
 const selectedValueIndex = $w('#' + dropdownId).selectedIndex
 if (selectedValueIndex !== undefined) {
    const items = (await $w('#' + datasetId).getItems(0, 20)).items
    const selectedItemId = items[selectedValueIndex]._id
    return filter.eq(fieldKey, selectedItemId)
  }
 return filter
}

And then filterProducts can be rewritten as follows:

async function productFilter() {
 let filter = wixData.filter()
  filter = addConditionIfDropdownSelected(filter, 'dropdown1', 'model')
  filter = addConditionIfDropdownSelected(filter, 'dropdown2', 'year')
  filter = await addConditionIfReferenceDropdownSelected(filter, 'dataset2', 'dropdown3', 'category')
  filter = await addConditionIfReferenceDropdownSelected(filter, 'dataset3', 'dropdown4', 'subCategory')
  $w('#dataset1').setFilter(filter)
}

@egidijus-jucevicius I changed the “model” field from a “text field” to a “reference field” (with multiple items selected.

I thought I could make the following changes, but it is not working. I’m wondering if reference fields with multiple items handled differently?

  1. “return filter.eq(fieldKey, selectedItemId)” to “return filter.contains(fieldKey, selectedItemId)”

  2. “filter = addConditionIfDropdownSelected(filter, ‘dropdown1’, ‘model’)” to “filter = await addConditionIfReferenceDropdownSelected(filter, ‘dataset4’, ‘dropdown1’, ‘model’)”

Also, are you employed with Wix, or do you have a portfolio where people can hire you?

@dustin sorry for late reply!

You’re changes are OK, however having multiple items has impact on how to construct a WixData filter - you’ll need to use another method which is called “.hasSome()” instead of “.contains()”. Let’s wrap this logic up into a separate function:

async function addConditionIfMultiReferenceDropdownSelected(filter, datasetId, dropdownId, fieldKey) {
 const selectedValueIndex = $w('#' + dropdownId).selectedIndex
 if (selectedValueIndex !== undefined) {
   const items = (await $w('#' + datasetId).getItems(0, 20)).items
   const selectedItemId = items[selectedValueIndex]._id
   return filter.hasSome(fieldKey, selectedItemId)
  }
 return filter
}

Regarding portfolio - I don’t have one, because I’m working at Wix :slight_smile:

@egidijus-jucevicius

Hi! I got this to work to get the ID of the dropdown selection value, but I wanna know what is the purpose of .getItems(0, 20)).items in this code snippet below?

async function subcategoryfilter() {
  // retrieve all items from the Category dropdown
  const categories = (await $w("#dataset2").getItems(0, 20)).items
 
  // get ID of currently selected Category
  const selectedCategoryId = categories[$w("#dropdown3").selectedIndex]._id
 
  $w('#dataset3').setFilter(
    wixData.filter().eq("category", selectedCategoryId)
  )
}

Thank you so much!

@freedfoxworldwide

Tells you in the Wix API Reference for it.
https://www.wix.com/corvid/reference/wix-dataset.Dataset.html#getItems
The index of the first item to return/The number of items to return.

Old post being closed.