Components are reusable pieces of view template. They can contain native Django template syntax and can be used inside standard Django templates.
The {{ slot }} variable captures all content passed between a component's opening and closing tags.
<div class="box">
{{ slot }}
</div>
Used in a parent template:
<c-box>
<p>Some <strong>content</strong></p>
</c-box>
We can further customize components with attribute, which allow you to pass specific data into the component as key-value pairs.
<p>It's {{ temperature }}<sup>{{ unit }}</sup> and the condition is {{ condition }}.</p>
<c-weather temperature="23" unit="{{ unit }}" condition="windy"></c-weather>
If we want to pass HTML instead of just a string (or another data type) into a component, we can pass them as named slots with the <c-slot name="...">...</c-slot> syntax.
So as with normal attributes, you reference the slot content like normal variables, as in:
<div class="flex ...">
<h2>{{ day }}:</h2> {{ icon }} {{ label }}
</div>
<c-weather-card day="Tuesday">
<c-slot name="icon">
<img src="sunny-icon.png" alt="Sunny">
</c-slot>
<c-slot name="label">
<div class="yellow">Sunny</div>
</c-slot>
</c-weather-card>
We saw how by default, all attributes that we pass to a component are treated as strings. If we want to pass HTML, we can use named slots. But what if we want to pass another data type like a template variable, boolean, integer, float, dictionary, list, dictionary?
Sometimes you'll want to pass a variable from the parent's context 'as is' for the child component to perform what it wants.
<!--
context = { 'today': Weather.objects.get(...) }
-->
<c-weather :today="today"></c-weather>
<p>It's {{ today.temperature }}<sup>{{ today.unit }}</sup> and the condition is {{ today.condition }}.</p>
<c-mycomp :prop="1" />
<!-- {% prop == 1 %} -->
<c-mycomp :prop="None" />
<!-- {% prop is None %} -->
<c-mycomp :items="['item1','item2','item3']" />
<!-- {% for item in items %} -->
<c-mycomp :mydict="{'name': 'Thom', 'products': [1,2]}" />
<!-- {{ mydict.name }}, {{ mydict.products }} -->
<c-mycomp :product="product" />
<!-- {{ product.title }} -->
<c-mycomp :slides="['{{ image1 }}', '{{ image2 }}']" />
<!-- {% for images in slides %} -->
<c-mycomp :is_highlighted="{% if important %}True{% endif %}" />
<!-- {% is_valid is False %} -->
A quick example of this is a select component that you want to fill with options:
<c-select :options="['no', 'yes', 'maybe']" />
<select>
{% for option in options %}
<option value="{{ option }}">{{ option }}</option>
{% endfor %}
</select>
Sometimes it's useful to be able to reflect all attributes provided in the parent on to an HTML element in the component. This is particularly powerful when you are building form inputs.
<c-input name="first_name" placeholder="First name" />
<c-input name="last_name" placeholder="Last name" value="Smith" readonly />
<input type="text" {{ attrs }} />
<!-- html output
<input type="text" name="first_name" placeholder="First name" />
<input type="text" name="last_name" placeholder="Last name" value="Smith" readonly />
-->
<c-vars /> gives components local state and default behavior, making them more self-sufficient and reducing the need for repetitive attribute declarations and maintaining UI state in the backend.
Vars are defined using a <c-vars /> tag at the top of a component file. It can either contain key="value" pairs or just standalone keys (keep reading to understand why).
You may design a component that will often have a default behaviour and rarely needs overriding. In this case, you may opt to give a default value to your component.
<c-vars type="success" />
<div class="{% if type == 'success' %} .. {% elif type == 'danger' %} .. {% endif %}">
{{ slot }}
</div>
<c-alert>All good!</c-alert>
<c-alert type="danger">Oh no!</c-alert>
Keys defined in <c-vars /> will not be included in {{ attrs }}. This is useful when some of the properties you pass down to a component are for configuration purposes only and not intended as attributes.
<c-vars label errors />
<label>{{ label }}</label>
<input type="text" class="border ..." {{ attrs }} />
{% if errors %}
{% for error in errors %}
{{ error }}
{% endfor %}
{% endif %}
<c-input-group label="First name" placeholder="First name" :errors="errors.first_name" />
<c-input-group label="Last name" placeholder="Last name" :errors="errors.last_name" />
Sometimes you just want to pass a simple boolean to a component. Cotton supports providing the attribute name without a value which will provide a boolean True to the component.
<input type="text" {{ attrs }} />
{% if required is True %}
<span class="text-red-500">*</span>
{% endif %}
<c-input name="telephone" required />
There can be times where components need to be included dynamically. For these cases we can reach for a special <c-component> tag with an is attribute:
{% for icon in icons %}
<c-component is="icons.{{ icon }}" />
{% endfor %}
The is attribute is similar to other attributes so we have a number of possibilities to define it:
<!-- as variable -->
<c-component :is="icon_name" />
<!-- as an expression -->
<c-component is="icon_{{ icon_name }}" />
Similar to Django's {% include %} tag you can add an "only" attribute which will prevent the component from inheriting the parent's context.