-
Notifications
You must be signed in to change notification settings - Fork 291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Retrieve query params from URL to use in Mesop #340
Comments
Yep, I agree we should support some kind of get/set query params. |
Some ideas on potential APIs Option 1: Declarative API (preferred)We can use the fact that we have State class which are already serialized back-and-forth between the client and server and use a part of the State class as a query parameter. @me.stateclass(query_params=["prompt"])
class State:
prompt: str
some_thing: Message This would automatically set the value of If you mutated the value of prompt, then it would automatically update the query parameter. Option 2: Imperative APIYou could read query parameter as part of an on_load hook see #318 def on_load(e: LoadEvent):
me.state(State).prompt = e.query_params["p"] You could also set the query parameter in an event handler def click(e):
me.state(State).prompt = e.key
me.set_query_param("p", me.key) ThoughtsI think Option 1 is nicer because it avoids the need to manually synchronize between state attributes and query parameter, but Option 2 is more flexible (although I can't think of a case where this is actually beneficial). @richard-to WDYT? |
1There's two things about option 1 that give some slight pause:
2I like this option a bit better since it more clearly delineates the query params from state. So I don't mind manually setting state here. It also gives us another use case for on load. And also keeps a consistent pattern for setting the initial state dynamically based on the user. I think for updating query params in the event handler, maybe change that to using me.navigate. Although I'm curious your reasoning for allowing updating of query params with forcing navigation. It's possible that the query param is not stored in the state but used to load specific data for a state variable. if e.query_params["data_source"] == "X":
state.data = load_data_X()
else:
state.data = load_data_default() |
I think basically my view is that query params are a type of state. If you go back to the reactive UI paradigm of: UI is a function of state (i.e. For example, if you pay close attention to many of Search's features, they serialize their state into the URL. For example if you go to this movie search query it not only encodes which movie is selected but also the other movies in the carousel, so you get that same animation/transition in when you load the page. I think this is a fairly complex example which we probably don't need to support in Mesop any time soon, but I think eventually it'll be convenient to serialize/deserialize complex types (beyond strings) in query params. If you think about it, we could only support string in stateclass and make app developers serialize and deserialize by hand, but this would be a pain. The other aspect of this is that keeping it automatically sync'ed means that it's much easier to create apps where you can share a URL and the user will automatically get the same state as you were on. For example, if you have a prompt filled into a textarea, it becomes very convenient to have this populate in the URL and you can share this with a teammate. Doing this by hand is tedious and app developers will probably forget to do this. For the load_data example, I think you can treat these as two separate state properties. One of them (probably shorter, e.g. file path) is the query param, and you use that state property to load a much larger value (e.g. actual file contents) into a second state property, which isn't a query param. I think Option #2 is the conventional approach and it's basically Streamlit's API, but I feel like Option #1 is actually more consistent with Mesop's overall approach to state management, which is to provide strong type safety, incl. for complex types. Let me know what you think or we can chat more in-person - it's quite an involved topic :) |
Thanks for the explanation of the use case where someone may want to update the query params. That makes sense. I actually didn't know Google Search did that. So I think you've convinced me that it makes sense to be able to update the query params without refreshing. So two usages looks like:
I guess my main question is do we want to support encoding the state into query params as a core functionality? Or do you feel that is more implementation related? Additionally, do you have any concerns about max length for query strings? I took a cursory look but I'm not sure if the info I was seeing out of date or not. Seems like most browsers can handle somewhat large amounts of data. But there's still a limit. So given that, an option 3 could be to create a subclass of state for query param state. I think this makes sense since we can already have multiple state classes in mesop. So if someone wants to use query param state, then they can create a separate state class. This makes it clearer what is query param state and what is not. This way we don't need to annotate the state class. Also it avoids the issue of multiple state classes in the same app using query params that may overlap. I guess someone could still use multiple
|
I think encoding state into query params should be exposed as an API. After looking at your suggestion with @me.queryparamstate, I think we could something like this: @me.stateclass(mode="query_param")
class State:
... By default, if you just use In the future, we can have different modes like "in-memory" (i.e. if it's a singleton server, then you could just rely on the server to hold it in-memory) or something like "fs", "redis" if you want to store it in an external storage system (it probably wouldn't be a string, because you'd need more configuration, but the idea is that it's a unified stateclass API and how you want to store it is something you can easily switch between as a Mesop app developer.
I think really big query strings can be problematic, but I think we can point people to keeping your stateclass relatively small if you're storing it as a query param. I think the suggestion of having separate stateclass for query param is good because it'll encourage people to be deliberate about what they store in query param.
Yeah, it's possible there will be conflicts, we could use a hash of the module+class name and use that to prefix each attribute. I think to keep it simple though (and have prettier query param names) we can just use the attribute name and print a warning if there's a duplicate query param name. In general, I'd guess for most apps (since people are building fairly small single-page apps for mesop), they will use one state class at most for query param. |
I like that idea of adding a mode parameter to the stateclass decorator. That's a good point about using with other modes. That's an interesting idea. |
One other nice mode could be session storage using the built in Flask session object. This would allow temporary persistence of state even after reloads. This could be nice for certain use cases where you want certain settings to persist that may not be suitable in the query params. |
Recap: |
Right now it's not possible to get the query params from the URL.
Even request.args from flask would not work in this case since Mesop gets called via the
/ui
endpoint so the initial query params wouldn't be available in the flask request.The text was updated successfully, but these errors were encountered: