woernerm / dynoselect

Reflex select component which allows the user to search for options and create new ones.
Apache License 2.0
6 stars 1 forks source link

How I can add options from an API async request? #2

Open IvanPagan opened 5 months ago

IvanPagan commented 5 months ago

Hi, I need to load items in the dynoselect options loaded from an asynchronous call from an API. Any ideas, I'm trying several approaches but they all lead me to a dead end, where I dynamically have an rx.var that I can't assign to options, and I don't see a way to handle this with states. Sorry if this is obvious or has nothing to do with your component, but I would love to be able to use it in my app.

woernerm commented 5 months ago

Hello IvanPagan, can you post a short example of your code?

Best regards, Marcus

IvanPagan commented 5 months ago

Hi, this is the code, with the last test, getting dynoselect via classmethod from State, but I can't get the components to render again when the _options have been updated by on_mount. I imagine it won't be the right way, but I don't have much experience with Reflex, and I'm new to the React concept.

# data is returned from a list of dictionaries from an api
# [{'id': 1, 'name': 'name_example'}, {'id': 2, 'name': 'name_example2'}, {'id': 3, 'name': 'name_example3']
data = get_dyno_options()

# Function to parse api list to dyno options
def parse_to_options(data):
    options = []
    for item in data:
        options.append({"label": item['name'], "value": str(item['id'])})
    return options`

class DyndynoSelectState(rx.State):
      _options1: List[Dict[str, str]] = [{"label": "Loading...", "value": "0"}]
      _options2: List[Dict[str, str]] = [{"label": "Loading...", "value": "0"}]
      _options3: List[Dict[str, str]] = [{"label": "Loading...", "value": "0"}]
      selected_option1: str = ""
      selected_option2: str = ""
      selected_option3: str = ""
      updated: bool = False

    async def fetch_options1(self):
        api_data = get_dyno_options()
        parsed_options = parse_to_options(api_data)
        self._options1 = parsed_options
        self.updated = not self.updated

    async def fetch_options2(self):
        api_data = get_dyno_options()
        parsed_options = parse_to_options(api_data)
        self._options2 = parsed_options
        self.updated = not self.updated

    async def fetch_options3(self):
        api_data = get_dyno_options()
        parsed_options = parse_to_options(api_data)
        self._options3 = parsed_options
        self.updated = not self.updated

    async def on_mount(self):
        await self.fetch_options1()
        await self.fetch_options2()
        await self.fetch_options3()

    def set_selected_option1(self, value: str):
        self.selected_option1 = value
        print("Selected option 1: ", value)

    def set_selected_option2(self, value: str):
        self.selected_option2 = value
        print("Selected option 2: ", value)

    def set_selected_option3(self, value: str):
        self.selected_option3 = value
        print("Selected option 3: ", value)

    @classmethod
    def all_dyno_selects(cls) -> rx.Component:
        return rx.vstack(
                rx.color_mode.button(position="top-right"),
                rx.heading("Dinamic Dyno Select", size="9"),
                rx.button("CARGAR", size="2", on_click=cls.on_mount),
                rx.text(
                    "Select a Dyno to view its details.",
                    size="5",
                ),
                *[rx.text(item['label'], size="5") for item in cls._options1],
                dynoselect(
                    options=cls._options1,
                    placeholder="Select a Dyno 1",
                    on_select=cls.set_selected_option1,
                ),
                rx.spacer(),
                dynoselect(
                    options=cls._options2,
                    placeholder="Select a Dyno 2",
                    on_select=cls.set_selected_option2,
                ),
                rx.spacer(),
                dynoselect(
                    options=cls._options3,
                    placeholder="Select a Dyno 3",
                    on_select=cls.set_selected_option3,
                ),
                rx.spacer(),                
                rx.logo(),
            )

def dyndyno_page() -> rx.Component:
    print("OPTIONS 1: ", DyndynoSelectState._options1)
    print("OPTIONS 2: ", DyndynoSelectState._options2)
    print("OPTIONS 3: ", DyndynoSelectState._options3)
    return rx.container(
        DyndynoSelectState.all_dyno_selects(),
        on_mount=DyndynoSelectState.on_mount,
    )

[EDIT] In this version, I've used backend vars in _options1, _options2 and _options3, dynoselect can't work with rx.var passed to options, There's some way to do It?

Thanks

onexay commented 5 months ago

I also have a similar question, basically dynoselect doesn't work with dynamic data.

woernerm commented 5 months ago

Thanks for the code snippet. Now, I know what you are trying to do. Unfortunately, onexay is almost correct: The Dynoselect class has a member variable options that can be modified at runtime (look at the included dynotimezone component. It updates the UTC offset every time the dropdown is opened. However, the Dynotimezone class does this by inheriting from the Dynoselect class and then modifying its own options member variable. This is difficult to do from another state (I tried to find a workaround but came up empty).

Since the application I originally started Dynoselect for has all information at compile time, I never thought about changing the options at runtime this way. It is a reasonable use case, though. So, I am planning to include it in the next release of Dynoselect.

alexlausz commented 4 months ago

I also have a similar question, basically dynoselect doesn't work with dynamic data.我也有类似的问题,基本上 dynoselect 不适用于动态数据。

same here