Code Monkey home page Code Monkey logo

django-cotton's Introduction

Django Cotton Logo

Django Cotton

Build Status License

Bringing component-based design to Django templates.

Contents

Why?
Your First component
Attributes
Named Slots
Pass Template Variables
Template expressions in attributes
Boolean attributes
Passing Python data types
Increase Re-usability with {{ attrs }}
In-component Variables with <c-vars>
HTMX Example
Usage Basics
Limitations in Django that Cotton overcomes
Caching
Changelog
Comparison with other packages

Why Cotton?

Cotton aims to overcome certain limitations that exist in the django template system that hold us back when we want to apply modern practises to compose UIs in a modular and reusable way.

Key Features

  • Modern UI Composition: Efficiently compose and reuse UI components.
  • Interoperable with Django: Cotton enhances django's existing template system.
  • HTML-like Syntax: Better code editor support and productivity as component tags are similar to html tags.
  • Minimal Overhead: Compiles to native Django components with dynamic caching.
  • Encapsulates UI: Keep layout, design and interaction in one file (especially when paired with Tailwind and Alpine.js)
  • Compliments HTMX: Create smart components, reducing repetition and enhancing maintainability.

Walkthrough

Your first component

<!-- cotton/button.html -->
<a href="/" class="...">{{ slot }}</a>
<!-- in view -->
<c-button>Contact</c-button>
<!-- html output -->
<a href="/" class="...">Contact</a>

Everything provided between the opening and closing tag is provided to the component as {{ slot }}. It can contain any content, HTML or Django template expression.

Add attributes

<!-- cotton/button.html -->
<a href="{{ url }}" class="...">
    {{ slot }}
</a>
<!-- in view -->
<c-button url="/contact">Contact</c-button>
<!-- html output -->
<a href="/contact" class="...">
    Contact
</a>

Named slots

Named slots are a powerful concept. They allow us to provide HTML to appear in one or more areas in the component. Here we allow the button to optionally display an svg icon:

<!-- cotton/button.html -->
<a href="{{ url }}" class="...">
    {{ slot }}
  
    {% if icon %} 
        {{ icon }} 
    {% endif %}
</a>
<!-- in view -->
<c-button url="/contact">
    Contact
    <c-slot name="icon">
        <svg>...</svg>
    </c-slot>
</c-button>

Named slots can also contain any django native template logic:

<!-- in view -->
<c-button url="/contact">
    <c-slot name="icon">
      {% if mode == 'edit' %}
          <svg id="pencil">...</svg>
      {% else %}
          <svg id="disk">...</svg>
      {% endif %}
    </c-slot>
</c-button>

Pass template variable as an attribute

To pass a template variable you prepend the attribute name with a colon :. Consider a bio card component:

<!-- in view -->
<c-bio-card :user="user" />

That has a component definition like:

<!-- cotton/bio_card.html -->
<div class="...">
  <img src="{{ user.avatar }}" alt="...">
  {{ user.username }} {{ user.country_code }}
</div>

Template expressions inside attributes

You can use template expression statements inside attributes.

<c-weather icon="fa-{{ icon }}"
           unit="{{ unit|default:'c' }}"
           condition="very {% get_intensity %}"
/>

Boolean attributes

Boolean attributes reduce boilerplate when we just want to indicate a certain attribute should be True or not.

<!-- in view -->
<c-button url="/contact" external>Contact</c-button>

By passing just the attribute name without a value, it will automatically be provided to the component as True

<!-- cotton/button.html -->
<a href="{{ url }}" {% if external %} target="_blank" {% endif %} class="...">
    {{ slot }}
</a>

Passing Python data types

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:

  • Integers and Floats
  • None, True and False
  • Lists
  • Dictionaries

This benefits a number of use-cases, for example if you have a select component that you want to provide the possible options from the parent:

<!-- cotton/select.html -->
<select {{ attrs }}>
    {% for option in options %}
        <option value="{{ option }}">{{ option }}</option>
    {% endfor %}
