Help with Opening itemDetailScreen After Scanning Barcode (Match by ID)

Hi all,

I’m building a Retool mobile app with a barcode scanner to make looking up products faster. I have a scanner1 component set up, and I want it to work like this:

After scanning a barcode, the app should navigate to itemDetailScreen and show the product whose id matches the scanned value.

The barcode values correspond to the id field in my productos table.

What I’ve tried so far:

  • I can get the scanned value using scanner1.data.
  • I thought of using a JS query or transformer to filter the product list and find the matching item.
  • But I’m not sure how to pass that selected item to itemDetailScreen, or how to set itemCollection.selectedItem correctly before navigating.

Has anyone done something similar? What’s the best way to:

  1. Match the scanned barcode to an item by id,
  2. Set that item as the selected one,
  3. Navigate to itemDetailScreen with the right data?

Thanks a lot for any advice or examples!

Inventario Farmasi.json (71.6 KB)

1 Like

Hello @max.lopezzz,

Please update the event handler for scanner1 by changing the Navigation event type from Open to Capture, as shown in the attached screenshot.

Also, please update the transformer code in the filterItems query with the following:

// Get raw columnar data from query
const rawData = {{ getItems.data }};
// Handle case where data might be null or empty
if (!rawData || !rawData.nombre || !Array.isArray(rawData.nombre)) {
  return [];
}
// Convert columnar format to array of objects
const data = rawData.nombre.map((_, index) => {
  return {
    id: rawData.id?.[index] ?? null,
    nombre: rawData.nombre?.[index] ?? null,
    cantidad: rawData.cantidad?.[index] ?? null,
    costo: rawData.costo?.[index] ?? null,
    precio: rawData.precio?.[index] ?? null,
    categoria: rawData.categoria?.[index] ?? null,
    subcategoria: rawData.subcategoria?.[index] ?? null,
    descripcion: rawData.descripcion?.[index] ?? null,
    foto: rawData.foto?.[index] ?? null
  };
});
// Filtering function
function filterItems(searchString, categoria, subcategoria, id) {
  let filteredData = [...data];
  // Filter by search string in 'nombre'
  if (searchString) {
    const lowerCaseSearch = searchString.toLowerCase();
    filteredData = filteredData.filter(item =>
      item.nombre && item.nombre.toLowerCase().includes(lowerCaseSearch)
    );
  }
  // Filter by categoria
  if (categoria !== null && categoria !== undefined && categoria !== "") {
    filteredData = filteredData.filter(item =>
      item.categoria !== null &&
      item.categoria !== undefined &&
      item.categoria.toString() === categoria.toString()
    );
  }
  // Filter by subcategoria
  if (subcategoria !== null && subcategoria !== undefined && subcategoria !== "") {
    filteredData = filteredData.filter(item =>
      item.subcategoria !== null &&
      item.subcategoria !== undefined &&
      item.subcategoria.toString() === subcategoria.toString()
    );
  }
  // Filter by exact id
  if (id !== null && id !== undefined && id !== "") {
    filteredData = filteredData.filter(item =>
      item.id !== null &&
      item.id !== undefined &&
      item.id.toString() === id.toString()
    );
  }
  return filteredData;
}
// Execute filtering and return result
return filterItems(
  {{ busqueda.value || "" }},
  {{ select1.value ?? null }},
  {{ select2.value ?? null }},
  {{ scanValue.value ?? null }}
); 
1 Like

Thanks for sharing such a thorough explanation, @WidleStudioLLP! :+1:

Adding on to the above, @max.lopezzz, you will likely need to define two different Capture event handlers - one that sets the value of a selectedItem global variable and another that navigates the user to the itemDetailScreen.

You can actually see a good example of such event handlers in the mobile template app, except that they're defined on the Press event.

In general, the only way to pass data between pages is via global scope. :+1:

1 Like