Props and AEP2 #54
Replies: 6 comments 14 replies
-
If I understand you correctly, what you're missing is a way to override the injection globally, or within a context. I suppose to allow someone to change the default behavior of a component while leaving the possibility of overriding it locally with an explicit argument? If so, @component
def Hello(message: str = 'World') -> VDOM:
return html(f'<div>{message}</div>')
# defaults() added by @component, with the same argument as the Hello() function.
# overriding the default values of message.
with Hello.defaults(message="New World"):
... # here the default value is "New World" when creating any Hello component |
Beta Was this translation helpful? Give feedback.
-
Yes, on the first paragraph, an explicit argument. It isn't only with constants, by the way. |
Beta Was this translation helpful? Give feedback.
-
To be more explicit, I'd do something like this: from __future__ import annotations
import inspect
from contextlib import contextmanager
from typing import Any, TypeVar, Generic, Iterator, Callable
from typing_extensions import ParamSpec
T = TypeVar('T')
P = ParamSpec('P')
class Component(Generic[P, T]):
def __init__(self, func: Callable[P, T]) -> None:
if any(p.kind != inspect.Parameter.KEYWORD_ONLY
for p in inspect.signature(func).parameters.values()):
raise TypeError()
self.__defaults: dict[str, Any] = dict()
self.__wrapped__ = func
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
assert not args
kw = self.__defaults.copy()
kw.update(**kwargs)
return self.__wrapped__(**kw)
@contextmanager
def default(self, *args: P.args, **kwargs: P.kwargs) -> Iterator[None]:
assert not args
previous = self.__defaults.copy()
self.__defaults.update(**kwargs)
try:
yield
except Exception:
self.__defaults = previous
def component(f: Callable[P, T]) -> Component[P, T]:
return Component(f)
@component
def Hello(*, message: str = "World") -> str:
return f"Hello {message}!"
print(Hello())
print(Hello(message="Test"))
with Hello.default(message="New"):
print(Hello()) |
Beta Was this translation helpful? Give feedback.
-
Ok, played a bit trying to use |
Beta Was this translation helpful? Give feedback.
-
Thinking again about your issue, it seems to me that # Either pure contract
@interface
def current_user(short: bool = False) -> str:
...
# Or as the default implementation
@interface(as_default=True)
def current_user(short: bool = False) -> str:
return "Unknown"
@implements(current_user)
def auto_detect(short: bool = False) -> str:
return "John" if short else "John Doe"
@inject
def greeting(user: str = current_user(short=True)) -> str:
return f"Hello {user}"
For the context part, it's not obvious if it's really something that should be handled at the framework level. For a global context, you could have a singleton (or eventually state) which you change and inject wherever necessary. For local context, it'd probably be better to just simply propagate it downwards. |
Beta Was this translation helpful? Give feedback.
-
As an update, I have a package |
Beta Was this translation helpful? Give feedback.
-
I'm busy converting
viewdom
to use Antidote instead of my Hopscotch system. First big question: "props".Frontend components use node attributes as arguments to the component. For example, a React component:
Then later, some usage:
Thus, a single usage can provide a unique (but static) argument to the function.
Let's write that as a component in
viewdom
:You can also have a default value.
But you could also use injection, to avoid "prop drilling" (passing a value down a long component tree) plus other reasons.
However, we've just lost "props". There's no way to have a specific usage -- from a template -- pass in the value. If a value is passed in, it has higher priority than the injection.
How
viewdom
Does ThisIn
viewdom
, when you're in a template, the caller and callee are under its control. A template points at a component which might point to a subcomponent.viewdom
needs to construct an instance. It has the injector, but it also has the "props" of the caller.The injector goes through each parameter in the signature:
Is that parameter name a key in the props? If so, use it.
Is that parameter injectable? Inject it.
Does it have a default value? Use it.
Proposal
Antidote also has control of the caller and callee and handles construction of each parameter. But it has no place (currently) to find "props", which are a
viewdom
idea, not an Antidote idea.AEP2 might help.
viewdom
, as it is rendering, could put the props in some@state
value.But that's not enough. The Antidote injector would have to know to look there first. Of course, Antidote could grow some framework-ness. But that has a smell. Not sure the answer.
Beta Was this translation helpful? Give feedback.
All reactions