Closed JuroOravec closed 2 weeks ago
Hi! I like the custom tags version just like you do, because it feels like the simplest way to make this work.
A fifth option would be to actually make editable=not disabled
work like you would expect it to. That is, allow arbitrary boolean operators just like if we where executing an if block like this editable=editable and not disabled
.
About merge_attrs, I wonder if we can expand how tags can be sent to components again?
{% component "tab"
attrs=attrs # Default dict sent to the component
attrs:@click="(evt) => alert(evt.target.data)"
%}{% endcomponent %}
Today I also looked a bit more into the spread operator, as it feels like the last crucial step (after the self-closing tags and the dynamic expressions), to have the templating syntax similarly powerful as React or Vue. For the UI templating library, migrating components from JSX to Django components is currently the biggest bottleneck, because things have to be done differently in the latter. That's why I'd like get these features first.
Anyway, notes:
_parse_component_args
, etc). With the spread operator, we'd know all the kwargs passed to a node only at render time. So that code will have to be moved into the render
method of respective nodes (e.g. ComponentNode
, SlotNode
, etc).Since Django components supports both positional and keyword args, this raised a question of how the spread should work - AKA whether one could spread lists into positional args, or only dictionaries into kwargs.
IMO, the spread operator should be treated as a keyword arg, which means that:
With the dynamic expressions, we could actually have two formats:
{% component 'test'
my_var="123"
...dict <----- Spreading a variable
another=1
...="{{ abc }}" <----- Spreading the results of an expression
/ %}
**dict
, as a native Python way to spread a dictionary, but Django template parser doesn't support asterisks (*
), and I don't feel like it's necessary to expand the set only for this....
. Unlike the example above, what could also work is if we treated ...
as a key. So instead of ...dict
, it would be ...=dict
{% component 'test'
my_var="123"
...=dict <----- Spreading a variable
another=1
...="{{ abc }}" <----- Spreading the results of an expression
/ %}
=
), then we could also have a dedicated key, other than ...
, that suggest spreading. E.g. how in Vue it's v-bind
."...{{ }}"
, to take inspiration from the dynamic expressions. But this looks like it suggests that syntax could be used also in places where the dynamic expressions are used, and that's not true. Furthermore, I also like ...=dict
more, because it is a kwarg, so implementing it will be easier than internally transforming a non-kwarg into a kwarg, so it doesn't raise errors during parsing.I think keeping things simple is likely the best way forward here. I'm +1 adding support for just ...dict
in the template.
Problem
I'm updating one of my projects from v0.73 to v0.82. Features like the
html_attrs
tag,prop:key=value
syntax and provide/inject cleaned up the code really nicely.However, there are still a few use cases where I need to define HTML attributes in Python (instead of keeping them in the HTML), to work around the limitations of the library. This adds unnecesary mental overhead as I need to trace where the HTML attributes belong.
Consider the example below, where:
I am passing
attrs
(dict with HTML attrs) to child componenttab
. I want to add some extra HTML attrs to it (@click=...
). Ideally the@click=...
HTML attribute could live in the template, directly on thetab
component. But because I needed to merge my HTML attribute with the user-givenattrs
, I had to define it in the Python section.I'm passing in a list of TabItem objects. These have
disabled
attribute. However,input_form
component acceptseditable
, which in this case is the negation ofdisabled
. I cannot doeditable=not disabled
inside the template, so I had to convert the list ofTabItems
to a list of dictionaries, so I could define an extra keyreadonly
.Solution
❌ Custom filters
While custom filters could be enough for the negation:
It's already insufficient for ternary (if/else). Django has the built-in
yesno
filter, but the filter works only with strings. I cannot useyesno
to decide between two objects.This works:
But I cannot achieve this:
One custom filter I can think of that could work is if I defined a filter that runs a function:
But here the limitation is that I could pass only a single value to it.
So while it could work with predefined True/False values:
I couldn't pass the True/False values on the spot:
✅ Custom tags
The upside of tags is that you can pass in an arbirary number of arguments, and you can capture the output with
as var
syntax:And this could be also used for merging of the HTML attributes inside the template:
❓Inlined custom tags
In the Custom tags examples, the tag still had to be defined on a separate line. I wonder if we could have a feature similar to React or Vue, where you could directly put logic as the value of the prop.
So what in React looks like:
Could possibly be achieved Django something like this:
Where the value wrapped in
`{% ... %}`
would mean "interpret the content as template tag".Implementation notes:
`{% ... %}`
construct, because it may be threw off by the inner%}
.`{% ... %}`
, we'd use Django's smart_split, which is the same as what Django uses when it parses template tags.as var
from the inlined template tag, since it's implied that we return the output of it.❓Spread operator
One last limitation when working with the components currently is that it doesn't have a spread operator. Again this is mostly useful when I want to combine my inputs with inputs given from outside.
For context, in React, it looks like this:
And Vue:
In Django it could look like this:
We already allow dots (
.
) in kwarg names for components. So we would give special treatment to kwargs that start with...
, and put the dict entries in it's place.