Components are reusable pieces of view template. They can contain native Django template syntax and can be used inside standard Django templates.
{{ slot }}
<c-my-component>
<p>Some content</p>
</c-my-component>
Some content
The {{ slot }} variable will contain all of the content provided between the opening and closing tag of the current component as defined in the parent.
Components are highly configurable. One way to control the content and behaviour of a component is through attributes.
<p>It's {{ temperature }}<sup>{{ unit }}</sup> and the condition is {{ condition }}.</p>
<c-weather temperature="23" unit="c" condition="windy"></c-weather>
Sometimes you'll want to pass a variable from the parent's context 'as is' for the child component to perform what it wants.
To pass data by reference, prepend the attribute with a " : ".
<c-weather :today="today"></c-weather>
<p>It's {{ today.temperature }}<sup>{{ today.unit }}</sup> and the condition is {{ today.condition }}.</p>
There are occasions when you will need to pass blocks of HTML or dynamic content. In these cases, we can reach to named slots .
Named slots are defined with the <c-slot name="...">...</c-slot> tag. The content is passed to the component like a standard template variable.
They allow you to define mixed markup, HTML and Django native tags and the rendered block will be provided as a template variable to the child component.
Adopting the nested HTML approach here keeps readability and integrates well with how editors already treat html-like tags and patterns.
After writing a couple of components like this, you will notice the fluidity of this approach.
<div class="flex ...">
<h2>{{ day }}:</h2> {{ icon }} {{ label }}
</div>
<c-weather-card day="Tuesday">
<c-slot name="icon">
<svg>...</svg>
</c-slot>
<c-slot name="label">
<h2 class="text-yellow-500">Sunny</h2>
</c-slot>
</c-weather-card>
It's sometimes useful to be able to reflect all attributes provided in the parent down to an HTML element in the component. This is particularly powerful when you are building form inputs.
<input type="text" class="border ..." {{ attrs }} />
<c-input name="first_name" placeholder="First name" />
<c-input name="last_name" placeholder="Last name" value="Smith" readonly />
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 />
Cotton allows you to include template variables and expressions inside attributes.
<c-weather temperature="{{ temperature|floatformat:0 }}"
unit="{{ unit|default:'c' }}"
condition="very {% get_intensity %}"
/>
Using the ':' to prefix an attribute tells Cotton we're passing a dynamic type down. We already know we can use this to send a variable, but you can also send basic python types, namely:
This benefits a number of use-cases, for example if you have a select component that you want to provide some value:
<select {{ attrs }}>
{% for option in options %}
<option value="{{ option }}">{{ option }}</option>
{% endfor %}
</select>
<c-select name="q1" :options="['yes', 'no', 'maybe']" />
This approach can also be utilised by in-component vars in the c-vars tag:
<c-vars :config="{'category': 'fruit', 'limit': 10}" />
<div>
Current category: {{ config.category }}
Current limit: {{ config.limit }}
...
</div>
This example shows we can use c-vars to provide a default to an optional dictionary variable `config` from the parent.
Vars are defined using a <c-vars /> tag at the top of a component file. It can contain numerous key="value" pairs. It can also be a single key without a default value. Specifying an attribute as a 'var' will remove the item from {{ attrs }}.
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" />
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 }}" />