[ENG-4570] Fix rx.foreach over dict (#4743)

* Add test case for literal dict in foreach

* [ENG-4570] Iterate over ObjectVar.entries

* Adjust expectations of test_foreach.py unit tests
This commit is contained in:
Masen Furer 2025-02-03 17:21:00 -08:00 committed by GitHub
parent 2ff840aba6
commit 20e8b83421
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 31 additions and 9 deletions

View File

@ -60,7 +60,7 @@
{# Args: #}
{# component: component dictionary #}
{% macro render_iterable_tag(component) %}
<>{ {%- if component.iterable_type == 'dict' -%}Object.entries({{- component.iterable_state }}){%- else -%}{{- component.iterable_state }}{%- endif -%}.map(({{ component.arg_name }}, {{ component.arg_index }}) => (
<>{ {{ component.iterable_state }}.map(({{ component.arg_name }}, {{ component.arg_index }}) => (
{% for child in component.children %}
{{ render(child) }}
{% endfor %}

View File

@ -54,6 +54,8 @@ class Foreach(Component):
TypeError: If the render function is a ComponentState.
UntypedVarError: If the iterable is of type Any without a type annotation.
"""
from reflex.vars.object import ObjectVar
iterable = LiteralVar.create(iterable)
if iterable._var_type == Any:
raise ForeachVarError(
@ -70,6 +72,9 @@ class Foreach(Component):
"Using a ComponentState as `render_fn` inside `rx.foreach` is not supported yet."
)
if isinstance(iterable, ObjectVar):
iterable = iterable.entries()
component = cls(
iterable=iterable,
render_fn=render_fn,

View File

@ -605,6 +605,20 @@ def VarOperations():
rx.box(rx.foreach(range(42, 80, 3), rx.text.span), id="range_in_foreach2"),
rx.box(rx.foreach(range(42, 20, -6), rx.text.span), id="range_in_foreach3"),
rx.box(rx.foreach(range(42, 43, 5), rx.text.span), id="range_in_foreach4"),
# Literal dict in a foreach
rx.box(rx.foreach({"a": 1, "b": 2}, rx.text.span), id="dict_in_foreach1"),
# State Var dict in a foreach
rx.box(
rx.foreach(VarOperationState.dict1, rx.text.span),
id="dict_in_foreach2",
),
rx.box(
rx.foreach(
VarOperationState.dict1.merge(VarOperationState.dict2),
rx.text.span,
),
id="dict_in_foreach3",
),
)
@ -809,6 +823,9 @@ def test_var_operations(driver, var_operations: AppHarness):
("range_in_foreach2", "42454851545760636669727578"),
("range_in_foreach3", "42363024"),
("range_in_foreach4", "42"),
("dict_in_foreach1", "a1b2"),
("dict_in_foreach2", "12"),
("dict_in_foreach3", "1234"),
]
for tag, expected in tests:

View File

@ -170,32 +170,32 @@ seen_index_vars = set()
ForEachState.primary_color,
display_primary_colors,
{
"iterable_state": f"{ForEachState.get_full_name()}.primary_color",
"iterable_type": "dict",
"iterable_state": f"Object.entries({ForEachState.get_full_name()}.primary_color)",
"iterable_type": "list",
},
),
(
ForEachState.color_with_shades,
display_color_with_shades,
{
"iterable_state": f"{ForEachState.get_full_name()}.color_with_shades",
"iterable_type": "dict",
"iterable_state": f"Object.entries({ForEachState.get_full_name()}.color_with_shades)",
"iterable_type": "list",
},
),
(
ForEachState.nested_colors_with_shades,
display_nested_color_with_shades,
{
"iterable_state": f"{ForEachState.get_full_name()}.nested_colors_with_shades",
"iterable_type": "dict",
"iterable_state": f"Object.entries({ForEachState.get_full_name()}.nested_colors_with_shades)",
"iterable_type": "list",
},
),
(
ForEachState.nested_colors_with_shades,
display_nested_color_with_shades_v2,
{
"iterable_state": f"{ForEachState.get_full_name()}.nested_colors_with_shades",
"iterable_type": "dict",
"iterable_state": f"Object.entries({ForEachState.get_full_name()}.nested_colors_with_shades)",
"iterable_type": "list",
},
),
(