Actions#

Components can also trigger methods from the templates by listening to any valid event type. The most common events would be click, input, keydown, keyup, and mouseenter, but MDN has a list of all of the browser event types available.

Events#

An example action to call the clear_name method on the component.

<!-- clear-name.html -->
<div>
  <input unicorn:model="name" type="text" id="text" />
  Hello {{ name|title }}
  <button unicorn:click="clear_name">Clear Name</button>
</div>
# clear_name.py
from django_unicorn.components import UnicornView

class ClearNameView(UnicornView):
    name = "World"

    def clear_name(self):
        self.name = ""

When the button is clicked, the name property will get set to an empty string. Then, the component will intelligently re-render itself and the text input will update to match the property on the component.

Tip

Instance methods without arguments can be called from the template with or without parenthesis.

Passing arguments#

Actions can also pass basic Python types to the backend component.

<!-- passing-args.html -->
<div>
  <input unicorn:model="name" type="text" id="text" />
  Hello {{ name|title }} 👋
  <button unicorn:click="set('Bob')">Set as Bob</button>
  <button unicorn:click="set()">Set default value of name argument</button>
</div>
# passing_args.py
from django_unicorn.components import UnicornView

class PassingArgsView(UnicornView):
    name = "World"

    def set(self, name="Universe"):
        self.name = name

Argument types#

Most basic Python types, including string, int, float, bool, list, tuple, dictionary, and set, are supported by default.

<!-- argument-types.html -->
<div>
  <button unicorn:click="update(99)">Pass int</button>
  <button unicorn:click="update(1.234)">Pass float</button>
  <button unicorn:click="update(True)">Pass bool</button>
  <button unicorn:click="update({'key': 'value'})">Pass dictionary</button>
  <button unicorn:click="update([1, 2, 3])">Pass list</button>
  <button unicorn:click="update((1, 2, 3))">Pass tuple</button>
  <button unicorn:click="update({1, 2, 3})">Pass set</button>
</div>

Coerced types#

Arguments with the types of datetime, date, time, timedelta, and UUID can be coerced by using a type annotation in the action method.

Note

Django’s dateparse methods are used to convert strings to the date-related type.

Note

Both integer and float epochs can also be coerced into datetime or date objects.

# argument_type_hints.py
from django_unicorn.components import UnicornView
from datetime import date, datetime
from uuid import UUID

class ArgumentTypeHintsView(UnicornView):
    def is_datetime(self, obj: datetime):
        assert type(obj) is datetime
    
    def is_uuid(self, obj: UUID):
        assert type(obj) is UUID

    def is_date(self, obj: date = None):
        assert type(obj) is date
<!-- argument-type-hints.html -->
<div>
  <button unicorn:click="is_datetime('2020-09-12T01:01:01')">Check datetime with string</button>
  <button unicorn:click="is_datetime(1691499534)">Check datetime with epoch</button>
  <button unicorn:click="is_uuid('90144cb9-fc47-476d-b124-d543b0cff091')">Check UUID</button>
  <button unicorn:click="is_date(obj='2020-09-12')">Check date</button>
</div>

Django models#

Django models can be instantiated as an argument or with a pk kwarg and a type annotation.

# argument_model.py
from django_unicorn.components import UnicornView
from django.contrib.auth.models import User

class ArgumentModelView(UnicornView):
    def is_user_via_arg(self, obj: User):
        assert type(obj) is User
    
    def is_user_via_kwarg(self, pk: User=None):
        assert type(obj) is User
<!-- argument-model.html -->
<div>
  <button unicorn:click="is_user_via_arg(1)">Gets user with pk of 1</button>
  <button unicorn:click="is_user_via_kwarg(pk=2)">Gets user with pk of 2</button>
</div>

Custom types#

Custom objects can also be used as a type annotation and Unicorn will attempt to instantiate the object with the value that is passed in as the argument.

# argument_custom_type.py
from django_unicorn.components import UnicornView

class CustomType:
  def __init__(self, custom_type_id: int):
    self.custom_type_id = custom_type_id

class ArgumentCustomTypeView(UnicornView):
    def is_custom_type(self, obj: CustomType):
        assert type(obj) is CustomType
<!-- argument-custom-type.html -->
<div>
  <button unicorn:click="is_custom_type(1)">Gets custom type</button>
</div>

Enums#

Enums themselves cannot be passed as an argument, but the enum value can be since that is a Python primitive. Use the enum as a type annotation to coerce the value into the specified enum.

# enum.py
from django_unicorn.components import UnicornView
from enum import Enum

class Color(Enum):
  RED = 1
  GREEN = 2
  BLUE = 3
  PURPLE = 4

class EnumView(UnicornView):
    color = Color.RED
    purple_color = Color.PURPLE

    def set_color(self, color: Color):
      self.color = color
<!-- enum.html -->
<div>
  <button unicorn:click="set_color({{ color.BLUE.value }})">Show BLUE (and 3) below when clicked</button>
  <button unicorn:click="set_color(2)">Show GREEN (and 2) below when clicked</button>
  <button unicorn:click="set_color({{ purple_color.value }})">Show PURPLE (and 4) below when clicked</button>

  <br />
  <!-- This will be RED when first rendered, and then will change based on the button clicked above -->
  Color: {{ color }}

  <br />
  <!-- This will be 1 when first rendered, and then will change based on the button clicked above -->
  Color int: {{ color.value }}
</div>

Set shortcut#

Actions can also set properties without requiring an explicit method.

<!-- set-shortcut.html -->
<div>
  <input unicorn:model="name" type="text" id="text" />
  Hello {{ name|title }} 👋
  <button unicorn:click="name='Bob'">Set name as Bob</button>
</div>
# set_shortcut.py
from django_unicorn.components import UnicornView

class SetShortcutView(UnicornView):
    name = "World"

Modifiers#

Similar to models, actions also have modifiers which change how the method gets called.

prevent#

Prevents the default action the browser would use for that element. The same as calling preventDefault.

<!-- prevent-modifier.html -->
<div>
  <button unicorn:click.prevent="name='Bob'">Set name as Bob</button>
</div>

stop#

Stops the event from bubbling up the event chain. The same as calling stopPropagation.

<!-- stop-modifier.html -->
<div>
    <button unicorn:click.stop="name='Bob'">Set name as Bob</button>
</div>

discard#

Discards any model updates from being saved before calling the specified method on the view. Useful for a cancel button.

<!-- discard-modifier.html -->
<div>
    <input type="text" unicorn:model="name">
    <button unicorn:click.discard="cancel">Cancel</button>
</div>
# discard_modifier.py
from django_unicorn.components import UnicornView

class DiscardModifierView(UnicornView):
    name = None

    def cancel(self):
        pass

debounce#

Waits the specified time in milliseconds before calling the specified method.

<!-- debounce-modifier.html -->
<div>
    <input type="text" unicorn:model="name">
    <button unicorn:click.debounce-1000="add_count">Add Count</button>
</div>

Special arguments#

$event#

A reference to the event that triggered the action.

<!-- event.html -->
<div>
    <input type="text" unicorn:change="update($event.target.value.trim())">Update</input>
</div>

$returnValue#

A reference to the last return value from an action method.

<!-- returnValue.html -->
<div>
    <input type="text" unicorn:change="update($returnValue.trim())">Update</input>
</div>

$parent#

A reference to the parent of the current component.

Special methods#

$refresh#

Refresh and re-render the component from its current state.

<!-- refresh-method.html -->
<div>
  <button unicorn:click="$refresh">Refresh the component</button>
</div>

$reset#

Revert the component to its original state.

<!-- reset-method.html -->
<div>
  <button unicorn:click="$reset">Reset the component</button>
</div>

$toggle#

Toggle a field on the component. Can only be used for fields that are booleans.

<!-- toggle-method.html -->
<div>
  <button unicorn:click="$toggle('check')">Toggle the check field</button>
</div>

Tip

Multiple fields can be toggled at the same time by passing in multiple fields at a time: unicorn:click="$toggle('check', 'another_check', 'a_third_check')". Nested properties are also supported: unicorn:click="$toggle('nested.check')".

$validate#

Validates the component.

<!-- validate-method.html -->
<div>
  <button unicorn:click="$validate">Validate the component</button>
</div>

Calling methods#

Sometimes you need to trigger a method on a component from regular JavaScript. That is possible with Unicorn.call(). The first argument is the name (or key) of the component and the second argument is the name of the method to call.

<!-- call-with-component-name.html -->
{% unicorn 'hello-world' %}

<button onclick="Unicorn.call('hello-world', 'set_name');">
  Set the name from outside the component
</button>
<!-- call-with-component-key.html -->
{% unicorn 'hello-world' key='hello-universe' %}

<button onclick="Unicorn.call('hello-universe', 'set_name');">
  Set the name from outside the component
</button>

Passing arguments to the method call is also supported.

<!-- index.html -->
{% unicorn 'hello-world' %}

<button onclick="Unicorn.call('hello-world', 'set_name', 'World');">
  Set the name to "World" from outside the component
</button>

Return values#

To retrieve the last action method’s return value, use Unicorn.getReturnValue().

<!-- index.html -->
{% unicorn 'hello-world' %}

<button onclick="alert(Unicorn.getReturnValue('hello-world'));">
  Get the last return value
</button>