foreach.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. """Create a list of components from an iterable."""
  2. from __future__ import annotations
  3. import typing
  4. from typing import Any, Callable, Iterable
  5. from reflex.components.component import Component
  6. from reflex.components.layout.fragment import Fragment
  7. from reflex.components.tags import IterTag
  8. from reflex.vars import BaseVar, Var, get_unique_variable_name
  9. class Foreach(Component):
  10. """A component that takes in an iterable and a render function and renders a list of components."""
  11. # The iterable to create components from.
  12. iterable: Var[Iterable]
  13. # A function from the render args to the component.
  14. render_fn: Callable = Fragment.create
  15. @classmethod
  16. def create(cls, iterable: Var[Iterable], render_fn: Callable, **props) -> Foreach:
  17. """Create a foreach component.
  18. Args:
  19. iterable: The iterable to create components from.
  20. render_fn: A function from the render args to the component.
  21. **props: The attributes to pass to each child component.
  22. Returns:
  23. The foreach component.
  24. Raises:
  25. TypeError: If the iterable is of type Any.
  26. """
  27. try:
  28. type_ = (
  29. iterable._var_type
  30. if iterable._var_type.mro()[0] == dict
  31. else iterable._var_type.__args__[0]
  32. )
  33. except Exception:
  34. type_ = Any
  35. iterable = Var.create(iterable) # type: ignore
  36. if iterable._var_type == Any:
  37. raise TypeError(
  38. f"Could not foreach over var of type Any. (If you are trying to foreach over a state var, add a type annotation to the var.)"
  39. )
  40. arg = BaseVar(_var_name="_", _var_type=type_, _var_is_local=True)
  41. comp = IterTag(iterable=iterable, render_fn=render_fn).render_component(arg)
  42. return cls(
  43. iterable=iterable,
  44. render_fn=render_fn,
  45. children=[comp],
  46. **props,
  47. )
  48. def _render(self) -> IterTag:
  49. return IterTag(
  50. iterable=self.iterable,
  51. render_fn=self.render_fn,
  52. index_var_name=get_unique_variable_name(),
  53. )
  54. def render(self):
  55. """Render the component.
  56. Returns:
  57. The dictionary for template of component.
  58. """
  59. tag = self._render()
  60. try:
  61. type_ = (
  62. tag.iterable._var_type
  63. if tag.iterable._var_type.mro()[0] == dict
  64. else typing.get_args(tag.iterable._var_type)[0]
  65. )
  66. except Exception:
  67. type_ = Any
  68. arg = BaseVar(
  69. _var_name=get_unique_variable_name(),
  70. _var_type=type_,
  71. )
  72. index_arg = tag.get_index_var_arg()
  73. component = tag.render_component(arg)
  74. return dict(
  75. tag.add_props(
  76. **self.event_triggers,
  77. key=self.key,
  78. sx=self.style,
  79. id=self.id,
  80. class_name=self.class_name,
  81. ).set(
  82. children=[component.render()],
  83. props=tag.format_props(),
  84. ),
  85. iterable_state=tag.iterable._var_full_name,
  86. arg_name=arg._var_name,
  87. arg_index=index_arg,
  88. iterable_type=tag.iterable._var_type.mro()[0].__name__,
  89. )