</select>
<c-select name="q1" :options="['yes', 'no', 'maybe']" />
<!-- source code output -->
<select name="q1">
    <option value="yes">yes</option>
    <option value="no">no</option>
    <option value="maybe">maybe</option>
</select>

Increase Re-usability with {{ attrs }}

{{ attrs }} is a special variable that contains all the attributes passed to the component in an key="value" format. This is useful when you want to pass all attributes to a child element without having to explicitly define them in the component template. For example, you have inputs that can have any number of attributes defined:

<!-- cotton/input.html -->
<input type="text" class="..." {{ attrs }} />
<!-- example usage -->
<c-input placeholder="Enter your name" />
<c-input name="country" id="country" value="Japan" />
<c-input class="highlighted" required />
<!-- html output -->
<input type="text" class="..." placeholder="Enter your name" />
<input type="text" class="..." name="country" id="country" value="Japan" />
<input type="text" class="..." class="highlighted" required />

In-component Variables with <c-vars>

Django templates adhere quite strictly to the MVC model and does not permit a lot of data manipulation in views. Fair enough, but what if we want to handle data for the purpose of UI state only? Having presentation related variables defined in the back is overkill and can quickly lead to higher maintenance cost and loses encapsulation of the component. Cotton allows you define in-component variables for the following reasons:

1. Using <c-vars> for default attributes

In this example we have a button component with a default "theme" but it can be overridden.

<!-- cotton/button.html -->
<c-vars theme="bg-purple-500" />

<a href="..." class="{{ theme }}">
    {{ slot }}
</a>
<!-- in view -->
<c-button>I'm a purple button</c-button>
<!-- html output -->
<a href="..." class="bg-purple-500">
    I'm a purple button
</a>

Now we have a default theme for our button, but it is overridable:

<!-- in view -->
<c-button theme="bg-green-500">But I'm green</c-button>
<!-- html output -->
<a href="..." class="bg-green-500">
    But I'm green
</a>

2. Using <c-vars> to govern {{ attrs }}

Using {{ attrs }} to pass all attributes from parent scope onto an element in the component, you'll sometimes want to provide additional properties to the component which are not intended to be an attributes. In this case you can declare them in <c-vars /> and it will prevent it from being in {{ attrs }}

Take this example where we want to provide any number of attributes to an input but also an icon setting which is not intened to be an attribute on <input>:

<!-- in view -->
<c-input type="password" id="password" icon="padlock" />
<!-- cotton/input.html -->
<c-vars icon />

<img src="icons/{{ icon }}.png" />

<input {{ attrs }} />

Input will have all attributes provided apart from the icon:

<input type="password" id="password" />

An example with HTMX

Cotton helps carve out re-usable components, here we show how to make a re-usable form, reducing code repetition and improving maintainability:

<!-- cotton/form.html -->
<div id="result" class="..."></div>

<form {{ attrs }} hx-target="#result" hx-swap="outerHTML">
    {{ slot }}
    <button type="submit">Submit</button>
</form>
<!-- in view -->
<c-form hx-post="/contact">
    <input type="text" name="name" placeholder="Name" />
    <input type="text" name="email" placeholder="Email" />
    <input type="checkbox" name="signup" />
</c-form>

<c-form hx-post="/buy">
    <input type="text" name="type" />
    <input type="text" name="quantity" />
</c-form>

Usage Basics

  • Component Placement: Components should be placed in the templates/cotton folder.
  • Naming Conventions:
    • Component filenames use snake_case: my_component.html
    • Components are called using kebab-case: <c-my-component />

For full docs and demos, checkout django-cotton.com

Limitations in Django that Cotton overcomes

Whilst you can build frontends with Django’s native tags, there are a few things that hold us back when we want to apply modern practices:

{% block %} and {% extends %}

This system strongly couples child and parent templates making it hard to create a truly re-usable component that can be used in places without it having a related base template.

What about {% include %} ?

Modern libraries allow components to be highly configurable, whether it’s by attributes, passing variables, passing HTML with default and named slots. {% include %} tags, whilst they have the ability to pass simple variables and text, they will not allow you to easily send HTML blocks with template expressions let alone other niceties such as boolean attributes, named slots etc.

What's with {% with %}?

Whilst {% with %} tags allow us to provide variables and strings it quickly busies up your code and has the same limitations about passing more complex types.

Custom {% templatetags %}

Cotton does essentially compile down to templatetags but there is some extra work it performs above it to help with scoping and auto-managing keys which will be difficult to manage manually in complex nested structures.

[Source article]

Native Django template tags vs Cotton

In addition, Cotton enables you to navigate around some of the limitations with Django's native tags and template language:

HTML in attributes

Django native:

{% my_component header="<h1>Header</h1>" %}

Cotton:

<c-my-component>
    <c-slot name="header">
        <h1>Header</h1>
    </c-slot>
</c-my-component>

Template expressions in attributes

Django native:

{% my_component model="todos.{{ index }}.name" extra="{% get_extra %}" %}

Cotton:

<c-my-component model="todos.{{ index }}.name" extra="{% get_extra %} />

Pass simple python types

Django native:

{% my_component default_options="['yes', 'no', 'maybe']" %}
{% my_component config="{'open': True}" %}

Cotton:

<c-my-component :default_options="['yes', 'no', 'maybe']" />
<c-my-component :config="{'open': True}" />

Multi-line definitions

Django native:

{% my_component
    arg=1 %}

Cotton:

<c-my-component
    class="blue"
    x-data="{
        something: 1
    }" />

Caching

Cotton components are cached whilst in production (DEBUG = False). The cache's TTL is for the duration of your app's lifetime. So on deployment, when the app is normally restarted, caches are cleared. During development, changes are detected on every component render. This feature is a work in progress and some refinement is planned.

Changelog

Version Date Title and Description
v0.9.18 2024-07-14 Fix: Cotton loader permits duplicate attributes in html tags in the compiled django template.
v0.9.17 2024-07-13 Fix: Allow trailing/leading quotes in attributes
v0.9.16 2024-07-10 Cotton Component Caching
Cotton components are now cached whilst in production / DEBUG = False. LRU cache type with a record count of 1024, cleared on app restart.
v0.9.15 2024-07-06           Hyphen to Underscore Conversion
Converts variable names with hyphens to underscores for attribute access. i.e. <c-component x-data="{}" /> -> {{ x_data }}
v0.9.14 2024-07-05 c-vars Optimization
Optimizes c-vars processing for performance, yielding a 15% speed improvement in component rendering.
v0.9.13 2024-07-05 Multi-line Attribute Support
Enables multi-line values in attributes, allowing more support for js-expression style attributes like in alpine.js
v0.9.12 2024-07-03 Dropped ".cotton.html" Requirement
Cotton no longer requires the .cotton.html suffix on component or view templates. A simple .html will do.
v0.9.11 2024-06-24 Attribute Ordering Fix
Attribute ordering was not being kept during compilation which was breaking situations when using template expressions inside tags.
v0.9.10 2024-06-22 Template Expression Attributes
Ensures that the new template expression attributes are also provided in {{ attrs }} alongside all normal attributes.
v0.9.9 2024-06-22 Native Tags in Attributes
Cotton now allows you to include template variables inside attributes. Added expression attributes to {{ attrs }}.
v0.9.7 2024-06-21 Dynamic Type Attributes
Using the : to prefix an attribute tells Cotton we're passing a dynamic type down. You can also send basic Python types.
v0.9.6 2024-06-17 Rename c-props to c-vars
Rename c props, all <c-props /> are now <c-vars />.
v0.9.4 2024-06-11 Boolean Attributes
Support for Boolean attributes added with docs update.
v0.9.1 2024-06-08 Open Source Release
Open source release.

Comparison with other packages

Note: For mistakes or if I have missed something from other packages - please create an issue!

Feature Cotton django-components Slippers Django Template Partials
Intro UI-focused, expressive syntax Holistic solution with backend logic Enhances DTL for reusable components Define and reuse inline HTML partials
Definition of ‘component’ An HTML template A backend class with template An HTML template Inline specified partial
Syntax Style
HTML-like
HTML-like Django Template Tags Django Template Tags with custom tags Django Template Tags
Create component in one step? Yes
(place in folder)
No
(create additional class file)
No
(need to register in YAML file or with function)
Yes
(declare inline or load via include tag)
Slots (default)
Pass HTML content between tags
Yes Yes Yes No
Named Slots
Designate a slot in the component template
Yes Yes Yes, using ‘fragment’ No
Scoped Slots
Reference component context in parent template
No Yes No No
Dynamic Attributes
Pass string literals of basic Python types
Yes No No No
Boolean Attributes
Pass valueless attributes as True
Yes Yes No No
Declare Variables in Component View
Set defaults for UI states
Yes No
(Use class properties)
Yes No
Implicit Attribute Passing
Pass all defined attributes to an element
Yes No Yes No
Django Template Expressions in Attribute Values
Use Django expressions in attribute values
Yes No No No
Auto-Registering Components
Start using components without manual registration
Yes No
(Create class with decorator)
No
(Register in YAML file or with helper function)
Yes
Attribute Merging
Replace existing attributes with component attributes
Yes Yes No No
Multi-line Component Tags
Write component tags over multiple lines
Yes No No No

django-cotton's People

Contributors

wrabit avatar actions-user avatar shakedown-street avatar ikollipara avatar

Stargazers

Chinseok Lee avatar Ivan Domenech avatar  avatar Alexander Bogushov avatar Al Mahdi avatar Simon Cam avatar Sergei Konik avatar Jason Emerick avatar Bart avatar Yevhen Ts. avatar Varlam Zhordania avatar Marco Meo avatar  avatar  Chase Frankenfeld  avatar Miguel Lopez avatar Abdullah Altatan avatar Patrick Hintermayer avatar Christoph avatar ryche avatar Michael May avatar Josiah Kaviani avatar imhyuk5054 avatar Victor avatar Max Muth avatar Patrick Arminio avatar Li Xulun avatar Luca Fedrizzi avatar Oleksandr Romaniuk avatar Kevin Tewouda avatar  avatar Jan Feřtek avatar Clarence Vinzcent Reyes avatar Paulo Amaral avatar Scott Sharkey avatar Pedro Costa avatar Jason Christa avatar David Colgan avatar Florian Kromer avatar Robert Engler avatar Petr Stříbný avatar Farhan Masud Aneek avatar Jonathan Hoffman avatar Christoph Bülter avatar Muhammad Usman avatar Sergey Kopylov avatar Mujahid Anuar avatar fF avatar Bruno Melo avatar  avatar Waseem akram avatar Nikita Karamov avatar Pascal Fouque avatar John Speno avatar Padraic Harley avatar Wouter Lansu avatar Mark Kennedy avatar  avatar John Beimler avatar Alessio Civitillo avatar Brian Perrett avatar Glenn Franxman avatar Arthur Kay avatar  avatar Andy Smith avatar  avatar Ali Yaman avatar Mahmoud Ezzat avatar  avatar cyrus mwendwa avatar Amine Lemaizi avatar Qing Ao avatar Joseph Sanger avatar  avatar Moonlit Grace avatar Arcuri Davide avatar Patrick Forringer avatar Sebastian Pipping avatar Dan Hayden avatar turulomio avatar Thomas Feldmann avatar Eugene Klimov avatar  avatar stephen avatar Zeyad Moustafa avatar Anton Alekseev avatar George Poulos avatar Dustin Wyatt avatar Przemysław Płyś avatar Jason Oppel avatar Mangabo  Kolawole avatar Ehco avatar Sadra Yahyapour avatar Ernesto González avatar Samuel Mok avatar Will Vincent avatar Alex Silverman avatar beres avatar Jannik Waschkau avatar Michał Zawadzki avatar  avatar

Watchers

Lucian avatar Przemysław Płyś avatar Dan Hayden avatar  avatar Arthur Kay avatar

django-cotton's Issues

'submit' is not allowed.

Hello!

There is an issue with passing the word 'submit' as a var.

<!-- button.cotton.html -->
<c-vars type="button"/>


<button type="{{ type }}">
  {{ slot }}
</button>

The following works and outputs <button type="button">A button</button> correctly.

<!-- view.cotton.html -->

<c-buttons.button>
    A button
</c-buttons.button>

However, the following outputs <button>A button</button>

<!-- view.cotton.html -->

<c-buttons.button type="submit">
    A button
</c-buttons.button>

Even stranger, if you change 'submit' to 'submitt' it works and outputs: <button type="submitt">A button</button>

<!-- view.cotton.html -->

<c-buttons.button type="submitt">
    A button
</c-buttons.button>

Rename c-props to c-vars

Motivation

Props was adopted from frontend frameworks, Vue, Svelte and Laravel Blade components. But in the django world, 'vars' / variables are more often used and better understood.

<c-props> provides a way to set default values for attributes (which are made available to Cotton components as template variables).

Based on feedback and research, I decided to make this change, whilst the project is still in pre 1.x.x release.

Map "False", "True", "None" in c-vars to python values

Motivation

We can set defaults for attributes inside a component using c-vars, at the moment, we can only set string values.

<c-vars show="True" />

Will set a template variable show: str = "True". Much like the standard django templates vars, it would be nice that the following get's converted:

"True": True
"False": False
"None": None

This will allow us to use proper types in our default values in c-vars.

We could go a step further here and allow for other native python types to be set here, i.e.:

<c-vars :options="['option 1', 'option 2']" :settings="{'name': 'Austria'}" />

Consider dropping the `.cotton.html` suffix requirement

Motivation

.cotton.html was implemented as a performance concern so that we can target specific files for the loader, this was if cotton was used sparingly. But the approach seems to be to adopt cotton everywhere in the project. Therefore the additional 'project-creep' that this requirement brings really is not worth it.

Breaking changes:

  • This will cause a breaking change so clear instruction about renaming will be given.
  • Also, cached versions of compiled components will need to be broken in this update so provide instruction to clear cache to unsure cached compiled files with previous api are not served

Tasks

  • Remove the check in get_template_sources
  • Update docs, tests repo files

Optimise cvars usage

Currently an additional 'vars_frame' component wraps around contents of ALL components no matter if the component declares a c-vars tag or not.

The vars frame's job is to manage attributes + vars which will add fractional compute. Multiplied by 10's or more components on the page, the possible savings could be worth while.

Tasks:

  • Make the _vars_frame optional + selectively include it when there are cvars to process
  • Tests to ensure functionality is the same with or without the vars_frame
  • Benchmark before and after to discover any performance gain

How to work with urls that have arguments?

I'm having a great time migrating a lot of boilerplate + dropdowns so thank you for this library!

One pattern I have not been able to crack properly is working with URLs that have arguments.

This is what I have tried so far but for some reason it still tries rendering the URL and a NoReverseMatch error is throw when a URL is not passed. Further, I can't seem to get database IDs or other URL arguments to be passed down.

button.cotton.html

<c-vars method url base_class class=""/>

<button {{ attrs }}

  {% if url %}
  hx-{{ method }}="{% url url %}"
  {% endif %}
  
    class="inline-flex items-center gap-x-2 rounded {{ padding }} {{ base_class }} {{ class }}">
  {{ icon_left }}
  {{ slot }}
  {{ icon_right }}
</button>

Usage

<c-buttons.button 
   class="w-full"
    method="delete"
     <--- have tried many attempts of passing the url without success --->
    url="delete_obj {{ obj.id }}" 
    :url="delete_obj obj.id"
    :url="'delete_obj' {{ obj.id }}"
    <--- end attempts --->
    hx-confirm="Please confirm you wish to delete this object."
    type="button">
    <c-slot name="icon_left">
      <c-icons.trash class="w-4 h-4"></c-icons.trash>
    </c-slot>
    Delete
</c-buttons.button>


Thanks,

Template loader path incorrect in docs

The path to the loader is incorrect in the docs

In the docs it says to add:

'django_cotton.template.loaders.CottonLoader'

When the loader is actually located at:

'django_cotton.cotton_loader.Loader'

Suggestion: props as boolean attributes

I'm not sure how hard this would be to implement, but it'd be nice to be able to pass props as boolean attributes rather than key/value pairs. As an example, a rounded prop on a button component.

Being able to do something like:

<c-button rounded>Click me</c-button>

Rather than:

<c-button rounded="true">Click me</c-button>

Loader is in someway changing the style

Hello,

Firstly, thank you for your project it's exactly what I've been searching for for quite some time.

I'm currently testing django-cotton, and one issue I've noticed is that the Loader seems to be affecting some application styles in a certain way.

<nav class="-mb-px flex space-x-8" aria-label="Tabs">
  <a hx-get="{% url 'route_name' pk=obj.pk %}?search={{ params.items|get_filter_parameters_excluding }}&page=1&order={{ order }}&tab=test1&page_size={{ page_size }}"
      hx-swap="outerHTML"
      hx-target="#obj-page"
      hx-push-url="true"
      hx-indicator=".transitioned-on-loading"
      {% if tab == "test1" %} class="cursor-pointer border-indigo-500 text-indigo-600 dark:text-indigo-500 group inline-flex items-center py-4 px-1 border-b-2 font-medium text-sm" aria-current="page" {% else %} class="cursor-pointer border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-300 dark:hover:text-white hover:border-gray-300 group inline-flex items-center py-4 px-1 border-b-2 font-medium text-sm" {% endif %}>
    <span class="text-center sm:text-left">Test 1</span>
  </a>
  <a hx-get="{% url 'route_name' pk=obj.pk %}?search={{ params.items|get_filter_parameters_excluding }}&page=1&order={{ order }}&tab=test2&page_size={{ page_size }}"
      hx-swap="outerHTML"
      hx-target="#obj-page"
      hx-push-url="true"
      hx-indicator=".transitioned-on-loading"
      {% if tab == "test2" %} class="cursor-pointer border-indigo-500 text-indigo-600 dark:text-indigo-500 group inline-flex items-center py-4 px-1 border-b-2 font-medium text-sm" aria-current="page" {% else %} class="cursor-pointer border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-300 dark:hover:text-white hover:border-gray-300 group inline-flex items-center py-4 px-1 border-b-2 font-medium text-sm" {% endif %}>
    <span class="text-center sm:text-left">Test 2</span>
  </a>
  <a hx-get="{% url 'route_name' pk=obj.pk %}?search={{ params.items|get_filter_parameters_excluding }}&page=1&order={{ order }}&tab=test3&page_size={{ page_size }}"
      hx-swap="outerHTML"New
      hx-target="#obj-page"
      hx-push-url="true"
      hx-indicator=".transitioned-on-loading"
      {% if tab == "test3" %} class="cursor-pointer border-indigo-500 text-indigo-600 dark:text-indigo-500 group inline-flex items-center py-4 px-1 border-b-2 font-medium text-sm" aria-current="page" {% else %} class="cursor-pointer border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-300 dark:hover:text-white hover:border-gray-300 group inline-flex items-center py-4 px-1 border-b-2 font-medium text-sm" {% endif %}>
    <span class="text-center sm:text-left">Test 3</span>
  </a>
