docs: fix links in README and "overview" section, add tutorial (#842)

This commit is contained in:
Juro Oravec 2024-12-16 14:15:02 +01:00 committed by GitHub
parent 6813c9d7aa
commit 789f3807aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 1213 additions and 216 deletions

View file

@ -0,0 +1,240 @@
---
title: Adding JS and CSS
weight: 2
---
Next we will add CSS and JavaScript to our template.
!!! info
In django-components, using JS and CSS is as simple as defining them on the Component class.
You don't have to insert the `<script>` and `<link>` tags into the HTML manually.
Behind the scenes, django-components keeps track of which components use which JS and CSS
files. Thus, when a component is rendered on the page, the page will contain only the JS
and CSS used by the components, and nothing more!
### 1. Update project structure
Start by creating empty `calendar.js` and `calendar.css` files:
```
sampleproject/
├── calendarapp/
├── components/
│ └── calendar/
│ ├── calendar.py
│ ├── calendar.js 🆕
│ ├── calendar.css 🆕
│ └── calendar.html
├── sampleproject/
├── manage.py
└── requirements.txt
```
### 2. Write CSS
Inside `calendar.css`, write:
```css title="[project root]/components/calendar/calendar.css"
.calendar {
width: 200px;
background: pink;
}
.calendar span {
font-weight: bold;
}
```
Be sure to prefix your rules with unique CSS class like `calendar`, so the CSS doesn't clash with other rules.
<!-- TODO: UPDATE AFTER SCOPED CSS ADDED -->
!!! note
Soon, django-components will automatically scope your CSS by default, so you won't have to worry
about CSS class clashes.
This CSS will be inserted into the page as an inlined `<style>` tag, at the position defined by
[`{% component_css_dependencies %}`](../../reference/template_tags.md#component_css_dependencies),
or at the end of the inside the `<head>` tag (See [JS and CSS output locations](../../advanced/rendering_js_css/#js-and-css-output-locations)).
So in your HTML, you may see something like this:
```html
<html>
<head>
...
<style>
.calendar {
width: 200px;
background: pink;
}
.calendar span {
font-weight: bold;
}
</style>
</head>
<body>
...
</body>
</html>
```
### 3. Write JS
Next we write a JavaScript file that specifies how to interact with this component.
You are free to use any javascript framework you want.
```js title="[project root]/components/calendar/calendar.js"
(function () {
document.querySelector(".calendar").onclick = () => {
alert("Clicked calendar!");
};
})();
```
A good way to make sure the JS of this component doesn't clash with other components is to define all JS code inside
an [anonymous self-invoking function](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) (`(() => { ... })()`).
This makes all variables defined only be defined inside this component and not affect other components.
<!-- TODO: UPDATE AFTER FUNCTIONS WRAPPED -->
!!! note
Soon, django-components will automatically wrap your JS in a self-invoking function by default
(except for JS defined with `<script type="module">`).
Similarly to CSS, JS will be inserted into the page as an inlined `<script>` tag, at the position defined by
[`{% component_js_dependencies %}`](../../reference/template_tags.md#component_js_dependencies),
or at the end of the inside the `<body>` tag (See [JS and CSS output locations](../../advanced/rendering_js_css/#js-and-css-output-locations)).
So in your HTML, you may see something like this:
```html
<html>
<head>
...
</head>
<body>
...
<script>
(function () {
document.querySelector(".calendar").onclick = () => {
alert("Clicked calendar!");
};
})();
</script>
</body>
</html>
```
#### Rules of JS execution
1. **JS is executed in the order in which the components are found in the HTML**
By default, the JS is inserted as a **synchronous** script (`<script> ... </script>`)
So if you define multiple components on the same page, their JS will be
executed in the order in which the components are found in the HTML.
So if we have a template like so:
```htmldjango
<html>
<head>
...
</head>
<body>
{% component "calendar" / %}
{% component "table" / %}
</body>
</html>
```
Then the JS file of the component `calendar` will be executed first, and the JS file
of component `table` will be executed second.
2. **JS will be executed only once, even if there is multiple instances of the same component**
In this case, the JS of `calendar` will STILL execute first (because it was found first),
and will STILL execute only once, even though it's present twice:
```htmldjango
<html>
<head>
...
</head>
<body>
{% component "calendar" / %}
{% component "table" / %}
{% component "calendar" / %}
</body>
</html>
```
### 4. Link JS and CSS to a component
Finally, we return to our Python component in `calendar.py` to tie this together.
To link JS and CSS defined in other files, use the `Media` nested class
([Learn more about using Media](../fundamentals/defining_js_css_html_files.md)).
```python title="[project root]/components/calendar/calendar.py"
from django_components import Component
class Calendar(Component):
template_name = "calendar.html"
class Media: # <--- new
js = "calendar.js"
css = "calendar.css"
def get_context_data(self):
return {
"date": "1970-01-01",
}
```
!!! info
The `Media` nested class is shaped based on [Django's Media class](https://docs.djangoproject.com/en/5.1/topics/forms/media/).
As such, django-components allows multiple formats to define the nested Media class:
```py
# Single files
class Media:
js = "calendar.js"
css = "calendar.css"
# Lists of files
class Media:
js = ["calendar.js", "calendar2.js"]
css = ["calendar.css", "calendar2.css"]
# Dictionary of media types for CSS
class Media:
js = ["calendar.js", "calendar2.js"]
css = {
"all": ["calendar.css", "calendar2.css"],
}
```
If you define a list of JS files, they will be executed one-by-one, left-to-right.
!!! note
Same as with the template file, the file paths for the JS and CSS files can be either:
1. Relative to the Python component file (as seen above),
2. Relative to any of the component directories as defined by
[`COMPONENTS.dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.dirs)
and/or [`COMPONENTS.app_dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.app_dirs)
(e.g. `[your apps]/components` dir and `[project root]/components`)
And that's it! If you were to embed this component in an HTML, django-components will
automatically embed the associated JS and CSS.
Now that we have a fully-defined component, [next let's use it in a Django template ➡️](./components_in_templates.md).

View file

@ -0,0 +1,295 @@
---
title: Adding slots
weight: 5
---
Our calendar component's looking great! But we just got a new assignment from
our colleague - The calendar date needs to be shown on 3 different pages:
1. On one page, it needs to be shown as is
2. On the second, the date needs to be **bold**
3. On the third, the date needs to be in *italics*
As a reminder, this is what the component's template looks like:
```htmldjango
<div class="calendar">
Today's date is <span>{{ date }}</span>
</div>
```
There's many ways we could approach this:
- Expose the date in a slot
- Style `.calendar > span` differently on different pages
- Pass a variable to the component that decides how the date is rendered
- Create a new component
First two options are more flexible, because the custom styling is not baked into a component's
implementation. And for the sake of demonstration, we'll solve this challenge with slots.
### 1. What are slots
Components support something called [Slots](../fundamentals/slots.md).
When a component is used inside another template, slots allow the parent template
to override specific parts of the child component by passing in different content.
This mechanism makes components more reusable and composable.
This behavior is similar to [slots in Vue](https://vuejs.org/guide/components/slots.html).
In the example below we introduce two tags that work hand in hand to make this work. These are...
- `{% slot <name> %}`/`{% endslot %}`: Declares a new slot in the component template.
- `{% fill <name> %}`/`{% endfill %}`: (Used inside a [`{% component %}`](../../reference/template_tags.md#component)
tag pair.) Fills a declared slot with the specified content.
### 2. Add a slot tag
Let's update our calendar component to support more customization. We'll add
[`{% slot %}`](../../reference/template_tags.md#slot) tag to the template:
```htmldjango
<div class="calendar">
Today's date is
{% slot "date" default %} {# <--- new #}
<span>{{ date }}</span>
{% endslot %}
</div>
```
Notice that:
1. We named the slot `date` - so we can fill this slot by using `{% fill "date" %}`
2. We also made it the [default slot](../fundamentals/slots.md#default-slot).
3. We placed our original implementation inside the [`{% slot %}`](../../reference/template_tags.md#slot)
tag - this is what will be rendered when the slot is NOT overriden.
### 3. Add fill tag
Now we can use [`{% fill %}`](../../reference/template_tags.md#fill) tags inside the
[`{% component %}`](../../reference/template_tags.md#component) tags to override the `date` slot
to generate the bold and italics variants:
```htmldjango
{# Default #}
{% component "calendar" date="2024-12-13" / %}
{# Bold #}
{% component "calendar" date="2024-12-13" %}
<b> 2024-12-13 </b>
{% endcomponent %}
{# Italics #}
{% component "calendar" date="2024-12-13" %}
<i> 2024-12-13 </i>
{% endcomponent %}
```
Which will render as:
```html
<!-- Default -->
<div class="calendar">
Today's date is <span>2024-12-13</span>
</div>
<!-- Bold -->
<div class="calendar">
Today's date is <b>2024-12-13</b>
</div>
<!-- Italics -->
<div class="calendar">
Today's date is <i>2024-12-13</i>
</div>
```
!!! info
Since we used the `default` flag on `{% slot "date" %}` inside our calendar component,
we can target the `date` component in multiple ways:
1. Explicitly by it's name
```htmldjango
{% component "calendar" date="2024-12-13" %}
{% fill "date" %}
<i> 2024-12-13 </i>
{% endfill %}
{% endcomponent %}
```
2. Implicitly as the [default slot](../fundamentals/slots.md#default-slot) (Omitting the
[`{% fill %}`](../../reference/template_tags.md#fill) tag)
```htmldjango
{% component "calendar" date="2024-12-13" %}
<i> 2024-12-13 </i>
{% endcomponent %}
```
3. Explicitly as the [default slot](../fundamentals/slots.md#default-slot) (Setting fill name to `default`)
```htmldjango
{% component "calendar" date="2024-12-13" %}
{% fill "default" %}
<i> 2024-12-13 </i>
{% endfill %}
{% endcomponent %}
```
### 5. Wait, there's a bug
There is a mistake in our code! `2024-12-13` is Friday, so that's fine. But if we updated
the to `2024-12-14`, which is Saturday, our template from previous step would render this:
```html
<!-- Default -->
<div class="calendar">
Today's date is <span>2024-12-16</span>
</div>
<!-- Bold -->
<div class="calendar">
Today's date is <b>2024-12-14</b>
</div>
<!-- Italics -->
<div class="calendar">
Today's date is <i>2024-12-14</i>
</div>
```
The first instance rendered `2024-12-16`, while the rest rendered `2024-12-14`!
Why? Remember that in the [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
method of our Calendar component, we pre-process the date. If the date falls on Saturday or Sunday, we shift it to next Monday:
```python title="[project root]/components/calendar/calendar.py"
from datetime import date
from django_components import Component, register
# If date is Sat or Sun, shift it to next Mon, so the date is always workweek.
def to_workweek_date(d: date):
...
@register("calendar")
class Calendar(Component):
template_name = "calendar.html"
...
def get_context_data(self, date: date, extra_class: str | None = None):
workweek_date = to_workweek_date(date)
return {
"date": workweek_date,
"extra_class": extra_class,
}
```
And the issue is that in our template, we used the `date` value that we used *as input*,
which is NOT the same as the `date` variable used inside Calendar's template.
### 5. Adding data to slots
We want to use the same `date` variable that's used inside Calendar's template.
Luckily, django-components allows passing data to the slot, also known as [Scoped slots](../fundamentals/slots.md#scoped-slots).
This consists of two steps:
1. Pass the `date` variable to the [`{% slot %}`](../../reference/template_tags.md#slot) tag
2. Access the `date` variable in the [`{% fill %}`](../../reference/template_tags.md#fill)
tag by using the special `data` kwarg
Let's update the Calendar's template:
```htmldjango
<div class="calendar">
Today's date is
{% slot "date" default date=date %} {# <--- changed #}
<span>{{ date }}</span>
{% endslot %}
</div>
```
!!! info
The [`{% slot %}`](../../reference/template_tags.md#slot) tag has one special kwarg, `name`. When you write
```htmldjango
{% slot "date" / %}
```
It's the same as:
```htmldjango
{% slot name="date" / %}
```
Other than the `name` kwarg, you can pass any extra kwargs to the [`{% slot %}`](../../reference/template_tags.md#slot) tag,
and these will be exposed as the slot's data.
```htmldjango
{% slot name="date" kwarg1=123 kwarg2="text" kwarg3=my_var / %}
```
### 6. Accessing slot data in fills
Now, on the [`{% fill %}`](../../reference/template_tags.md#fill) tags, we can use the `data` kwarg to specify the variable under which
the slot data will be available.
The variable from the `data` kwarg contains all the extra kwargs passed to the [`{% slot %}`](../../reference/template_tags.md#slot) tag.
So if we set `data="slot_data"`, then we can access the date variable under `slot_data.date`:
```htmldjango
{# Default #}
{% component "calendar" date="2024-12-13" / %}
{# Bold #}
{% component "calendar" date="2024-12-13" %}
{% fill "date" data="slot_data" %}
<b> {{ slot_data.date }} </b>
{% endfill %}
{% endcomponent %}
{# Italics #}
{% component "calendar" date="2024-12-13" %}
{% fill "date" data="slot_data" %}
<i> {{ slot_data.date }} </i>
{% endfill %}
{% endcomponent %}
```
By using the `date` variable from the slot, we'll render the correct date
each time:
```html
<!-- Default -->
<div class="calendar">
Today's date is <span>2024-12-16</span>
</div>
<!-- Bold -->
<div class="calendar">
Today's date is <b>2024-12-16</b>
</div>
<!-- Italics -->
<div class="calendar">
Today's date is <i>2024-12-16</i>
</div>
```
!!! info
**When to use slots vs variables?**
Generally, slots are more flexible - you can access the slot data, even the original slot content.
Thus, slots behave more like functions that render content based on their context.
On the other hand, variables are static - the variable you pass to a component is what will be used.
Moreover, slots are treated as part of the template - for example the CSS scoping (work in progress)
is applied to the slot content too.

View file

@ -0,0 +1,196 @@
---
title: Components in templates
weight: 3
---
By the end of this section, we want to be able to use our components in Django templates like so:
```htmldjango
{% load component_tags %}
<!DOCTYPE html>
<html>
<head>
<title>My example calendar</title>
</head>
<body>
{% component "calendar" / %}
</body>
<html>
```
### 1. Register component
First, however, we need to register our component class with [`ComponentRegistry`](../../../reference/api#django_components.ComponentRegistry).
To register a component with a [`ComponentRegistry`](../../../reference/api#django_components.ComponentRegistry),
we will use the [`@register`](../../../reference/api#django_components.register)
decorator, and give it a name under which the component will be accessible from within the template:
```python title="[project root]/components/calendar/calendar.py"
from django_components import Component, register # <--- new
@register("calendar") # <--- new
class Calendar(Component):
template_name = "calendar.html"
class Media:
js = "calendar.js"
css = "calendar.css"
def get_context_data(self):
return {
"date": "1970-01-01",
}
```
This will register the component to the default registry. Default registry is loaded into the template
by calling `{% load component_tags %}` inside the template.
!!! info
Why do we have to register components?
We want to use our component as a template tag (`{% ... %}`) in Django template.
In Django, template tags are managed by the `Library` instances. Whenever you include `{% load xxx %}`
in your template, you are loading a `Library` instance into your template.
[`ComponentRegistry`](../../../reference/api#django_components.ComponentRegistry) acts like a router
and connects the registered components with the associated `Library`.
That way, when you include `{% load component_tags %}` in your template, you are able to "call" components
like `{% component "calendar" / %}`.
`ComponentRegistries` also make it possible to group and share components as standalone packages.
[Learn more here](../advanced/authoring_component_libraries.md).
!!! note
You can create custom [`ComponentRegistry`](../../../reference/api#django_components.ComponentRegistry)
instances, which will use different `Library` instances.
In that case you will have to load different libraries depending on which components you want to use:
Example 1 - Using component defined in the default registry
```htmldjango
{% load component_tags %}
<div>
{% component "calendar" / %}
</div>
```
Example 2 - Using component defined in a custom registry
```htmldjango
{% load my_custom_tags %}
<div>
{% my_component "table" / %}
</div>
```
Note that, because the tag name `component` is use by the default ComponentRegistry,
the custom registry was configured to use the tag `my_component` instead. [Read more here](../advanced/component_registry.md)
### 2. Load and use the component in template
The component is now registered under the name `calendar`. All that remains to do is to load
and render the component inside a template:
```htmldjango
{% load component_tags %} {# Load the default registry #}
<!DOCTYPE html>
<html>
<head>
<title>My example calendar</title>
</head>
<body>
{% component "calendar" / %} {# Render the component #}
</body>
<html>
```
!!! info
Component tags should end with `/` if they do not contain any [Slot fills](../fundamentals/slots.md).
But you can also use `{% endcomponent %}` instead:
```htmldjango
{% component "calendar" %}{% endcomponent %}
```
We defined the Calendar's template as
```htmldjango
<div class="calendar">
Today's date is <span>{{ date }}</span>
</div>
```
and the variable `date` as `"1970-01-01"`.
Thus, the final output will look something like this:
```htmldjango
<!DOCTYPE html>
<html>
<head>
<title>My example calendar</title>
<style>
.calendar {
width: 200px;
background: pink;
}
.calendar span {
font-weight: bold;
}
</style>
</head>
<body>
<div class="calendar">
Today's date is <span>1970-01-01</span>
</div>
<script>
(function () {
document.querySelector(".calendar").onclick = () => {
alert("Clicked calendar!");
};
})();
</script>
</body>
<html>
```
This makes it possible to organize your front-end around reusable components, instead of relying on template tags
and keeping your CSS and Javascript in the static directory.
!!! info
Remember that you can use
[`{% component_js_dependencies %}`](../../reference/template_tags.md#component_js_dependencies)
and [`{% component_css_dependencies %}`](../../reference/template_tags.md#component_css_dependencies)
to change where the `<script>` and `<style>` tags will be rendered (See [JS and CSS output locations](../../advanced/rendering_js_css/#js-and-css-output-locations)).
!!! info
How does django-components pick up registered components?
Notice that it was enough to add [`@register`](../../../reference/api#django_components.register) to the component.
We didn't need to import the component file anywhere to execute it.
This is because django-components automatically imports all Python files found in the component directories
during an event called [Autodiscovery](../fundamentals/autodiscovery.md).
So with Autodiscovery, it's the same as if you manually imported the component files on the `ready()` hook:
```python
class MyApp(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "myapp"
def ready(self):
import myapp.components.calendar
import myapp.components.table
...
```
You can now render the components! But our component will render the same content now matter where
and how many times we use it. [Let's parametrise some of its state, so that our Calendar component
is configurable from within the template ➡️](./parametrising_components.md)

View file

@ -0,0 +1,225 @@
---
title: Parametrising components
weight: 4
---
So far, our Calendar component will always render the date `1970-01-01`. Let's make it more useful and flexible
by being able to pass in custom date.
What we want is to be able to use the Calendar component within the template like so:
```htmldjango
{% component "calendar" date="2024-12-13" extra_class="text-red" / %}
```
### 1. Understading component inputs
In section [Create your first component](./your_first_component.md), we defined
the [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data) method
that defines what variables will be available within the template:
```python title="[project root]/components/calendar/calendar.py"
from django_components import Component, register
@register("calendar")
class Calendar(Component):
template_name = "calendar.html"
...
def get_context_data(self):
return {
"date": "1970-01-01",
}
```
What we didn't say is that [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
actually receives the args and kwargs that were passed to a component.
So if we call a component with a `date` and `extra_class` keywords:
```htmldjango
{% component "calendar" date="2024-12-13" extra_class="text-red" / %}
```
This is the same as calling:
```py
Calendar.get_context_data(date="2024-12-13", extra_class="text-red")
```
And same applies to positional arguments, or mixing args and kwargs, where:
```htmldjango
{% component "calendar" "2024-12-13" extra_class="text-red" / %}
```
is same as
```py
Calendar.get_context_data("2024-12-13", extra_class="text-red")
```
### 2. Define inputs for `get_context_data`
Let's put this to test. We want to pass `date` and `extra_class` kwargs to the component.
And so, we can write the [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
method such that it expects those parameters:
```python title="[project root]/components/calendar/calendar.py"
from datetime import date
from django_components import Component, register
@register("calendar")
class Calendar(Component):
template_name = "calendar.html"
...
def get_context_data(self, date: date, extra_class: str | None = None):
return {
"date": date,
"extra_class": extra_class,
}
```
!!! info
Since [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
is just a regular Python function, type hints annotations work the same way as anywhere else.
!!! warning
Since [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
is just a regular Python function, it will raise TypeError if it receives incorrect parameters.
Since `extra_class` is optional in the function signature, it's optional also in the template.
So both following calls are valid:
```htmldjango
{% component "calendar" "2024-12-13" / %}
{% component "calendar" "2024-12-13" extra_class="text-red" / %}
```
However, `date` is required. Thus we MUST provide it. Same with regular Python functions,
`date` can be set either as positional or keyword argument. But either way it MUST be set:
```htmldjango
{% component "calendar" "2024-12-13" / %}
{% component "calendar" extra_class="text-red" date="2024-12-13" / %}
{% component "calendar" extra_class="text-red" / %}
```
### 3. Process inputs in `get_context_data`
The [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
method is powerful, because it allows us to decouple
component inputs from the template variables. In other words, we can pre-process
the component inputs, and massage them into a shape that's most appropriate for
what the template needs. And it also allows us to pass in static data into the template.
Imagine our component receives data from the database that looks like below
([taken from Django](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#regroup)).
```py
cities = [
{"name": "Mumbai", "population": "19,000,000", "country": "India"},
{"name": "Calcutta", "population": "15,000,000", "country": "India"},
{"name": "New York", "population": "20,000,000", "country": "USA"},
{"name": "Chicago", "population": "7,000,000", "country": "USA"},
{"name": "Tokyo", "population": "33,000,000", "country": "Japan"},
]
```
We need to group the list items by size into following buckets by population:
- 0-10,000,000
- 10,000,001-20,000,000
- 20,000,001-30,000,000
- +30,000,001
So we want to end up with following data:
```py
cities_by_pop = [
{
"name": "0-10,000,000",
"items": [
{"name": "Chicago", "population": "7,000,000", "country": "USA"},
]
},
{
"name": "10,000,001-20,000,000",
"items": [
{"name": "Calcutta", "population": "15,000,000", "country": "India"},
{"name": "Mumbai", "population": "19,000,000", "country": "India"},
{"name": "New York", "population": "20,000,000", "country": "USA"},
]
},
{
"name": "30,000,001-40,000,000",
"items": [
{"name": "Tokyo", "population": "33,000,000", "country": "Japan"},
]
},
]
```
Without the `get_context_data()` method, we'd have to either:
1. Pre-process the data in Python before passing it to the components.
2. Define a Django filter or template tag to take the data and process it on the spot.
Instead, with `get_context_data()`, we can keep this transformation private to this component,
and keep the rest of the codebase clean.
```py
def group_by_pop(data):
...
@register("population_table")
class PopulationTable(Component):
template_name = "population_table.html"
def get_context_data(self, data):
return {
"data": group_by_pop(data),
}
```
Similarly we can make use of `get_context_data()` to pre-process the date that was given to the component:
```python title="[project root]/components/calendar/calendar.py"
from datetime import date
from django_components import Component, register
# If date is Sat or Sun, shift it to next Mon, so the date is always workweek.
def to_workweek_date(d: date):
...
@register("calendar")
class Calendar(Component):
template_name = "calendar.html"
...
def get_context_data(self, date: date, extra_class: str | None = None):
workweek_date = to_workweek_date(date) # <--- new
return {
"date": workweek_date, # <--- changed
"extra_class": extra_class,
}
```
### 4. Pass inputs to components
Once we're happy with `Calendar.get_contex_data()`, we can update our templates to use
the parametrized version of the component:
```htmldjango
<div>
{% component "calendar" date="2024-12-13" / %}
{% component "calendar" date="1970-01-01" / %}
</div>
```
Next, you will learn [how to use slots give your components even more flexibility ➡️](./adding_slots.md)

View file

@ -0,0 +1,157 @@
---
title: Create your first component
weight: 1
---
A component in django-components can be as simple as a Django template and Python code to declare the component:
```py
from django_components import Component
class Calendar(Component):
template = """
<div class="calendar">
Today's date is <span>{{ date }}</span>
</div>
"""
```
Or a combination of Django template, Python, CSS, and Javascript:
```py
from django_components import Component
class Calendar(Component):
template = """
<div class="calendar">
Today's date is <span>{{ date }}</span>
</div>
"""
css = """
.calendar {
width: 200px;
background: pink;
}
"""
js = """
document.querySelector(".calendar").onclick = function () {
alert("Clicked calendar!");
};
"""
```
!!! note
With django-components, you can "inline" the HTML, JS and CSS code into the Python class,
as seen above.
You can set up [syntax highlighting](../../guides/setup/syntax_highlight.md),
but autocompletion / intellisense does not yet work.
So, in the example below we define the Django template in a separate file, `calendar.html`,
to allow our IDEs to interpret the file as HTML / Django file.
We'll start by creating a component that defines only a Django template:
### 1. Create project structure
Start by creating empty `calendar.py` and `calendar.html` files:
```
sampleproject/
├── calendarapp/
├── components/ 🆕
│ └── calendar/ 🆕
│ ├── calendar.py 🆕
│ └── calendar.html 🆕
├── sampleproject/
├── manage.py
└── requirements.txt
```
### 2. Write Django template
Inside `calendar.html`, write:
```htmldjango title="[project root]/components/calendar/calendar.html"
<div class="calendar">
Today's date is <span>{{ date }}</span>
</div>
```
In this example we've defined one template variable `date`. You can use any and as many variables as you like. These variables will be
defined in the Python file in [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
when creating an instance of this component.
!!! note
The template will be rendered with whatever template backend you've specified in your Django settings file.
Currently django-components supports only the default `"django.template.backends.django.DjangoTemplates"` template backend!
### 3. Create new Component in Python
In `calendar.py`, create a subclass of [Component](../../../reference/api#django_components.Component)
to create a new component.
To link the HTML template with our component, set [`template_name`](../../../reference/api#django_components.Component.template_name)
to the name of the HTML file.
```python title="[project root]/components/calendar/calendar.py"
from django_components import Component
class Calendar(Component):
template_name = "calendar.html"
```
!!! note
The path to the template file can be either:
1. Relative to the component's python file (as seen above),
2. Relative to any of the component directories as defined by
[`COMPONENTS.dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.dirs)
and/or [`COMPONENTS.app_dirs`](../../reference/settings.md#django_components.app_settings.ComponentsSettings.app_dirs)
(e.g. `[your apps]/components` dir and `[project root]/components`)
### 4. Define the template variables
In `calendar.html`, we've used the variable `date`. So we need to define it for the template to work.
This is done using [`Component.get_context_data()`](../../../reference/api#django_components.Component.get_context_data).
It's a function that returns a dictionary. The entries in this dictionary
will become available within the template as variables, e.g. as `{{ date }}`.
```python title="[project root]/components/calendar/calendar.py"
from django_components import Component
class Calendar(Component):
template_name = "calendar.html"
def get_context_data(self):
return {
"date": "1970-01-01",
}
```
Now, when we render the component with [`Component.render()`](../../../reference/api#django_components.Component.render)
method:
```py
Calendar.render()
```
It will output
```html
<div class="calendar">
Today's date is <span>1970-01-01</span>
</div>
```
And voilá!! We've created our first component.
Next, [let's add JS and CSS to this component ➡️](./adding_js_and_css.md).