mirror of
https://github.com/django-components/django-components.git
synced 2025-08-31 19:27:19 +00:00
docs: Move docs-folder to root (#816)
* Move docs-folder form src to root * Avoid mkdocs package / module name clash * Update location of docs & add Windows compatibility * Update requirements-docs * Update generated file to current state
This commit is contained in:
parent
cdc830fca3
commit
594c0689ba
68 changed files with 116 additions and 108 deletions
227
docs/concepts/advanced/rendering_js_css.md
Normal file
227
docs/concepts/advanced/rendering_js_css.md
Normal file
|
@ -0,0 +1,227 @@
|
|||
---
|
||||
title: Rendering JS / CSS
|
||||
weight: 1
|
||||
---
|
||||
|
||||
### JS and CSS output locations
|
||||
|
||||
If:
|
||||
|
||||
1. Your components use JS and CSS via any of:
|
||||
- [`Component.css`](#TODO)
|
||||
- [`Component.js`](#TODO)
|
||||
- [`Component.Media.css`](#TODO)
|
||||
- [`Component.Media.js`](#TODO)
|
||||
2. And you use the [`ComponentDependencyMiddleware`](#TODO) middleware
|
||||
|
||||
Then, by default, the components' JS and CSS will be automatically inserted into the HTML:
|
||||
|
||||
- CSS styles will be inserted at the end of the `<head>`
|
||||
- JS scripts will be inserted at the end of the `<body>`
|
||||
|
||||
If you want to place the dependencies elsewhere in the HTML, you can override
|
||||
the locations by inserting following Django template tags:
|
||||
|
||||
- [`{% component_js_dependencies %}`](#TODO) - Set new location(s) for JS scripts
|
||||
- [`{% component_css_dependencies %}`](#TODO) - Set new location(s) for CSS styles
|
||||
|
||||
So if you have a component with JS and CSS:
|
||||
|
||||
```python
|
||||
from django_components import Component, types
|
||||
|
||||
class MyButton(Component):
|
||||
template: types.django_html = """
|
||||
<button class="my-button">
|
||||
Click me!
|
||||
</button>
|
||||
"""
|
||||
js: types.js = """
|
||||
for (const btnEl of document.querySelectorAll(".my-button")) {
|
||||
btnEl.addEventListener("click", () => {
|
||||
console.log("BUTTON CLICKED!");
|
||||
});
|
||||
}
|
||||
"""
|
||||
css: types.css """
|
||||
.my-button {
|
||||
background: green;
|
||||
}
|
||||
"""
|
||||
|
||||
class Media:
|
||||
js = ["/extra/script.js"]
|
||||
css = ["/extra/style.css"]
|
||||
```
|
||||
|
||||
Then the JS from `MyButton.js` and `MyButton.Media.js` will be rendered at the default place,
|
||||
or in [`{% component_js_dependencies %}`](#TODO).
|
||||
|
||||
And the CSS from `MyButton.css` and `MyButton.Media.css` will be rendered at the default place,
|
||||
or in [`{% component_css_dependencies %}`](#TODO).
|
||||
|
||||
And if you don't specify `{% component_dependencies %}` tags, it is the equivalent of:
|
||||
|
||||
```django
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MyPage</title>
|
||||
...
|
||||
{% component_css_dependencies %}
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
...
|
||||
</main>
|
||||
{% component_js_dependencies %}
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Setting up the middleware
|
||||
|
||||
[`ComponentDependencyMiddleware`](#TODO) is a Django [middleware](https://docs.djangoproject.com/en/5.1/topics/http/middleware/)
|
||||
designed to manage and inject CSS / JS dependencies of rendered components dynamically.
|
||||
It ensures that only the necessary stylesheets and scripts are loaded
|
||||
in your HTML responses, based on the components used in your Django templates.
|
||||
|
||||
To set it up, add the middleware to your [`MIDDLEWARE`](https://docs.djangoproject.com/en/5.1/ref/settings/#std-setting-MIDDLEWARE)
|
||||
in `settings.py`:
|
||||
|
||||
```python
|
||||
MIDDLEWARE = [
|
||||
# ... other middleware classes ...
|
||||
'django_components.middleware.ComponentDependencyMiddleware'
|
||||
# ... other middleware classes ...
|
||||
]
|
||||
```
|
||||
|
||||
### `render_dependencies` and rendering JS / CSS without the middleware
|
||||
|
||||
For most scenarios, using the [`ComponentDependencyMiddleware`](#TODO) middleware will be just fine.
|
||||
|
||||
However, this section is for you if you want to:
|
||||
|
||||
- Render HTML that will NOT be sent as a server response
|
||||
- Insert pre-rendered HTML into another component
|
||||
- Render HTML fragments (partials)
|
||||
|
||||
Every time there is an HTML string that has parts which were rendered using components,
|
||||
and any of those components has JS / CSS, then this HTML string MUST be processed with [`render_dependencies()`](#TODO).
|
||||
|
||||
It is actually [`render_dependencies()`](#TODO) that finds all used components in the HTML string,
|
||||
and inserts the component's JS and CSS into `{% component_dependencies %}` tags, or at the default locations.
|
||||
|
||||
#### Render JS / CSS without the middleware
|
||||
|
||||
The truth is that the [`ComponentDependencyMiddleware`](#TODO) middleware just calls [`render_dependencies()`](#TODO),
|
||||
passing in the HTML content. So if you render a template that contained [`{% component %}`](#TODO) tags,
|
||||
you MUST pass the result through [`render_dependencies()`](#TODO). And the middleware is just one of the options.
|
||||
|
||||
Here is how you can achieve the same, without the middleware, using [`render_dependencies()`](#TODO):
|
||||
|
||||
```python
|
||||
from django.template.base import Template
|
||||
from django.template.context import Context
|
||||
from django_component import render_dependencies
|
||||
|
||||
template = Template("""
|
||||
{% load component_tags %}
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MyPage</title>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
{% component "my_button" %}
|
||||
Click me!
|
||||
{% endcomponent %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
""")
|
||||
|
||||
rendered = template.render(Context())
|
||||
rendered = render_dependencies(rendered)
|
||||
```
|
||||
|
||||
Same applies if you render a template using Django's [`django.shortcuts.render`](https://docs.djangoproject.com/en/5.1/topics/http/shortcuts/#render):
|
||||
|
||||
```python
|
||||
from django.shortcuts import render
|
||||
|
||||
def my_view(request):
|
||||
rendered = render(request, "pages/home.html")
|
||||
rendered = render_dependencies(rendered)
|
||||
return rendered
|
||||
```
|
||||
|
||||
Alternatively, when you render HTML with [`Component.render()`](#TODO)
|
||||
or [`Component.render_to_response()`](#TODO),
|
||||
these, by default, call [`render_dependencies()`](#TODO) for you, so you don't have to:
|
||||
|
||||
```python
|
||||
from django_components import Component
|
||||
|
||||
class MyButton(Component):
|
||||
...
|
||||
|
||||
# No need to call `render_dependencies()`
|
||||
rendered = MyButton.render()
|
||||
```
|
||||
|
||||
#### Inserting pre-rendered HTML into another component
|
||||
|
||||
In previous section we've shown that [`render_dependencies()`](#TODO) does NOT need to be called
|
||||
when you render a component via [`Component.render()`](#TODO).
|
||||
|
||||
API of django_components makes it possible to compose components in a "React-like" way,
|
||||
where we pre-render a piece of HTML and then insert it into a larger structure.
|
||||
|
||||
To do this, you must add [`render_dependencies=False`](#TODO) to the nested components:
|
||||
|
||||
```python
|
||||
card_actions = CardActions.render(
|
||||
kwargs={"editable": editable},
|
||||
render_dependencies=False,
|
||||
)
|
||||
|
||||
card = Card.render(
|
||||
slots={"actions": card_actions},
|
||||
render_dependencies=False,
|
||||
)
|
||||
|
||||
page = MyPage.render(
|
||||
slots={"card": card},
|
||||
)
|
||||
```
|
||||
|
||||
Why is `render_dependencies=False` required?
|
||||
|
||||
This is a technical limitation of the current implementation.
|
||||
|
||||
As mentioned earlier, each time we call [`Component.render()`](#TODO),
|
||||
we also call [`render_dependencies()`](#TODO).
|
||||
|
||||
However, there is a problem here - When we call [`render_dependencies()`](#TODO)
|
||||
inside [`CardActions.render()`](#TODO),
|
||||
we extract and REMOVE the info on components' JS and CSS from the HTML. But the template
|
||||
of `CardActions` contains no `{% component_depedencies %}` tags, and nor `<head>` nor `<body>` HTML tags.
|
||||
So the component's JS and CSS will NOT be inserted, and will be lost.
|
||||
|
||||
To work around this, you must set [`render_dependencies=False`](#TODO) when rendering pieces of HTML
|
||||
with [`Component.render()`](#TODO) and inserting them into larger structures.
|
||||
|
||||
#### Summary
|
||||
|
||||
1. Every time you render HTML that contained components, you have to call [`render_dependencies()`](#TODO)
|
||||
on the rendered output.
|
||||
2. There are several ways to call [`render_dependencies()`](#TODO):
|
||||
- Using the [`ComponentDependencyMiddleware`](#TODO) middleware
|
||||
- Rendering the HTML by calling [`Component.render()`](#TODO) with `render_dependencies=True` (default)
|
||||
- Rendering the HTML by calling [`Component.render_to_response()`](#TODO) (always renders dependencies)
|
||||
- Directly passing rendered HTML to [`render_dependencies()`](#TODO)
|
||||
3. If you pre-render one component to pass it into another, the pre-rendered component must be rendered with
|
||||
[`render_dependencies=False`](#TODO).
|
Loading…
Add table
Add a link
Reference in a new issue