-
Notifications
You must be signed in to change notification settings - Fork 289
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
38c486f
commit 3ffd914
Showing
23 changed files
with
820 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# Query Params API | ||
|
||
## Overview | ||
|
||
Query params, also sometimes called query string, provide a way to manage state in the URLs. They are useful for providing deep-links into your Mesop app. | ||
|
||
## Example | ||
|
||
Here's a simple working example that shows how you can read and write query params. | ||
|
||
```py | ||
@me.page(path="/examples/query_params/page_2") | ||
def page_2(): | ||
me.text(f"query_params={me.query_params}") | ||
me.button("Add query param", on_click=add_query_param) | ||
me.button("Navigate", on_click=navigate) | ||
|
||
def add_query_param(e: me.ClickEvent): | ||
me.query_params["key"] = "value" | ||
|
||
def navigate(e: me.ClickEvent): | ||
me.navigate("/examples/query_params", query_params=me.query_params) | ||
``` | ||
|
||
## Usage | ||
|
||
You can use query parameters from `me.query_params`, which has a dictionary-like interface, where the key is the parameter name and value is the parameter value. | ||
|
||
### Get a query param value | ||
|
||
```py | ||
value: str = me.query_params['param_name'] | ||
``` | ||
This will raise a KeyError if the parameter doesn't exist. You can use `in` to check whether a key exists in `me.query_params`: | ||
|
||
```py | ||
if 'key' in me.query_params: | ||
print(me.query_params['key']) | ||
``` | ||
|
||
???+ NOTE "Repeated query params" | ||
If a query param key is repeated, then you will get the _first_ value. If you want all the values use `get_all`. | ||
|
||
### Get all values | ||
|
||
To get all the values for a particular query parameter key, you can use `me.query_params.get_all`, which returns a sequence of parameter values (currently implemented as a `tuple`). | ||
|
||
```py | ||
all_values = me.query_params.get_all('param_name') | ||
``` | ||
|
||
### Iterate | ||
|
||
```py | ||
for key in query_params: | ||
value = query_params[key] | ||
``` | ||
|
||
### Set query param | ||
|
||
```py | ||
query_params['new_param'] = 'value' | ||
``` | ||
|
||
### Set repeated query param | ||
|
||
```py | ||
query_params['repeated_param'] = ['value1', 'value2'] | ||
``` | ||
|
||
### Delete | ||
|
||
```py | ||
del query_params['param_to_delete'] | ||
``` | ||
|
||
## Patterns | ||
|
||
### Navigate with existing query params | ||
|
||
Here's an example of how to navigate to a new page with query parameters: | ||
|
||
```py | ||
def click_navigate_button(e: me.ClickEvent): | ||
me.query_params['q'] = "value" | ||
me.navigate('/search', query_params=me.query_params) | ||
``` | ||
|
||
### Navigate with only new query params | ||
|
||
You can also navigate by passing in a dictionary to `query_params` parameter for `me.navigate` if you do _not_ want to keep the existing query parameters. | ||
|
||
```py | ||
def click_navigate_button(e: me.ClickEvent): | ||
me.navigate('/search', query_params={"q": "value}) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,39 @@ | ||
from typing import Sequence | ||
|
||
from mesop.features.query_params import ( | ||
QueryParams, | ||
) | ||
from mesop.features.query_params import ( | ||
query_params as me_query_params, | ||
) | ||
from mesop.runtime import runtime | ||
from mesop.utils.url_utils import remove_url_query_param | ||
from mesop.warn import warn | ||
|
||
|
||
def navigate(url: str) -> None: | ||
def navigate( | ||
url: str, | ||
*, | ||
query_params: dict[str, str | Sequence[str]] | QueryParams | None = None, | ||
) -> None: | ||
""" | ||
Navigates to the given URL. | ||
Args: | ||
url: The URL to navigate to. | ||
query_params: A dictionary of query parameters to include in the URL, or `me.query_params`. If not provided, all current query parameters will be removed. | ||
""" | ||
runtime().context().navigate(url) | ||
cleaned_url = remove_url_query_param(url) | ||
if url != cleaned_url: | ||
warn( | ||
"Used me.navigate to navigate to a URL with query params. The query params have been removed. " | ||
"Instead pass the query params using the keyword argument like this: " | ||
"me.navigate(url, query_params={'key': 'value'})" | ||
) | ||
if isinstance(query_params, QueryParams): | ||
query_params = {key: query_params.get_all(key) for key in query_params} | ||
|
||
# Clear the query params because the query params will | ||
# either be replaced with the new query_params or emptied (in server.py). | ||
me_query_params.clear() | ||
runtime().context().navigate(cleaned_url, query_params) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import mesop as me | ||
|
||
|
||
def on_load(e: me.LoadEvent): | ||
me.query_params["on_load"] = "loaded" | ||
|
||
|
||
@me.page(path="/examples/query_params", on_load=on_load) | ||
def page(): | ||
me.text(f"query_params={me.query_params}") | ||
me.button("append query param list", on_click=append_query_param_list) | ||
me.button("URL-encoded query param", on_click=url_encoded_query_param) | ||
me.button( | ||
"navigate URL-encoded query param", | ||
on_click=navigate_url_encoded_query_param, | ||
) | ||
me.button("increment query param directly", on_click=increment_query_param) | ||
me.button("delete list query param", on_click=delete_list_query_param) | ||
me.button("delete all query params", on_click=delete_all_query_params) | ||
me.button("increment query param by navigate", on_click=increment_by_navigate) | ||
me.button("navigate to page 2 with query params", on_click=navigate_page_2) | ||
me.button("navigate to page 2 with dict", on_click=navigate_page_2_new_dict) | ||
me.button( | ||
"navigate to page 2 without query params", | ||
on_click=navigate_page_2_without_query_params, | ||
) | ||
|
||
|
||
@me.stateclass | ||
class State: | ||
click_append_query_param_list: int | ||
|
||
|
||
def append_query_param_list(e: me.ClickEvent): | ||
current_list = ( | ||
[] if "list" not in me.query_params else me.query_params.get_all("list") | ||
) | ||
state = me.state(State) | ||
state.click_append_query_param_list += 1 | ||
me.query_params["list"] = [ | ||
*current_list, | ||
f"val{state.click_append_query_param_list}", | ||
] | ||
|
||
|
||
def url_encoded_query_param(e: me.ClickEvent): | ||
me.query_params["url_encoded"] = ["&should-be-escaped=true"] | ||
|
||
|
||
def navigate_url_encoded_query_param(e: me.ClickEvent): | ||
me.navigate( | ||
"/examples/query_params/page_2", | ||
query_params={ | ||
"url_encoded": "&should-be-escaped=true", | ||
"url_encoded_values": ["value1&a=1", "value2&a=2"], | ||
}, | ||
) | ||
|
||
|
||
def increment_query_param(e: me.ClickEvent): | ||
if "counter" not in me.query_params: | ||
me.query_params["counter"] = "0" | ||
|
||
me.query_params["counter"] = str(int(me.query_params["counter"]) + 1) | ||
|
||
|
||
def delete_list_query_param(e: me.ClickEvent): | ||
del me.query_params["list"] | ||
|
||
|
||
def delete_all_query_params(e: me.ClickEvent): | ||
for key in list(me.query_params.keys()): | ||
del me.query_params[key] | ||
|
||
|
||
def navigate_page_2(e: me.ClickEvent): | ||
me.navigate("/examples/query_params/page_2", query_params=me.query_params) | ||
|
||
|
||
def navigate_page_2_new_dict(e: me.ClickEvent): | ||
me.navigate( | ||
"/examples/query_params/page_2", | ||
query_params={"page2": ["1", "2"], "single": "a"}, | ||
) | ||
|
||
|
||
def navigate_page_2_without_query_params(e: me.ClickEvent): | ||
me.navigate("/examples/query_params/page_2") | ||
|
||
|
||
def increment_by_navigate(e: me.ClickEvent): | ||
if "counter" not in me.query_params: | ||
me.query_params["counter"] = "0" | ||
|
||
counter = int(me.query_params["counter"]) + 1 | ||
me.query_params["counter"] = str(counter) | ||
|
||
me.navigate( | ||
"/examples/query_params", | ||
query_params=me.query_params, | ||
) | ||
|
||
|
||
@me.page(path="/examples/query_params/page_2") | ||
def page_2(): | ||
me.text(f"query_params(page_2)={me.query_params}") | ||
me.button("Navigate back", on_click=navigate_back) | ||
|
||
|
||
def navigate_back(e: me.ClickEvent): | ||
me.navigate("/examples/query_params", query_params=me.query_params) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,24 @@ | ||
load("//build_defs:defaults.bzl", "py_library") | ||
load("//build_defs:defaults.bzl", "THIRD_PARTY_PY_PYTEST", "py_library", "py_test") | ||
|
||
package( | ||
default_visibility = ["//build_defs:mesop_internal"], | ||
) | ||
|
||
py_library( | ||
name = "features", | ||
srcs = glob(["*.py"]), | ||
srcs = glob( | ||
["*.py"], | ||
exclude = ["*_test.py"], | ||
), | ||
deps = [ | ||
"//mesop/runtime", | ||
], | ||
) | ||
|
||
py_test( | ||
name = "query_params_test", | ||
srcs = ["query_params_test.py"], | ||
deps = [ | ||
":features", | ||
] + THIRD_PARTY_PY_PYTEST, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
from typing import Callable, Iterator, MutableMapping, Sequence | ||
|
||
from mesop.runtime import runtime | ||
from mesop.runtime.context import Context | ||
|
||
|
||
class QueryParams(MutableMapping[str, str]): | ||
def __init__(self, get_context: Callable[[], Context]): | ||
self._get_context = get_context | ||
|
||
def __iter__(self) -> Iterator[str]: | ||
return iter(self._get_context().query_params()) | ||
|
||
def __len__(self) -> int: | ||
return len(self._get_context().query_params()) | ||
|
||
def __str__(self) -> str: | ||
return str(self._get_context().query_params()) | ||
|
||
def __getitem__(self, key: str) -> str: | ||
# Returns the first value associated with the key to match | ||
# the web API: | ||
# https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/get | ||
return self._get_context().query_params()[key][0] | ||
|
||
def get_all(self, key: str) -> Sequence[str]: | ||
if key not in self._get_context().query_params(): | ||
return tuple() | ||
return tuple(self._get_context().query_params()[key]) | ||
|
||
def __delitem__(self, key: str) -> None: | ||
self._get_context().set_query_param(key=key, value=None) | ||
|
||
def __setitem__(self, key: str, value: str | Sequence[str]) -> None: | ||
self._get_context().set_query_param(key=key, value=value) | ||
|
||
|
||
query_params = QueryParams(runtime().context) |
Oops, something went wrong.