mirror of
https://github.com/django-components/django-components.git
synced 2025-09-27 16:09:07 +00:00
refactor: change context_behavior default to "django" (#515)
This commit is contained in:
parent
881c36219a
commit
f3e0bf6b62
2 changed files with 62 additions and 42 deletions
102
README.md
102
README.md
|
@ -44,6 +44,11 @@ Read on to learn about the details!
|
||||||
|
|
||||||
## Release notes
|
## Release notes
|
||||||
|
|
||||||
|
🚨📢 **Version 0.79**
|
||||||
|
|
||||||
|
- BREAKING CHANGE: Default value for the `COMPONENTS.context_behavior` setting was changes from `"isolated"` to `"django"`. If you did not set this value explicitly before, this may be a breaking change. See the rationale for change [here](https://github.com/EmilStenstrom/django-components/issues/498).
|
||||||
|
|
||||||
|
|
||||||
🚨📢 **Version 0.77** CHANGED the syntax for accessing default slot content.
|
🚨📢 **Version 0.77** CHANGED the syntax for accessing default slot content.
|
||||||
- Previously, the syntax was
|
- Previously, the syntax was
|
||||||
`{% fill "my_slot" as "alias" %}` and `{{ alias.default }}`.
|
`{% fill "my_slot" as "alias" %}` and `{{ alias.default }}`.
|
||||||
|
@ -1360,9 +1365,11 @@ attributes_to_string(attrs)
|
||||||
|
|
||||||
## Component context and scope
|
## Component context and scope
|
||||||
|
|
||||||
By default, components are ISOLATED and CANNOT access context variables from the parent template. This is useful if you want to make sure that components don't accidentally access the outer context.
|
By default, context variables are passed down the template as in regular Django - deeper scopes can access the variables from the outer scopes. So if you have several nested forloops, then inside the deep-most loop you can access variables defined by all previous loops.
|
||||||
|
|
||||||
You can set the [context_behavior](#context-behavior) option to `"django"`, to make components behave just like templates that are included with the `{% include %}` tag. Just like with `{% include %}`, if you don't want a specific component template to have access to the parent context, add `only` to the end of the `{% component %}` tag:
|
With this in mind, the `{% component %}` tag behaves similarly to `{% include %}` tag - inside the component tag, you can access all variables that were defined outside of it.
|
||||||
|
|
||||||
|
And just like with `{% include %}`, if you don't want a specific component template to have access to the parent context, add `only` to the end of the `{% component %}` tag:
|
||||||
|
|
||||||
```htmldjango
|
```htmldjango
|
||||||
{% component "calendar" date="2015-06-19" only %}{% endcomponent %}
|
{% component "calendar" date="2015-06-19" only %}{% endcomponent %}
|
||||||
|
@ -1370,7 +1377,9 @@ You can set the [context_behavior](#context-behavior) option to `"django"`, to m
|
||||||
|
|
||||||
NOTE: `{% csrf_token %}` tags need access to the top-level context, and they will not function properly if they are rendered in a component that is called with the `only` modifier.
|
NOTE: `{% csrf_token %}` tags need access to the top-level context, and they will not function properly if they are rendered in a component that is called with the `only` modifier.
|
||||||
|
|
||||||
Components can also access the outer context in their context methods by accessing the property `outer_context`.
|
If you find yourself using the `only` modifier often, you can set the [context_behavior](#context-behavior) option to `"isolated"`, which automatically applies the `only` modifier. This is useful if you want to make sure that components don't accidentally access the outer context.
|
||||||
|
|
||||||
|
Components can also access the outer context in their context methods like `get_context_data` by accessing the property `self.outer_context`.
|
||||||
|
|
||||||
## Rendering JS and CSS dependencies
|
## Rendering JS and CSS dependencies
|
||||||
|
|
||||||
|
@ -1448,15 +1457,25 @@ COMPONENTS = {
|
||||||
|
|
||||||
> NOTE: `context_behavior` and `slot_context_behavior` options were merged in v0.70.
|
> NOTE: `context_behavior` and `slot_context_behavior` options were merged in v0.70.
|
||||||
>
|
>
|
||||||
> If you are migrating from BEFORE v0.67, set `context_behavior` to `"django"`. From v0.67 and later use the default value `"isolated"`.
|
> If you are migrating from BEFORE v0.67, set `context_behavior` to `"django"`. From v0.67 to v0.78 (incl) the default value was `"isolated"`.
|
||||||
|
>
|
||||||
|
> For v0.79 and later, the default is again `"django"`. See the rationale for change [here](https://github.com/EmilStenstrom/django-components/issues/498).
|
||||||
|
|
||||||
You can configure what variables are available inside the `{% fill %}` tags. See [Component context and scope](#component-context-and-scope).
|
You can configure what variables are available inside the `{% fill %}` tags. See [Component context and scope](#component-context-and-scope).
|
||||||
|
|
||||||
This has two modes:
|
This has two modes:
|
||||||
|
|
||||||
- `"django"` - Context variables are taken from its surroundings (default before v0.67)
|
- `"django"` - Default - The default Django template behavior.
|
||||||
|
|
||||||
- `"isolated"` - Default - Similar behavior to [Vue](https://vuejs.org/guide/components/slots.html#render-scope) or React - variables are taken ONLY from `get_context_data()` method of the component which defines the `{% fill %}` tag. This is useful if you want to make sure that components don't accidentally access the outer context.
|
Inside the `{% fill %}` tag, the context variables you can access are a union of:
|
||||||
|
- All the variables that were OUTSIDE the fill tag, including any loops or with tag
|
||||||
|
- Data returned from `get_context_data()` of the component that wraps the fill tag.
|
||||||
|
|
||||||
|
- `"isolated"` - Similar behavior to [Vue](https://vuejs.org/guide/components/slots.html#render-scope) or React, this is useful if you want to make sure that components don't accidentally access variables defined outside of the component.
|
||||||
|
|
||||||
|
Inside the `{% fill %}` tag, you can ONLY access variables from 2 places:
|
||||||
|
- `get_context_data()` of the component which defined the template (AKA the "root" component)
|
||||||
|
- Any loops (`{% for ... %}`) that the `{% fill %}` tag is part of.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
COMPONENTS = {
|
COMPONENTS = {
|
||||||
|
@ -1466,75 +1485,76 @@ COMPONENTS = {
|
||||||
|
|
||||||
#### Example "django"
|
#### Example "django"
|
||||||
|
|
||||||
Given this template
|
Given this template:
|
||||||
|
|
||||||
```django
|
|
||||||
{% with cheese="feta" %}
|
|
||||||
{% component 'my_comp' %}
|
|
||||||
{{ my_var }} # my_var
|
|
||||||
{{ cheese }} # cheese
|
|
||||||
{% endcomponent %}
|
|
||||||
{% endwith %}
|
|
||||||
```
|
|
||||||
|
|
||||||
and this context returned from the `get_context_data()` method
|
|
||||||
|
|
||||||
```py
|
```py
|
||||||
{ "my_var": 123 }
|
class RootComp(component.Component):
|
||||||
|
template = """
|
||||||
|
{% with cheese="feta" %}
|
||||||
|
{% component 'my_comp' %}
|
||||||
|
{{ my_var }} # my_var
|
||||||
|
{{ cheese }} # cheese
|
||||||
|
{% endcomponent %}
|
||||||
|
{% endwith %}
|
||||||
|
"""
|
||||||
|
def get_context_data(self):
|
||||||
|
return { "my_var": 123 }
|
||||||
```
|
```
|
||||||
|
|
||||||
Then if component "my_comp" defines context
|
Then if `get_context_data()` of the component `"my_comp"` returns following data:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
{ "my_var": 456 }
|
{ "my_var": 456 }
|
||||||
```
|
```
|
||||||
|
|
||||||
Then this will render:
|
Then the template will be rendered as:
|
||||||
|
|
||||||
```django
|
```django
|
||||||
456 # my_var
|
456 # my_var
|
||||||
feta # cheese
|
feta # cheese
|
||||||
```
|
```
|
||||||
|
|
||||||
Because "my_comp" overrides the variable "my_var",
|
Because `"my_comp"` overshadows the variable `"my_var"`,
|
||||||
so `{{ my_var }}` equals `456`.
|
so `{{ my_var }}` equals `456`.
|
||||||
And variable "cheese" equals `feta`, because the fill CAN access
|
|
||||||
the current context.
|
And variable `"cheese"` equals `feta`, because the fill CAN access
|
||||||
|
all the data defined in the outer layers, like the `{% with %}` tag.
|
||||||
|
|
||||||
#### Example "isolated"
|
#### Example "isolated"
|
||||||
|
|
||||||
Given this template
|
Given this template:
|
||||||
|
|
||||||
```django
|
|
||||||
{% with cheese="feta" %}
|
|
||||||
{% component 'my_comp' %}
|
|
||||||
{{ my_var }} # my_var
|
|
||||||
{{ cheese }} # cheese
|
|
||||||
{% endcomponent %}
|
|
||||||
{% endwith %}
|
|
||||||
```
|
|
||||||
|
|
||||||
and this context returned from the `get_context_data()` method
|
|
||||||
|
|
||||||
```py
|
```py
|
||||||
{ "my_var": 123 }
|
class RootComp(component.Component):
|
||||||
|
template = """
|
||||||
|
{% with cheese="feta" %}
|
||||||
|
{% component 'my_comp' %}
|
||||||
|
{{ my_var }} # my_var
|
||||||
|
{{ cheese }} # cheese
|
||||||
|
{% endcomponent %}
|
||||||
|
{% endwith %}
|
||||||
|
"""
|
||||||
|
def get_context_data(self):
|
||||||
|
return { "my_var": 123 }
|
||||||
```
|
```
|
||||||
|
|
||||||
Then if component "my_comp" defines context
|
Then if `get_context_data()` of the component `"my_comp"` returns following data:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
{ "my_var": 456 }
|
{ "my_var": 456 }
|
||||||
```
|
```
|
||||||
|
|
||||||
Then this will render:
|
Then the template will be rendered as:
|
||||||
|
|
||||||
```django
|
```django
|
||||||
123 # my_var
|
123 # my_var
|
||||||
# cheese
|
# cheese
|
||||||
```
|
```
|
||||||
|
|
||||||
Because both variables "my_var" and "cheese" are taken from the parent's `get_context_data()`.
|
Because variables `"my_var"` and `"cheese"` are searched only inside `RootComponent.get_context_data()`.
|
||||||
But since "cheese" is not defined there, it's empty.
|
But since `"cheese"` is not defined there, it's empty.
|
||||||
|
|
||||||
|
Notice that the variables defined with the `{% with %}` tag are ignored inside the `{% fill %}` tag with the `"isolated"` mode.
|
||||||
|
|
||||||
## Logging and debugging
|
## Logging and debugging
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ class AppSettings:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def CONTEXT_BEHAVIOR(self) -> ContextBehavior:
|
def CONTEXT_BEHAVIOR(self) -> ContextBehavior:
|
||||||
raw_value = self.settings.get("context_behavior", ContextBehavior.ISOLATED.value)
|
raw_value = self.settings.get("context_behavior", ContextBehavior.DJANGO.value)
|
||||||
return self._validate_context_behavior(raw_value)
|
return self._validate_context_behavior(raw_value)
|
||||||
|
|
||||||
def _validate_context_behavior(self, raw_value: ContextBehavior) -> ContextBehavior:
|
def _validate_context_behavior(self, raw_value: ContextBehavior) -> ContextBehavior:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue