-
Notifications
You must be signed in to change notification settings - Fork 15
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
Fragment node to avoid context pitfall #82
Comments
I do not fully follow your example, can you have a look at the variable names, what is (If possible I would like to avoid adding fragments since lists basically fill the same need in most cases. But yes, maybe we could replace render_node with a fragment class, that could be interesting!) Edit: I really want to make it possible to introduce htpy as gradually as possible and have it play nice with jinja/django templates is really important! |
Sorry for the confusion.
In one template I only need the inner component so I just have I put together a branch with a test case and my expectations: https://github.com/geigerzaehler/htpy/blob/fragment/tests/test_fragment.py. |
On a larger note: One thing I like about this library (and that I value in general with APIs) is that there are few surprises and the behavior is predictable. And one expectation I built up while using it and reading through the docs is that I can freely use a An idea that I had was to distinguish between the return type |
In your example, before passing inner to jinja, what about something like this: env.globals["inner"] = lambda *args, **kwargs: render_node(inner(*args **kwargs)) Would that solve your immediate problem? Then you should be able to turn it into a string from the template while still be able to put inner as a child to outer. I still find that your proposed fragment class a nicer/more straightforward way of dealing with this since it would avoid having to make this wrapper altogether and the fragment element can both be used as a child node and render itself as a string if needed. fragment should also then implement
The intention of Node is how you describe Child. Node's are never returned anywhere from htpy or in the docs examples. React/TypeScript uses ReactNode for the same thing, that is why I went with Node and not something like Child. Maybe Child would be a better name but I think it would cause too much pain/breakage the rename it now. If you have ideas on how to improve the docs/other ways to communicate on this that would be very welcome! If we introduce a Fragment class, your inner_component could return Fragment, not Node, which would solve the problem: def inner_component() -> Fragment:
return Fragment(
div()["inner"],
_feature_component(),
) (Sometimes I use Node as return value, for instance if a component can return ... so just adding the Fragment class would solve a lot of things? 😆 |
An idea, not sure it would be useful, we could add a protocol like: class Component(typing.Protocol):
def __str__(self) -> str: ...
def __html__(self) -> Markup: ...
def __iter__(self) -> Iterator[str]: ... that could be documented as the preferred return value of all component functions pretty much everywhere. from htpy import Component, Fragment, div
def inner_component() -> Component:
return Fragment(
div()["inner"],
_feature_component(),
) It will guide you to wrap strings, lists, tuples or any other kind of Node types in a fragment to ensure all components can be composed and rendered. |
As an avid user of htpy I would not be opposed to a The |
Thinking more about the Component thing, it should probably not be a protocol, more like: Component = BaseElement | Fragment | ContextProvider | ContextConsumer so the Component type would be both 1) part of |
Also Edit: comment() can also be changed to return a fragment instead: |
Happy to see that you like the idea for a fragment. My two cents on the points that were raised.
I think a protocol would be a useful way for users to extend the library. With such a protocol I could have implemented
I think the protocol would need to include an
In my experience working with React-style code bases I found it more practical to use Of course, this is just my experience, so just treat as some input into your decision. |
I’d be happy to give the |
I think the introduction of Fragment would make such a protocol for extending things unnecessary :) Custom stuff could then always be wrapped in Fragment to make it adhere to the Component type. I am not sure what other points of extensions would be useful. Feel free to work on a Fragment implementation! :) |
It’s been mentioned before (#23) that a fragment might be helpful. I have the following situation: I’m gradually introducing
htpy
to a Django project to implement small components. I expose these components Jinja2 globals and it roughly looks like this:Of course, calling
inner
in a template does not work because it actually returns a tuple and renders something like(<Element '<div>...</div>'>, <function _feature_component at 0x76110619f4c0>)
So I tried to fix this as follows:
But then rendering the outer component does not respect the context anymore.
The example may look a bit contrived, but I’m working under a lot of constraints that I can’t address.
My suggestion is to introduce a
fragment
class (or whatever you want to name it):fragment
could replacerender_node
everywhere. (Plus it has a slightly more convenient API since you don’t need to wrap the arguments in a list or tuple)The
_iter_node_context
implementation is pretty straight forward:The text was updated successfully, but these errors were encountered: