expression - Django-Components" > expression - Django-Components" >
Skip to content

expression ¤

Operator ¤

Bases: ABC

Operator describes something that somehow changes the inputs to template tags (the {% %}).

For example, a SpreadOperator inserts one or more kwargs at the specified location.

SpreadOperator ¤

SpreadOperator(expr: Expression)

Bases: Operator

Operator that inserts one or more kwargs at the specified location.

Source code in src/django_components/expression.py
def __init__(self, expr: Expression) -> None:
    self.expr = expr

process_aggregate_kwargs ¤

process_aggregate_kwargs(kwargs: Mapping[str, Any]) -> Dict[str, Any]

This function aggregates "prefixed" kwargs into dicts. "Prefixed" kwargs start with some prefix delimited with : (e.g. attrs:).

Example:

process_component_kwargs({"abc:one": 1, "abc:two": 2, "def:three": 3, "four": 4})
# {"abc": {"one": 1, "two": 2}, "def": {"three": 3}, "four": 4}


We want to support a use case similar to Vue's fallthrough attributes. In other words, where a component author can designate a prop (input) which is a dict and which will be rendered as HTML attributes.

This is useful for allowing component users to tweak styling or add event handling to the underlying HTML. E.g.:

class="pa-4 d-flex text-black" or @click.stop="alert('clicked!')"

So if the prop is attrs, and the component is called like so:

{% component "my_comp" attrs=attrs %}

then, if attrs is:

{"class": "text-red pa-4", "@click": "dispatch('my_event', 123)"}

and the component template is:

<div {% html_attrs attrs add:class="extra-class" %}></div>

Then this renders:

<div class="text-red pa-4 extra-class" @click="dispatch('my_event', 123)" ></div>

However, this way it is difficult for the component user to define the attrs variable, especially if they want to combine static and dynamic values. Because they will need to pre-process the attrs dict.

So, instead, we allow to "aggregate" props into a dict. So all props that start with attrs:, like attrs:class="text-red", will be collected into a dict at key attrs.

This provides sufficient flexiblity to make it easy for component users to provide "fallthrough attributes", and sufficiently easy for component authors to process that input while still being able to provide their own keys.

Source code in src/django_components/expression.py
def process_aggregate_kwargs(kwargs: Mapping[str, Any]) -> Dict[str, Any]:
    """
    This function aggregates "prefixed" kwargs into dicts. "Prefixed" kwargs
    start with some prefix delimited with `:` (e.g. `attrs:`).

    Example:
    ```py
    process_component_kwargs({"abc:one": 1, "abc:two": 2, "def:three": 3, "four": 4})
    # {"abc": {"one": 1, "two": 2}, "def": {"three": 3}, "four": 4}
    ```

    ---

    We want to support a use case similar to Vue's fallthrough attributes.
    In other words, where a component author can designate a prop (input)
    which is a dict and which will be rendered as HTML attributes.

    This is useful for allowing component users to tweak styling or add
    event handling to the underlying HTML. E.g.:

    `class="pa-4 d-flex text-black"` or `@click.stop="alert('clicked!')"`

    So if the prop is `attrs`, and the component is called like so:
    ```django
    {% component "my_comp" attrs=attrs %}
    ```

    then, if `attrs` is:
    ```py
    {"class": "text-red pa-4", "@click": "dispatch('my_event', 123)"}
    ```

    and the component template is:
    ```django
    <div {% html_attrs attrs add:class="extra-class" %}></div>
    ```

    Then this renders:
    ```html
    <div class="text-red pa-4 extra-class" @click="dispatch('my_event', 123)" ></div>
    ```

    However, this way it is difficult for the component user to define the `attrs`
    variable, especially if they want to combine static and dynamic values. Because
    they will need to pre-process the `attrs` dict.

    So, instead, we allow to "aggregate" props into a dict. So all props that start
    with `attrs:`, like `attrs:class="text-red"`, will be collected into a dict
    at key `attrs`.

    This provides sufficient flexiblity to make it easy for component users to provide
    "fallthrough attributes", and sufficiently easy for component authors to process
    that input while still being able to provide their own keys.
    """
    processed_kwargs = {}
    nested_kwargs: Dict[str, Dict[str, Any]] = {}
    for key, val in kwargs.items():
        if not is_aggregate_key(key):
            processed_kwargs[key] = val
            continue

        # NOTE: Trim off the prefix from keys
        prefix, sub_key = key.split(":", 1)
        if prefix not in nested_kwargs:
            nested_kwargs[prefix] = {}
        nested_kwargs[prefix][sub_key] = val

    # Assign aggregated values into normal input
    for key, val in nested_kwargs.items():
        if key in processed_kwargs:
            raise TemplateSyntaxError(
                f"Received argument '{key}' both as a regular input ({key}=...)"
                f" and as an aggregate dict ('{key}:key=...'). Must be only one of the two"
            )
        processed_kwargs[key] = val

    return processed_kwargs