</nav>

with the Loader:
image

without the Loader:
image

Thanks in advance

Encode special characters

Issue

In German we have special characters like Ä, Ö, Ü and ß. But I've tested also cyrillic, arabic and greek.

Enablind the django_cotton.cotton_loader.Loader in the TEMPLATE['OPTIONS']['loaders'] settings.py will lead to display all these characters loaded directly from an html page incorrectly.
e.g. Händler is displayed as Händler

This is only true for directly in HTML "hardcoded" words.

Example

<html lang="de">
<head>
    <meta charset="utf-8">
    ...
</head>
<body>
    <h1>Test Ä Ö Ü ß</h1>  <!-- This will be messed up as described above -->
    <p>Добрый день.</p>
    <p>الخير.</p>
    <p>καλό απόγευμα</p>
    <ul>
    {% for user in users %}
    <li>{{ user.get_name }}</li>  <!-- This is displayed correctly even if the name has special characters  -->
    {% endfor %}
    </ul>
</body>
</html>

Result

Test Ä Ö Ü ß
Добрый день.
الخير.
καλό απόγευμα

  • Jürgen Gärtner
  • Thomas Große

What I've tried

I tried changing the encoding inside the <head> and inside Djangos TEMPLATES['OPTIONS']['file_charset'] without success.
Here's my full TEMPLATES settings in case I'm missing something:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
        'APP_DIRS': False,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'loaders': [
                'django_cotton.cotton_loader.Loader',  # uncommenting this line will resolve the issue but obviously will break c-cotton
                'django.template.loaders.filesystem.Loader',
                'django.template.loaders.app_directories.Loader',
            ],
            'builtins': [
                'django_cotton.templatetags.cotton',
            ],
            'file_charset': 'utf-8',  # default: utf-8
        },
    },
]
```

Issue with using Django for loops

When using a for loop in a component the .last and .first attributes do not work.

infinity_scroll.cotton.html

<ul role="list" class="divide-y divide-gray/5">
  {% for item in items %}
    <li class="{{ class }}"
        <!--  doing 'forloop.last is True' also does not work    -->
        {% if forloop.last %} <-- does not work
                hx-get="{% url 'my-url' %}?page={{ page }}"
                hx-swap="afterend"
                hx-trigger="intersect once"
                <!--       This does oddly succesfully evaluate to True/False but the conditional above doesn't.         -->
                data-has-more="{{ forloop.last }}"
      {% endif %}>
      {{ slot }}
    </li>
  {% endfor %}
</ul>

usage

<!--Events is a paginated queryset from the view.-->
<c-components.infinity-scroll :items="events"></c-components.infinity-scroll>

My assumption is that built-in template tags are being overridden.

Perhaps https://docs.djangoproject.com/en/5.0/ref/templates/builtins/#for should be protected while building the component?

Performance consideration when using cotton

Hi @wrabit

First and foremost, I absolutely love the idea behind this project. This is just fascinating and I have been looking for something like this for a very long time.

I was trying this and my first concern was what kind of performance hit this would cause for a project.

We wrote a small test case and while it was not running in production, we did see an order of magnitude difference in {% include "..." %} vs using the component.

Do you or anyone has done any performance testing? Or are there any tricks we can use to improve the performance.

Regards
Amit

Allow django tags inside attribute values

Currently, the only way to pass a string that contains django native tags down to a component is by a named slot. It would be easer and simpler to be able to add these directly inside attribute values:

Now:

<c-link>
  <c-slot name="url">{% url 'page1' id %}</c-slot>
  Link
</c-link>

<c-todo>
  <c-slot name="input_name">todos.{{ index }}.title</c-slot>
</c-todo>

After:

<c-link url="{% url 'page1' id %}">
  Link
</c-link>

<c-todo input_name="todos.{{ index }}.title" />

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.