# Welcome

Build reactive applications with the Django tooling you already know and love

## What is Sockpuppet?

**Sockpuppet is a new way to craft modern, reactive web interfaces with Django.**

We extend the capabilities of both [Django](https://www.djangoproject.com/) and [Stimulus](https://stimulusjs.org) by intercepting user interactions and passing them to Django over real-time websockets. These interactions are processed by *Reflex actions* that change application state. The current page is quickly re-rendered and the changes are sent to the client. The page is then [morphed](https://github.com/patrick-steele-idem/morphdom) to reflect the new application state. This entire round-trip allows us to update the UI in 20-30ms without flicker or expensive page loads.

This architecture eliminates the complexity imposed by full-stack frontend frameworks without abandoning [high-performance reactive user experiences](https://www.youtube.com/watch?v=SWEts0rlezA\&t=214s). With Sockpuppet, small teams can do big things faster than ever before. We invite you to explore **a fresh alternative to the Single Page App** (SPA).

We are indebted to the work done in [StimulusReflex](https://docs.stimulusreflex.com). Without them Sockpuppet wouldn't exist; it's our bigger sibling who prefers to play in the Rails world.

{% hint style="success" %}
**Get Involved.** We are stronger together! Please join us on [Discord![Discord shield](https://img.shields.io/discord/629472241427415060)](https://discord.gg/XveN625)

[![GitHub stars](https://img.shields.io/github/stars/jonathan-s/sockpuppet?style=social)](https://github.com/jonathan-s/sockpuppet) [![GitHub forks](https://img.shields.io/github/forks/jonathan-s/sockpuppet?style=social)](https://github.com/jonathan-s/sockpuppet) [![Twitter follow](https://img.shields.io/twitter/follow/argparse?style=social)](https://twitter.com/argparse)
{% endhint %}

## Why should I use Sockpuppet?

Wouldn't it be great if you could **focus on your product** instead of the technical noise introduced by modern JavaScript? With Sockpuppet, you'll **ship projects faster, with smaller teams** and re-discover the joy of programming.

### Goals

* [x] Enable small teams to do big things, faster 🏃🏽‍♀️
* [x] Increase developer happiness ❤️❤️❤️
* [x] Facilitate simple, concise, and clear code 🤸
* [x] Integrate seamlessly with Django 🚝

## Build the next Twitter in just 9 minutes (or less) 😉

Our friends at StimulusReflex proves that you can build things fast.

{% embed url="<https://www.youtube.com/watch?v=F5hA79vKE_E>" %}

## How we got here

Applications nowadays pursue native UI speeds which spawned a new breed of increasingly complex technologies. Modern **Single Page Apps** have pushed much of the server's responsibilities to the client. Unfortunately, this new approach trades *a developer experience* that was once **fun and productive** for an alternative of high complexity and only marginal gains.

**There must be a better way.**

## The revolution begins

In his 2018 ElixirConf keynote, [Chris McCord](https://twitter.com/chris_mccord) *(creator of the* [*Phoenix*](http://www.phoenixframework.org/) *framework for* [*Elixir*](https://elixir-lang.org/)*)* introduced [LiveView](https://github.com/phoenixframework/phoenix_live_view), an alternative to the SPA. His [presentation](https://www.youtube.com/watch?v=8xJzHq8ru0M) captures the same promise and excitement that Rails had in the early days.

We love Elixir and Phoenix. Elixir hits a sweet spot for people who want Rails-like conventions in a functional language. The community is terrific, but it's still small and comparatively niche.

Also, we just really enjoy using **Django**.

Sockpuppet was originally inspired by StimulusReflex which was inspired by LiveView, but we are charting our own course together with StimulusReflex. Our goal has always been to make building modern apps with Django the most productive and enjoyable option available. We want to inspire our friends working with other tools and technologies to evaluate how concepts like Sockpuppet could work in their ecosystems and communities.

## Architecture

So what is happening behind the scenes here? Sockpuppet works in three layers.

It uses JavaScript; The JavaScript is re-used from StimulusReflex, which in itself is built upon StimulusJS. The JavaScript in StimulusReflex also uses something called `morphdom` which has the responsibility of modifying the DOM. The JavaScript layer ensures that the data is being sent to the server layer through websockets. When a message is received from the server layer it will re-render the dom according to the server-side instructions.

The HTML layer has the responsibility of defining where and how a "Reflex" is going to be triggered. Ie, a Reflex could be triggered by a browser event or the click of a button. When building more complex applications the HTML layer may also be used to store state so that this state is accessible when doing some interactivity in a stimulus controller.

The last layer is the server layer. This is where a Reflex class is defined. The Reflex knows what path and template was received and will re-render that template to contain any new information defined in the Reflex. The Reflex could also make database queries or initiate long-running processes. Once the template is re-rendered it will send it back to the frontend where JavaScript will modify the DOM to update everything.


# Setup

How to prepare your app to use Sockpuppet

Sockpuppet is ultimately a port of Rails' library StimulusReflex and thus also relies on [Stimulus](https://stimulusjs.org/), an excellent library from the creators of Rails (though it has no dependency on Rails and can be used entirely by itself).

You can easily install Sockpuppet to new and existing Django projects.

```bash
pip install django-sockpuppet

# If performance is important you can take advantage lxml parsing
# It will typically speed up the round trip by 30-90ms depending on the html
pip install django-sockpuppet[lxml]


# Add these into INSTALLED_APPS in settings.py
INSTALLED_APPS = [
    'channels',
    'sockpuppet'
]

# generates scaffolding for webpack.config.js and installs required js dependencies
# if you prefer to do that manually read on below.
python manage.py initial_sockpuppet

# scaffolds a new reflex with everything that's needed.
python manage.py generate_reflex app_name name_of_reflex
```

The terminal commands above will ensure that Sockpuppet is installed. It creates an example to get you started.

If you want or need to build your own JavaScript you need to make some more adjustments. The `initial_sockpuppet` command helps you to set up a JavaScript build flow with Webpack. If you don't want to do this you can use the following in your templates to load the required JavaScript.

```python
{% static 'sockpuppet/sockpuppet.js' %}
```

You also need to make some further configurations in `settings.py` to configure Channels.

## Configuration

Sockpuppet depends on django-channels for the websockets functionality, and as such we need that configuration. We need to make some changes to `settings.py` where we need to add the following.

```python
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}
# in the same folder as wsgi.py
ASGI_APPLICATION = 'sockpuppet.routing.application'
INSTALLED_APPS = [
    ...
    'channels',
    'sockpuppet',
    ...
]
```

{% hint style="danger" %}
Instead of using redis as a channel layer you can use the in-memory channel layer. But that should **ONLY** be used for development purposes or tests.

```python
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels.layers.InMemoryChannelLayer"
    }
}
```

{% endhint %}

If you already are using `django-channels` in your project you can take a look at the source code of the routing file in Sockpuppet and amend your routing as needed.

### Javascript configuration

You may already have a working build system in javascript for your Django project. If you don't we've got you covered.

There isn't a particularly strong convention on javascript should be handled in Django, so below is a proposal on how you could organize your build setup.

So let's first install all the dependencies we need for the most minimal Webpack configuration to work.

```bash
npm i -D fs path sockpuppet-js stimulus stimulus_reflex webpack webpack-cli
```

We also need to build and watch any changes that we make in our project. For this we add two script options into `package.json`

```javascript
"scripts": {
    "build": "webpack --mode production",
    "watch": "webpack --watch --info-verbosity verbose"
},
```

The last part is the configuration for Webpack itself.

{% tabs %}
{% tab title="webpack.config.js" %}

```javascript
const webpack = require('webpack');
const glob = require('glob');


let globOptions = {
    ignore: ['node_modules/**', 'venv/**']
}

let entryFiles = glob.sync("**/javascript/*.js", globOptions)

let entryObj = {};
entryFiles.forEach(function(file){
    if (file.includes('.')) {
        let parts = file.split('/')
        let path = parts.pop()
        let fileName = path.split('.')[0];
        entryObj[fileName] = `./${file}`;
    }
});

const config = {
    mode: process.env.NODE_ENV,
    entry: entryObj,
    output: {
        path: __dirname + '/dist/js',
        filename: '[name].js'
    },
    optimization: {
        minimize: false
    }
}

module.exports = config
```

{% endtab %}
{% endtabs %}

The configuration above will look for JavaScript files in the folder `your_app/javascript`, compile them and place the output in the folder `dist/js/`.

If you add that folder to `STATICFILES_DIRS` in settings it will pick that compiled JavaScript and you can use it in templates.

```python
from pathlib import Path
BASE_DIR = Path.cwd()
STATICFILES_DIRS = [
    ("js", f"{BASE_DIR}/dist/js"),
]
```

And that's it! **You can start using Sockpuppet in your application.**

## Session storage

By default, Django is using the database as a backend for sessions. Examples in the quickstart will be using sessions as a way to persist data between page loads.

This may cause more strain on your database in high-traffic scenarios than would you like. Since you are already using Redis for `django-channels` you could use Redis as a session storage. The library [`django-redis`](https://github.com/jazzband/django-redis) has instructions to set that up.


# Quick Start

How to use Sockpuppet in your app

## Before you begin...

**A great user experience can be created with Django**. In conversations about modern web development, servers like Django are often typecast as the backend for a frontend framework such as ReactJS or VueJS.

If you are happy with that way of building applications, then you can stop reading now.

We are only alive for a short while and learning any new technology is a sacrifice of time spent with those you love, creating art or walking in the woods.

Every framework you learn is a lost opportunity to build something that could really matter to the world. **Please choose responsibly.**

It might strike you as odd that we would start by questioning whether you need this library at all. Our motivations are an extension of the question we hope more people will ask.

Instead of "*Which Single Page App framework should I use?*" we believe that StimulusReflex can empower people to wonder "**Do we still need React, given what we now know is possible?**"

## Hello, Reflex

Bringing your first Reflex to life:

1. Declare the appropriate data attributes in HTML together with a Python view.
2. Initialize a Stimulus application in JavaScript.
3. Create a server-side Reflex object with Python.

### Getting started quickly

The following command will generate everything you need to see the Reflex in action.

```bash
python manage.py generate_reflex your_app your_reflex_name
# Side note: You can add --javascript if you want to generate a Stimulus controller as well.
```

Hook up the view that was generated to `urls.py`, visit the URL and click increment. Magic! ✨

{% hint style="info" %}
In the template, you'll see the following:

```markup
{% static 'sockpuppet/sockpuppet.js' %}
```

If you don't want or need to build your own JavaScript with a build tool you can use that static tag.

However, if you want to take advantage of things like [lifecycle callbacks](https://sockpuppet.argpar.se/lifecycle) you'll have to start defining your own Stimulus controllers and build your own JavaScript.
{% endhint %}

### Call Reflex methods on the server without defining a Stimulus controller

The command that you just ran generated a reflex without defining a Stimulus controller. The example will automatically update the page with the latest count whenever the anchor is clicked.

{% code title="your\_app/templates/index.html" %}

```markup
<body>
    <a href="#"
    data-reflex="click->CounterReflex#increment"
    data-step="1"
    data-count="{{ count }}"
    >Increment {{ count }}</a>
</body>
```

{% endcode %}

We use data attributes to declaratively tell Sockpuppet to pay special attention to this anchor link. `data-reflex` is the command you'll use on almost every action. The format follows the Stimulus convention of `[browser-event]->[ServerSideClass]#[action]`. The other two attributes, `data-step` and `data-count` are used to pass data to the server. You can think of them as arguments.

We are also assuming that we have a view that renders this template. The view looks like this.

{% code title="your\_app/view\.py" %}

```python
from django.views.generic.base import TemplateView

class CountView(TemplateView):
    template_name = 'index.html'

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context['count'] = 0
        return context
```

{% endcode %}

If you are building your own JavaScript this is what you need to wire up the JavaScript behind Sockpuppet. If not, you can use

```markup
{% load static %}
{% static 'sockpuppet/sockpuppet.js %}
```

in the template instead to include the required JavaScript.

{% code title="frontend/src/js/index.js" %}

```javascript
import { Application } from 'stimulus'
import StimulusReflex from 'stimulus_reflex'
import WebsocketConsumer from 'sockpuppet-js'

const application = Application.start()
const consumer = new WebsocketConsumer('ws://localhost:8000/ws/sockpuppet-sync')

StimulusReflex.initialize(application, { consumer })
```

{% endcode %}

Next up is defining a reflex in Python.

{% code title="your\_app/reflexes/counter\_reflex.py" %}

```python
from sockpuppet.reflex import Reflex

class CounterReflex(Reflex):
    def increment(self):
        self.count = (
            int(self.element.dataset['count']) +
            int(self.element.dataset['step'])
        )
```

{% endcode %}

Sockpuppet maps your requests to Reflex classes that live in your `your_app/reflexes` folder or reflexes that exist in the file `your_app/reflex.py`. In this example, the increment method is executed and the count is incremented by 1. The `self.count` instance variable is passed to the template when it is re-rendered.

{% hint style="success" %}
**Concerns like managing state and rendering views are handled server-side.** This technique works regardless of how complex the UI becomes. For example, we could render multiple instances of `self.count` in unrelated sections of the page and they will all update.
{% endhint %}

### Manually call a Reflex from a Stimulus controller

Real-world applications will benefit from additional structure and more granular control. Building on the solid foundation that Stimulus provides, we can use Controllers to build complex functionality and respond to events.

Manually calling a reflex from a stimulus controller also requires that you build your own JavaScript. The following command aims to help you setup a build flow using Webpack.

```
python manage.py initial_sockpuppet
```

Let's build on our increment counter example by adding a Stimulus controller and manually calling a Reflex action.

1. Declare the appropriate data attributes in HTML.
2. Create a client-side StimulusReflex controller with JavaScript.
3. Create a server-side Reflex object with Python.
4. Create a server-side Example view with Python.

{% code title="your\_app/templates/index.html" %}

```markup
<body>
    <a  href="#"
        data-controller="counter"
        data-action="click->counter#increment"
    >Increment {{ count }}</a>
</body>
```

{% endcode %}

Here, we rely on the standard Stimulus `data-controller` and `data-action` attributes. There's no StimulusReflex-specific markup required.

{% code title="frontend/src/js/controllers/counter\_controller.js" %}

```javascript
import { Controller } from 'stimulus';
import StimulusReflex from 'stimulus_reflex';

export default class extends Controller {
  connect() {
    StimulusReflex.register(this)
  }

  increment(event) {
    event.preventDefault()
    this.stimulate('CounterReflex#increment', 1)
  }
}
```

{% endcode %}

This controller needs to be registered together with the StimulusReflex application.

{% code title="" %}

```javascript
import { Application } from 'stimulus'
import StimulusReflex from 'stimulus_reflex'
import WebsocketConsumer from 'sockpuppet-js'
import CounterController from './controller/counter_controller.js'

const application = Application.start()
const consumer = new WebsocketConsumer('ws://localhost:8000/ws/sockpuppet-sync')

application.register('counter', CounterController)
StimulusReflex.initialize(application, { consumer })
```

{% endcode %}

The controller connects during the page load process and we tell StimulusReflex that this controller is going to be calling server-side Reflex actions. The `register` method has an optional second argument that accepts options, but we'll cover that later.

When the user clicks the anchor, Stimulus calls the `increment` method. All StimulusReflex controllers have access to the `stimulate` method. The first parameter is the `[ServerSideClass]#[action]` syntax, which tells the server which Reflex class and method to call. The second parameter is an optional argument which is passed to the Reflex method. If you need to pass multiple arguments, consider using a JavaScript object `{}` to do so.

{% hint style="warning" %}
If you're responding to an event like click on an element that would have a default action (such as an `a` or a `button` element) it's very important that you call preventDefault() on that event, or else you will experience undesirable side effects such as page navigation.
{% endhint %}

{% code title="your\_app/reflexes/counter\_reflex.py" %}

```python
from sockpuppet import reflex

class CounterReflex(reflex.Reflex):
  def increment(step=1)
    self.session['count'] = int(session['count']) + step
```

{% endcode %}

Here, you can see how we accept an optional `step` argument to our `increment` Reflex action. We're also now switching to using the Django session object to persist our values across multiple page load operations.

{% code title="your\_app/views.py.py" %}

```python
from django.views.generic.base import TemplateView

class CountView(TemplateView):
    template_name = 'index.html'

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        context['count'] = self.request.session.get('count', 0)
        return context
```

{% endcode %}

Finally, we set the value of the `self.count` instance variable in the view. When the page is first loaded, there will be no session\[:count] value and `self.count` will be 0.

{% hint style="info" %}
Instead of using sessions to persist data, you could store the data in Django models. To keep this example we use Django sessions to store our counter value.
{% endhint %}


# Reflexes

Reflex classes are full of Reflex actions. Reflex actions? Full of love. 🏩

Server-side Reflexes inherit from `sockpuppet.Reflex`. They hold logic responsible for performing operations like writing to your backend data stores. Reflexes are not concerned with rendering because rendering is delegated to the Django view.

## Glossary

* Sockpuppet: The name of this project, which has a JS websocket client and a Django-based server component, which is based on `django-channels`.
* Stimulus: An incredibly simple yet powerful JS framework by the creators of Rails.
* "a Reflex": Used to describe the full, round-trip life-cycle of a Sockpuppet operation, from client to server and back again
* Reflex class: A Python class that inherits from `sockpuppet.Reflex` and lives in your `reflexes` folder or `reflex.py`, this is where your Reflex actions are implemented.
* Reflex action: A method in a Reflex class, called in response to activity in the browser. It has access to several special accessors containing all of the Reflex controller element's attributes
* Reflex controller: A Stimulus controller that imports the StimulusReflex client library. It has a `stimulate` method for triggering Reflexes and like all Stimulus controllers, it's aware of the element it is attached to - as well as any Stimulus [targets](https://stimulusjs.org/reference/targets) in its DOM hierarchy
* Reflex controller element: The DOM element upon which the `data-reflex` attribute is placed, which often has data attributes intended to be delivered to the server during a Reflex action

## Calling a Reflex

Regardless of whether you use declarative Reflex calls via `data-reflex` attributes in your HTML or if you are using JavaScript, ultimately the `stimulate` method on your Stimulus controller is being called. We touched on this briefly in the **Quick Start** chapter; now we are going to document the function signature so that you fully understand what's happening behind the scenes.

All Stimulus controllers that have had `StimulusReflex.register(this)` called in their `connect` method gain a `stimulate` method.

```javascript
this.stimulate(string target, [DOMElement element], ...[JSONObject argument])
```

* **target**, required (exception: see "Requesting a Refresh" below): A string containing the server Reflex class and method, in the form "ExampleReflex#increment".
* **element**, optional: A reference to a DOM element which will provide both attributes and scoping selectors. Frequently pointed to `event.target` in JavaScript. **Defaults to the DOM element of the controller in scope**.
* **argument**, optional: A **splat** of JSON-compliant JavaScript datatypes - array, object, string, numeric or boolean - can be received by the server Reflex action as one or many ordered arguments. Defaults to no argument(s). **Note: the method signature has to match.** If the Reflex action is expecting two arguments and doesn't receive two arguments, it will raise an exception.

### Requesting a "refresh"

If you are building advanced workflows, there are edge cases where you may want to initiate a Reflex action that does nothing but re-render the view template and morph any new changes into the DOM. While this shouldn't be your primary tool, it's possible for your data to be mutated by destructive external side effects. 🧟

```javascript
this.stimulate()
```

Calling `stimulate` with no parameters invokes a special global Reflex that allows you to force a re-render of the current state of your application UI. This is the same thing that the user would see if they hit their browser's Refresh button, except without the painfully slow round-trip cycle.

It's also possible to trigger this global Reflex by passing nothing but a browser event to the `data-reflex` attribute. For example, the following button element will refresh the page content every time the user presses it:

```markup
<button data-reflex="click">Refresh</button>
```

## The Reflex Class

StimulusReflex makes the following properties available to the developer inside Reflex actions:

## Properties

* `consumer` - the Websocket connection from django channels.
* `request` - a django request object
* `request.post` - If the page contains a form, it will find the closest form which and the parameters will be contained here.
* `session` - the Django session store for the current visitor
* `url` - the URL of the page that triggered the reflex
* `element` - an object that represents the HTML element that triggered the reflex
* `params` - Contains the form parameters for the closest form

## Methods

* `get_context_data` - Accesses the context data from the view associated with the reflex. You will know that the method is triggered from the reflex because the context now contains `stimulus_reflex` which is equal to `True`. This will be available from `kwargs` so you can modify the context based on whether it is a reflex or not.
* `get_channel_id` - By default this returns the session key which is used to deliver the websocket update to the client. This function can be overridden if you need a different key for transferring the update.

{% hint style="danger" %}
`reflex` and `process` are reserved words inside Reflex classes. You cannot create Reflex actions with these names.
{% endhint %}

### Modify or add to the view context

When a reflex is triggered you can modify the current context of the view or add more context which previously didn't exist when the view rendered in the normal request-response cycle.

{% tabs %}
{% tab %}

```python
from sockpuppet.reflex import Reflex

class ExampleReflex(Reflex):
    def work(self):
        # All new instance variables in the reflex will be accessible
        # in the context during rendering.
        self.instance_variable = 'hello world'

        context = self.get_context_data()
        context['a_key'] = 'a pink elephant'
        # If "a_key" existed in the context before the reflex was triggered
        # the context variable will now be modified to "a pink elephant"

        # if it didn't exist, the context variable is then created with the
        # data "a pink elephant" 🐘
```

{% endtab %}

{% tab %}

```markup
<div>
    <!-- When the work reflex is triggered "new_variable" will be
    available in the context -->
    <span>{{ instance_variable }}</span>

    <!-- This will show up as "a pink elephant" when triggering the reflex -->
    <span>{{ a_key }}</span>
</div>
```

{% endtab %}
{% endtabs %}

### The `element` property

The `element` property contains all of the Reflex controller's [DOM element attributes](https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes) as well as other properties like, `tag_name`, `checked` and `value`.

{% hint style="info" %}
**Most values are strings.** The only exceptions are `checked` and `selected` which are booleans.

Elements that support **multiple values** (like `<select multiple>`, or a collection of checkboxes with equal `name`), will emit an additional **`values` property.** The `value` property will contain a comma-separated string of the checked options.
{% endhint %}

Here's an example that outlines how you can interact with the `element` property in your Reflexes.

{% code title="app/templates/show\.html" %}

```markup
<checkbox id="example" label="Example" checked
  data-reflex="ExampleReflex#work" data-value="123" />
```

{% endcode %}

{% tabs %}
{% tab %}

```python
from sockpuppet.reflex import Reflex

class ExampleReflex(Reflex):
    def work(self):
        self.element.attributes          # a dictionary that represents all attributes of the HTML element
        self.element.dataset             # a dictionary that represents the HTML element's dataset

        self.element.attributes['id']           # => 'example'
        self.element.attributes['tag_name']     # => 'CHECKBOX'
        self.element.attributes['checked']      # => 'true'
        self.element.attributes['label']        # => 'Example'
        self.element.attributes['data-reflex']  # => 'ExampleReflex#work'
        self.element.dataset['reflex']          # => 'ExampleReflex#work'
        self.element.attributes['data-value']   # => '123'
        self.element.dataset['value']           # => '123'
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
When Sockpuppet is rendering your template, a context variable named **stimulus\_reflex** is available to your Django view and set to true.

You can use this flag to create branching logic to control how the template might look different if it's a Reflex versus a normal page refresh.
{% endhint %}

### Inheriting data-attributes from parent elements

You might design your interface such that you have a deeply nested structure of data attributes on parent elements. Instead of writing code to travel your DOM and access those values, you can use the `data-reflex-dataset="combined"` directive to scoop all data attributes up the hierarchy and pass them as part of the Reflex payload.

```markup
<div data-post-id="{{ @post.id }}">
  <div data-category-id="{{ @category.id }}">
    <button data-reflex="click->Comment#create" data-reflex-dataset="combined">Create</button>
  </div>
</div>
```

This Reflex action will have `post-id` and `category-id` accessible:

```python
from sockpuppet import reflex

class CommentReflex(reflex.Reflex):
  def create(self)
    print(element.dataset["post-id"])
    print(element.dataset["category-id"])
```

If a data attribute appears several times, the deepest one in the DOM tree is taken. In the following example, `data-id` would be **2**.

```markup
<div data-id="1">
  <button data-id="2" data-reflex="Example#whatever" data-reflex-dataset="combined">Click me</button>
</div>
```


# Lifecycle

How to hook into Reflex activity... aka callbacks

## Client-Side Reflex Callbacks

StimulusReflex gives you the ability to inject custom Javascript at four distinct moments **around** sending an event to the server and updating the DOM. These hooks allow you to improve the user experience and handle edge cases.

1. **`before`** - prior to sending a request over the web socket
2. **`success`** - after the server-side Reflex succeeds and the DOM has been updated
3. **`error`** - whenever the server-side Reflex raises an error
4. **`after`** - after both `success` and `error`

{% hint style="info" %}
**Using lifecycle callback methods is not a requirement.**

Think of them as power tools that can help you build more sophisticated results. 👷
{% endhint %}

If you define a method with a name that matches what the library searches for, it will run at just the right moment. **If there's no method defined, nothing happens.** StimulusReflex will only look for these methods in Stimulus controllers that have called `StimulusReflex.register(this)` in their `connect()` function.

There are two kinds of callback methods: **generic** and **custom**. Generic callback methods are invoked for every Reflex action on a controller. Custom callback methods are only invoked for specific Reflex actions.

StimulusReflex also emits lifecycle events which can be captured in other Stimulus controllers, jQuery plugins or even the console.

### Generic Lifecycle Methods

StimulusReflex controllers can define up to four generic lifecycle callback methods. These methods fire for every Reflex action handled by the controller.

1. `beforeReflex`
2. `reflexSuccess`
3. `reflexError`
4. `afterReflex`

{% code title="templates/show\.html" %}

```markup
<div data-controller="example">
  <a href="#" data-reflex="ExampleReflex#update">Update</a>
  <a href="#" data-reflex="ExampleReflex#delete">Delete</a>
</div>
```

{% endcode %}

{% code title="javascript/controllers/example\_controller.js" %}

```javascript
import { Controller } from 'stimulus'
import StimulusReflex from 'stimulus_reflex'

export default class extends Controller {
  connect () {
    StimulusReflex.register(this)
  }

  beforeReflex(anchorElement) {
    const { reflex } = anchorElement.dataset
    if (reflex.match(/update$/)) anchorElement.innerText = 'Updating...'
    if (reflex.match(/delete$/)) anchorElement.innerText = 'Deleting...'
  }
}
```

{% endcode %}

In this example, we update each anchor's text before invoking the server side Reflex.

### Custom Lifecycle Methods

StimulusReflex controllers can define up to four custom lifecycle callback methods for **each** Reflex. These methods use a naming convention **based on the name of the Reflex**. For example, the Reflex `ExampleReflex#update` will cause StimulusReflex to check for the existence of the following lifecycle callback methods:

1. `beforeUpdate`
2. `updateSuccess`
3. `updateError`
4. `afterUpdate`

{% code title="templates/show\.html" %}

```markup
<div data-controller="example">
  <a href="#" data-reflex="ExampleReflex#update">Update</a>
  <a href="#" data-reflex="ExampleReflex#delete">Delete</a>
</div>
```

{% endcode %}

{% code title="javascript/controllers/example\_controller.js" %}

```javascript
import { Controller } from 'stimulus'
import StimulusReflex from 'stimulus_reflex'

export default class extends Controller {
  connect () {
    StimulusReflex.register(this)
  }

  beforeUpdate(anchorElement) {
    anchorElement.innerText = 'Updating...'
  }

  beforeDelete(anchorElement) {
    anchorElement.innerText = 'Deleting...'
  }
}
```

{% endcode %}

Adapting the Generic example, we've refactored our controller to capture the `before` callback events for each anchor individually.

{% hint style="info" %}
**It's not required to implement all lifecycle methods.** Pick and choose which lifecycle callback methods make sense for your application. The answer is frequently **none**.
{% endhint %}

### Conventions

#### Method Names

Lifecycle callback methods apply a naming convention based on your Reflex actions. For example, the Reflex `ExampleReflex#do_stuff` will produce the following camel-cased lifecycle callback methods.

1. `beforeDoStuff`
2. `doStuffSuccess`
3. `doStuffError`
4. `afterDoStuff`

#### Method Signatures

Both generic and custom lifecycle callback methods share the same arguments:

* `beforeReflex(element, reflex)`
* `reflexSuccess(element, reflex)`
* `reflexError(element, reflex, error)`
* `afterReflex(element, reflex, error)`

**element** - the DOM element that triggered the Reflex *this may not be the same as the controller's `this.element`*

**reflex** - the name of the server-side Reflex

**error** - the error message if an error occurred, otherwise `null`

### Lifecycle Events

If you need to know when a Reflex method is called, but you're working outside of the Stimulus controller that initiated it, you can subscribe to receive DOM events.

DOM events are limited to the generic lifecycle; developers can obtain information about which Reflex methods were called by inspecting the detail object when the event is captured.

Events are dispatched on the same element that triggered the Reflex. Events bubble but cannot be cancelled.

#### Event Names

* `stimulus-reflex:before`
* `stimulus-reflex:success`
* `stimulus-reflex:error`
* `stimulus-reflex:after`

#### Event Metadata

When an event is captured, you can obtain all of the data required to respond to a Reflex action:

```javascript
document.addEventListener('stimulus-reflex:before', event => {
  event.target // the element that triggered the Reflex (may not be the same as controller.element)
  event.detail.reflex // the name of the invoked Reflex
  event.detail.controller // the controller that invoked the stimuluate method
})
```

`event.target` is a reference to the element that triggered the Reflex, and `event.detail.controller` is a reference to the instance of the controller that called the `stimulate` method. This is especially handy if you have multiple instances of a controller on your page.

{% hint style="info" %}
Knowing which element dispatched the event might appear daunting, but the key is in knowing how the Reflex was created. If a Reflex is declared using a `data-reflex` attribute in your HTML, the event will be emitted by the element with the attribute.

If you're calling the `stimulate` method inside of a Stimulus controller, the event will be emitted by the element the `data-controller` attribute is declared on.
{% endhint %}

### Promises

Are you a hardcore Javascript developer? Then you'll be pleased to know that in addition to lifecycle methods and events, StimulusReflex allows you to write promise resolver functions:

```javascript
this.stimulate('MyReflex#action')
  .then(() => this.doSomething())
  .catch(() => this.handleError())
```

You can get a sense of the possibilities:

```javascript
this.stimulate('MyReflex#example')
  .then(payload => {
    const { data, element, event } = payload
    const { attrs, reflexId } = data
    // * attrs - an object that represents the attributes of the element that triggered the reflex
    // * data - the data sent from the client to the server over the web socket to invoke the reflex
    // * element - the element that triggered the reflex
    // * event - the source event
    // * reflexId - a unique identifier for this specific reflex invocation
  })
  .catch(payload => {
    const { data, element, event } = payload
    const { attrs, reflexId } = data
    const { error } = event.detail.stimulusReflex
    // * attrs - an object that represents the attributes of the element that triggered the reflex
    // * data - the data sent from the client to the server over the web socket to invoke the reflex
    // * element - the element that triggered the reflex
    // * error - the error message from the server
    // * event - the source event
    // * reflexId - a unique identifier for this specific reflex invocation
  })
```


# Scoping

How to restrict DOM updates

By default, the JavaScript library StimulusReflex updates your entire page. It uses the amazing [`morphdom`](https://github.com/patrick-steele-idem/morphdom) library to do the smallest number of DOM modifications necessary to refresh your UI in just a few milliseconds. For many developers, this will be a perfect solution and they can stop reading here.

Some applications are more sophisticated. You might want to think of your site in terms of components, or you might need to interact with legacy JavaScript plugins on your page that don't play nicely with modern techniques. Heck, you might just need to make sure we don't reload the same third-party ad tracker every time someone clicks a button.

Great news: we have you covered.

## Partial DOM updates

Instead of updating your entire page, you can specify exactly which parts of the DOM will be updated using the `data-reflex-root` attribute.

`data-reflex-root=".class, #id, [attribute]"`

Simply pass a comma-delimited list of CSS selectors. Each selector will retrieve one DOM element; if there are no elements that match, the selector will be ignored.

StimulusReflex will decide which element's children to replace by evaluating three criteria in order:

1. Is there a `data-reflex-root` on the element with the `data-reflex`?
2. Is there a `data-reflex-root` on an ancestor element with a `data-controller` above the element in the DOM? It could be the element's immediate parent, but it doesn't have to be.
3. Just use the `body` element.

Here is a simple example: the user is presented with a text box. Anything they type into the text box will be echoed back in two div elements, forward and backward.

{% tabs %}
{% tab title="index.html" %}

```
<div data-controller="example" data-reflex-root="[forward],[backward]">
  <input type="text" value="{{ words }}" data-reflex="keyup->ExampleReflex#words">
  <div forward>{{ words }}</div>
  <!-- provided there you have created a template tag that handles the reverse scenario -->
  <div backward>{{ words|reverse }}</div>
</div>
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="example\_reflex.py" %}

```python
class ExampleReflex(Reflex):
  def words():
    self.words = element['value']
    self.reversed = element['value'][::-1]
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
One interesting detail of this example is that by assigning the root to `[forward],[backward]` we are implicitly telling StimulusReflex to **not** update the text input itself. This prevents resetting the input value while the user is typing.
{% endhint %}

{% hint style="warning" %}
In StimulusReflex, `morphdom` is called with the **childrenOnly** flag set to *true*.

This means that `<body>` or the custom parent selector(s) you specify are not updated. For this reason, it's necessary to wrap anything you need to be updated in a `<div>`, `<span>`, or other bounding tag so that it can be swapped out without confusion.

If you're stuck with an element that just won't update, make sure that you're not attempting to update the attributes on an `<a>`.
{% endhint %}

{% hint style="info" %}
It's completely valid for an element with a `data-reflex-root` attribute to reference itself via a CSS class or other mechanism. Just always remember that the parent itself will not be replaced! Only the children of the parent are modified.
{% endhint %}

## Persisting Elements

Perhaps you just don't want a section of your DOM to be updated by StimulusReflex, even if you're using the full document body default.

Just add `data-reflex-permanent` to any element in your DOM, and it will be left unchanged.

{% code title="index.html" %}

```markup
<div data-reflex-permanent>
  <iframe src="https://ghbtns.com/github-btn.html?user=hopsoft&repo=stimulus_reflex&type=star&count=true" frameborder="0" scrolling="0" class="ghbtn"></iframe>
  <iframe src="https://ghbtns.com/github-btn.html?user=hopsoft&repo=stimulus_reflex&type=fork&count=true" frameborder="0" scrolling="0" class="ghbtn"></iframe>
</div>
```

{% endcode %}

{% hint style="warning" %}
This is especially important for third-party elements such as ad tracking scripts, Google Analytics, and any other widget that renders itself such as a React component or legacy jQuery plugin.
{% endhint %}

## Single Source of Truth

While stateless form submissions have technically always suffered from the "last update wins" problem, it's only in recent years that developers have created interfaces that need to respond to changing application state in real-time.

There are a few guiding principles that we adhere to when building a technology that can change the page you're on, even while you busy working on something important. The most important consideration is that even though Sockpuppet applications persist state on the server, the client should be the single source of truth for the text input element that has active focus.

Put differently: **the server should never update the value of a text box while you're typing into it**.

We've worked really hard to make sure that developers can update other aspects of the active text input element. For example, it's possible to change the background color or even mark the element as disables while you're typing into it. However, all attempts to overwrite the input element's value will be silently suppressed.

If you need to filter or constrain the contents of a text input, consider using a client-side library such as [Cleave.js](https://nosir.github.io/cleave.js/) instead of trying to circumvent the Single Source of Truth mechanisms, which are there to protect your users from their fellow collaborators.

Note that this concept only applies to the active text input element. Any elements which are marked with `data-reflex-permanent` will not be morphed in any way.


# Persistence

noun: firm or obstinate continuance in a course of action in spite of difficulty or opposition

We estimate that 80% of the pain points in web development are the direct result of maintaining state on the client. Even without considering the complexity of frameworks like React, how much time have you lost to fretting about model validation, stale data, and DOM readiness over your career?

#### Sockpuppet applications don't have a client state.\*

> \* This is *at least* 98% true.

Imagine if you could focus almost all of your time and attention on the fun parts of web development again. Exploring the best way to implement features instead of worrying about data serialization and forgotten user flows. Smaller teams working smarter and faster, then going home on time.

Designing applications in the Sockpuppet mindset is far simpler than what we're used to, and we don't have to give up responsive client functionality to see our productivity shoot through the roof. It does, however, require some unlearning of old habits. You're about to rethink how you approach persisting the state of your application. This can be jarring at first! Even positive changes feel like work.

### The life of a Reflex

When you access a page in a Sockpuppet application, you see the current state of your user interface for that URL. There is no mounting process and no fetching of JSON from an API. Your request goes through the URL router to your Django view where it renders the template and sends HTML to the browser. This is Django in all its server-rendered glory.

Only once the HTML page is displayed in your browser, the JavaScript library StimulusReflex wakes up. First, it opens a websocket connection and waits for messages. Then it scans your DOM for elements with `data-reflex` attributes. Those attributes become event handlers that map to methods in Stimulus controllers. The controllers connect events in your browser to methods in your Reflex classes on the server.

In a Reflex method, you can call the Django ORM, access data from Redis or sessions, and set instance variables that get picked up in your view. After the Reflex method is complete, the Django view is executed and any instance variables set on the Reflex will be passed onto the view's context.

We find that people learn how to work with Sockpuppet quickly when they are pushed in the right direction. The order of operations can seem fuzzy until the light bulb flicks on.

This document is here to get you to the light bulb moment quickly.

{% hint style="danger" %}
Sockpuppet only works with class-based views and expects the method `get_context_data` to exist on the view.

As Sockpuppet re-renders the view during the reflex phase, it's important to consider caching the view correctly. Otherwise queries in the view will be executed again, which will decrease performance.
{% endhint %}

## Instance Variables

One of the most common patterns in Sockpuppet is to pass instance variables from the Reflex method to the view, which then get rendered in the template. Sockpuppet will do this for you automatically as long as it can call `get_context_data` (you don't need to implement it unless you desire special behavior, however; the class-based views you'll be using include `ContextMixin`).

{% tabs %}
{% tab title="example\_reflex.py" %}

```python
def updateValue
  self.value = self.element.attributes['value']
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="example\_view\.py" %}

```python
def get_context_data(self, *args, **kwargs):
    context = super().get_context_data(*args, **kwargs)
    context['value'] = 0
    return context
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="index.html" %}

```markup
<div data-controller="example">
  <input type="text" data-reflex-permanent
    data-reflex="input->ExampleReflex#updateValue">
  <p>The value is: {{ value }}.</p>
</div>
```

{% endtab %}
{% endtabs %}

When you access the index page, the value will initially be set to 0. If the user changes the value of the text input, the value is updated to reflect whatever has been typed. This is possible because reflex data takes precedence over view data.

{% hint style="success" %}
Sockpuppet doesn't need to go through Django routing. This means updates are processed much faster than requests that come from typing in a URL or refreshing the page.
{% endhint %}

Of course, instance variables are aptly named; they only exist for the duration of a single request, regardless of whether that request is initiated by accessing a URL or clicking a button managed by StimulusReflex.

### The stimulus\_reflex context variable

When Sockpuppet calls your Django view, it passes any active instance variables along with a special context variable called `stimulus_reflex`, which is set to `true`. **You can use this context variable to create an if/else block in your template or view that behaves differently depending on whether it's being called within the context of a Reflex update or not.**

{% tabs %}
{% tab title="pinball\_view\.py" %}

```python
def get_context_data(self, *args, **kwargs):
    context = super().get_context_data(*args, **kwargs)
    if not context.get('stimulus_reflex'):
        self.request.session['balls_left'] = 3
    return context
```

{% endtab %}
{% endtabs %}

In this example, the user is given three new balls every time they refresh the page in their browser, effectively restarting the game. If the page state is updated via the Sockpuppet Reflex, no new balls are allocated.

Since the `stimulus_reflex` variable is only available during the reflex phase and *not* when executing the view normally you'll have to use `context.get`, otherwise you'll get an error.

This also means that `self.request.session['balls_left']` will be set to 3 before the initial HTML page has been rendered and transmitted.

{% hint style="success" %}
**The first time the view action executes is your opportunity to set up the state that Sockpuppet will later modify.**
{% endhint %}

## The Django session object

The `session` object will persist across multiple requests; indeed, you can open multiple browser tabs and they will all share the same `session.session_key` value on the server. See for yourself: you can create a new session using Incognito Mode or using a second web browser.

We can update our earlier example to use the session object, and it will now persist across multiple browser tabs and refreshes:

{% tabs %}
{% tab title="example\_reflex.py" %}

```python
def update_value(self):
    self.request.session['value'] = self.element['value']
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="example\_view\.py" %}

```python
def get(self, *args, **kwargs):
    context = self.get_context_data()
    context['value'] = self.request.session['value'] = 0
    return render_to_response(...)
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="index.html" %}

```markup
<div>
  <input type="text" data-reflex-permanent
    data-reflex="input->ExampleReflex#updateValue">
  <p>The value is: {{ value }}.</p>
</div>
```

{% endtab %}
{% endtabs %}


# Useful Patterns

How to build a great StimulusReflex application

In the course of creating Sockpuppet and using it to build applications, we have discovered several useful tricks. While it may be tempting to add features to the core library, every idea that we include creates bloat and comes with the risk of stepping on someone's toes because we didn't anticipate all of the ways it could be used.

## Client Side

### Application controller

You can make use of JavaScript's class inheritance to set up an Application controller that will serve as the foundation for all of your StimulusReflex controllers to build upon. This not only reduces boilerplate, but it's also a convenient way to set up lifecycle callback methods for your entire application.

{% tabs %}
{% tab title="application\_controller.js" %}

```javascript
import { Controller } from 'stimulus'
import StimulusReflex from 'stimulus_reflex'

export default class extends Controller {
  connect () {
    StimulusReflex.register(this)
  }

  sayHi () {
    console.log('Hello from the Application controller.')
  }
}
```

{% endtab %}
{% endtabs %}

Then, all that's required to create a StimulusReflex controller is inherit from ApplicationController:

{% tabs %}
{% tab title="custom\_controller.js" %}

```javascript
import ApplicationController from './application_controller'

export default class extends ApplicationController {
  sayHi () {
    super.sayHi()
    console.log('Hello from a Custom controller')
  }
}
```

{% endtab %}
{% endtabs %}

If you need to override any methods on your Application controller, you can redefine them. Optionally call `super.sayHi(...Array.from(arguments))` to invoke the method on the parent super class.

### Benchmarking your Reflex actions

You might want to see how long your Reflex actions are taking to complete a round-trip, and without Ajax calls to monitor getting reliable metrics requires new approaches.

We suggest making use of the `beforeReflex` and `afterReflex` lifecycle callback methods to sample your performance. As a rule of thumb, anything below 200-300ms will be perceived as "native" by your users.

You can add this code to your desired Reflex controller. If you're making use of the ApplicationController pattern described above, all of your Reflexes will log their round-trip execution times.

{% tabs %}
{% tab title="application\_controller.js" %}

```javascript
  beforeReflex () {
    this.benchmark = performance.now()
  }

  afterReflex (element, reflex) {
    console.log(reflex, `${(performance.now() - this.benchmark).toFixed(0)}ms`)
  }
```

{% endtab %}
{% endtabs %}

### Spinners for long-running actions

You can use `beforeReflex` and `afterReflex` to create UI spinners for anything that might take more than a heartbeat to complete. In addition to providing helpful visual feedback, research has demonstrated that acknowledging a slight delay will result in the user *perceiving* the delay as being shorter than they would if you did not acknowledge the delay. This is likely because we've been trained by good UI design to understand that this convention means we're waiting on the system. A sluggish UI otherwise forces people to wonder if they have done something wrong, and you don't want that.

{% tabs %}
{% tab title="application\_controller.js" %}

```javascript
  beforeReflex () {
    document.body.classList.add('wait')
  }

  afterReflex () {
    document.body.classList.remove('wait')
  }
```

{% endtab %}

{% tab title="application.css" %}

```css
body.wait, body.wait * {
  cursor: wait !important;
}
```

{% endtab %}
{% endtabs %}

### Autofocus text boxes

If you are working with input elements in your application, you will quickly realize an unfortunate quirk of web browsers is that the `autofocus` attribute is only processed on the initial page load. If you want to implement a "click to edit" UI, you need to use a lifecycle callback method to make sure that the focus lands in the right place.

Handling this problem for every action would be extremely tedious. Luckily we can make use of the `afterReflex` callback to inspect the element to see if it has the `autofocus` attribute and, if so, correctly set the focus on that element.

{% tabs %}
{% tab title="application\_controller.js" %}

```javascript
  afterReflex () {
    const focusElement = this.element.querySelector('[autofocus]')
    if (focusElement) {
      focusElement.focus()

      // shenanigans to ensure that the cursor is placed at the end of the existing value
      const value = focusElement.value
      focusElement.value = ''
      focusElement.value = value
    }
  }
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
Note that to obtain our **focusElement**, we looked for a single instance of `autofocus` on an element that is a child of our controller. We used **this.element** where **this** is a reference to the Stimulus controller.

If we wanted to only check the element that triggered the Reflex action, we would modify our **afterReflex ()** to **afterReflex(element)** and then call **element.querySelector** - or just check the attributes directly.

If we wanted to check the whole page for an **autofocus** attribute, we can just use **document.querySelector('\[autofocus]')** as usual. The square-bracket notation just tells your browser to look for an attribute called **autofocus**, regardless of whether it has a value or not.
{% endhint %}

### Offering visual feedback

We recommend [Velocity](https://github.com/julianshapiro/velocity/wiki) for light, tweening animations that alert the user to UI state changes.

### Capture all DOM update events

Stimulus provides a really powerful event routing syntax that includes custom events, specifying multiple events and capturing events on **document** and **window**.

```markup
<div data-action="cable-ready:after-morph@document->chat#scroll">
```

By capturing the **cable-ready:after-morph** event, we can run code after every update from the server. In this example, the scroll method on our Chat controller is being called to scroll the content window to the bottom, displaying new messages.

### Capture jQuery events with DOM event listeners

Don't hate jQuery: it was a life-saver 12 years ago, and many of its best ideas are now part of the JavaScript language. However, one of the uglier realities of jQuery in a contemporary context is that it has its' own entirely proprietary system for managing events, and it's not compatible with the now-standard DOM Events API.

Sometimes you still need to be able to interface with legacy components, but you don't want to have to write two event handling systems.

[jquery-events-to-dom-events](https://www.npmjs.com/package/jquery-events-to-dom-events) is an npm package that lets you easily access and respond to jQuery events.

### Access Stimulus controller instances

Stimulus doesn't provide an easy way to access a controller instance; you have to have access to your Stimulus application object, the element, the name of the controller and be willing to call an undocumented API.

```javascript
this.application.getControllerForElementAndIdentifier(document.getElementById('users'), 'users')
```

This is ugly, verbose and potentially impossible outside of another Stimulus controller. Wouldn't it be nice to access your controller's methods and local variables from a legacy jQuery component? Just add this line to the **initialize()** method of your Stimulus controllers:

```javascript
this.element[this.identifier] = this
```

This creates a document-scoped variable with the same name as your controller (or controllers!) on the element itself, so you can now call **element.controllerName.method()** without any Pilates required. You can read more about this technique [here](https://leastbad.com/stimulus-power-move).

{% hint style="warning" %}
If your controller's identifier doesn't obey the rules of JavaScript variable naming conventions, you will need to specify a viable name for your instance.

For example, if your controller is named *list-item* you might consider **this.element.listItem = this** for that controlle&#x72;**.**
{% endhint %}

## Server-Side

### Rendering views inside of a Django model

If you plan to broadcast an update of an html template from somewhere outside a reflex you can draw from the example below.

**The following isn't a complete working example**, but it should set you on the right path.

```python
from django.template.loader import render_to_string
from sockpuppet.channel import Channel


class Notification(models.Model):
    def save(self, *args, **kwargs):
        result = super().save(*args, **kwargs)
        html = render_to_string('my_template.html', {'foo': 'bar'})

        user_session_key = ... # get the user session somehow
        channel = Channel(user_session_key)
        channel.insert_adjacent_html({
            'selector': '#notification_dropdown',
            'position': 'afterbegin',
            'html': html
        })
        channel.broadcast()
```

### Triggering custom events and forcing DOM updates

You can trigger out of band updates with the `Channel` class, it is the workhorse behind Sockpuppet. Take a look at the [source code](https://github.com/jonathan-s/django-sockpuppet/blob/master/sockpuppet/channel.py) to learn more about what kind of updates you can do.

One of the things you can do is to dispatch an event. You can do that with the method `dispatch_event`, which allows you to trigger any event in the client, including custom events and jQuery events.

{% tabs %}
{% tab title="Python" %}

```python
from sockpuppet.reflex import Reflex
from sockpuppet.channel import Channel

class NotificationReflex(Reflex):

    def force_update(id)
        channel = Channel(self.consumer.scope['session'].session_key)
        channel.dispatch_event({
            name: "force:update",
            detail: {id: id},
        })
        channel.broadcast()

    def reload(self):
        # noop: this method exists so we can refresh the DOM
        pass
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="index.html" %}

```markup
<div data-action="force:update@document->notification#reload">
  <button data-action="notification#forceUpdate">
</div>
```

{% endtab %}
{% endtabs %}

We use the Stimulus event mapper to call our controller's reload method whenever a force:update event is received:

{% tabs %}
{% tab title="notification\_controller.js" %}

```javascript
let lastId

export default class extends Controller {
  forceUpdate () {
    lastId = Math.random()
    this.stimulate("NotificationReflex#force_update", lastId)
  }

  reload (event) {
    const { id } = event.detail
    if (id === lastId) return
    this.stimulate("NotificationReflex#reload")
  }
}
```

{% endtab %}
{% endtabs %}

By passing a randomized number to the Reflex as an argument, we allow ourselves to return before triggering a reload if we were the ones that initiated the operation.

#### Coming Soon: Notifications

## Anti-Patterns

#### Coming Soon: How to change the URL rendered by a reflex


# Working with Events

the JavaScript library StimulusReflex rocks because it stands on the shoulders of Stimulus

It's become progressively easier to work with events in a consistent way across all web browsers. There are still gotchas and awkward idiosyncrasies that would make Larry David squirm, but compared to the bad old days of IE6 - long a *nevergreen* browser default on Windows - there's usually a correct answer to most problems.

The team behind [StimulusReflex](https://docs.stimulusreflex.com) works hard to make sure that the JavaScript library has everything it needs to present a favorable alternative to using SPAs. They are also opinionated about what StimulusReflex shouldn't take on, and those decisions reflect some of the biggest differences from other solutions such as [LiveView](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#module-key-events).

A big part of the reason they can keep the footprint of the JavaScript library so small without sacrificing functionality is that it is tightly integrated with [Stimulus](https://stimulusjs.org), a lightweight library that provides powerful event handling.

They also draw upon proven libraries such as [Lodash](https://lodash.com) when necessary to craft flexible solutions to common problems.

## Throttle, Debounce and requestAnimationFrame

Some actions with some input devices can trigger enough events in a short period of time that unless you handle them properly, you will massively degrade the performance of your application. Common examples include: moving your mouse, holding down a key on your keyboard, scrolling a webpage and resizing your browser window.

For these use cases, we use a technique known as a **throttle**. A throttle accepts a stream of events and after allowing the first one to execute immediately, it will discard further events until a specified delay has passed.

> If you have a delay of 1000ms and send three events in rapid succession, it will fire the first event, wait one second and then fire the third event.

Other times, you might just want to exercise fine control over exactly when some events are allowed to fire. The most common example is the delayed suggested results you see on sites like Google as you type characters into the search box. Your goal is to hold back events until enough time has passed since the last event has been received.

For these use cases, we can use a technique known as a **debounce**. The classic mental model is holding open the elevator door for people to board. The elevator can't leave until a few seconds after you let go of the button.

Debounce is flexible. In addition to specifying a delay, additional options can indicate whether the first ("*leading*") event is fired and whether the last ("*trailing*") event is fired. Much like an angry, beeping elevator there is also *maxWait* to provide the amount of time to wait before an interim event is fired, even if new events are still arriving.

{% hint style="info" %}
**debounce** is so flexible that the Lodash implementation of throttle is actually implemented using debounce.
{% endhint %}

{% hint style="success" %}
LiveView's **debounce** implementation accepts **blur** as a delay value, effectively saying "don't do this until the user leaves this input element".

With Stimulus, we can just define a handler for the **blur** event and keep the concepts separate.
{% endhint %}

While you can find many implementations of throttle and debounce on the web, we strongly recommend that you use the functions found in the Lodash library. Not only are they are flexible, well-tested and optimised, but *they actually return new functions that you can assign to replace your existing functions*. Once you wrap your head around the power this provides, other approaches will feel like dirty hacks.

If you `yarn add lodash-es` you will be able to use a version of the library that supports **tree shaking**. This means that Webpack will only grab the minimum code required, keeping your production JS bundle size tiny.

Let's set up a simple example: we will debounce your page scroll events while keeping your server up-to-date on how far down your user is.

{% tabs %}
{% tab title="event\_controller.js" %}

```javascript
import { Controller } from 'stimulus'
import StimulusReflex from 'stimulus_reflex'
import { debounce } from 'lodash-es'

export default class extends Controller {
  connect () {
    StimulusReflex.register(this)
    this.scroll = debounce(this.scroll, 1000)
  }

  scroll () {
    this.stimulate('EventReflex#scroll', window.scrollY)
  }
}
```

{% endtab %}

{% tab title="event\_reflex.py" %}

```python
class EventReflex(Reflex):
  def scroll(value):
      # do something here, you've got the value on far it's been scrolled.
      return value
```

{% endtab %}

{% tab title="index.html" %}

```markup
<div
  data-controller="event"
  data-action="scroll@window->event#scroll"
  style="height: 5000px"
></div>
```

{% endtab %}
{% endtabs %}

We can use the [Stimulus Global Events](https://stimulusjs.org/reference/actions#global-events) syntax to map window scroll events to the `scroll` function on a Stimulus controller named `event`. When the controller is attached to the `div` at page load, `connect` is fired, StimulusReflex is instantiated and we use the Lodash `debounce` [function](https://lodash.com/docs/4.17.15#debounce) to return a new event handler that will execute when the page is scrolled *but then stops scrolling for at least a second*. We could set a `maxWait` option if we were worried about users who just won't stop scrolling, but that's as weird as it sounds and qualifies as premature optimisation.

When the handler is executed, we call `stimulate` and pass the current scroll offset of the browser window to the server as an integer argument. The server reflex executes the scroll method and it can do whatever you would like it to do.

We will look at more examples below, but for now just remember that `throttle` with default parameters has the example same form and syntax as `debounce`.

{% hint style="success" %}
Just before we move on, there is a third important mechanism modern browsers provide to control time in our applications, and that is **requestAnimationFrame**.

If you've ever developed games, simulations or visualisations, chances are that you've worked with *render loops*. For the rest of us, the idea that we can use JavaScript, WebGL and the HTML canvas/SVG elements to create incredible visual results might seem alien. There are many great starter articles including "[Anatomy of a video game](https://developer.mozilla.org/en-US/docs/Games/Anatomy)" on MDN.

**requestAnimationFrame** is the mechanism used to control screen draw operations. When paired with **keydown** and mouse/touch events, complete games with GPU-accelerated graphics are possible. New browser APIs such as [HTML5 Bluetooth](https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web) mean that you could use your Xbox controllers.

What might come as a surprise is that clever use of **StimulusReflex is theoretically fast enough to keep your game state running live on the server while your client is updating at 60fps**. We leave this as an exercise for the reader, but please tell us if you achieve cold fusion.
{% endhint %}

## The Four Horsemen aka Key Events

We're going to quickly cover the four primary key-capture events available to the modern JavaScript developer. While they all have their uses, it's quite likely that you're going to stick to one or two of them.

The *key* thing to remember is that `keydown` and `keyup` indicate which key is **pressed**, while `keypress` indicates which **character was entered**. A lowercase "a" will be reported as 65 by `keydown` and `keyup`, but as 97 by `keypress`. An uppercase "A" is reported as 65 by all events.

`keydown`, `keypress` and `keyup` can be declared on any receiver including `document`. The `input` event can only be captured for an `input`, `select` or `textarea` HTML element. Choose the right event depending on your needs.

### keydown

The lowest-level key capture events are also the only events that can pick up control characters; if you need to know that they are holding down control or even just holding down `w` to move forward, this is your event.

If you press the Escape key, this is the granularity of data you can obtain:

| key      | value    |
| -------- | -------- |
| altKey   | false    |
| charCode | 0        |
| code     | "Escape" |
| ctrlKey  | false    |
| key      | "Escape" |
| keyCode  | 27       |
| location | 0        |
| metaKey  | false    |
| repeat   | false    |
| shiftKey | false    |
| which    | 27       |

While very useful for game development, it doesn't see a lot of use in normal web development because if you access `event.target.value` it gives you the value of the element (usually a text box) **before the key was pressed**. Many developers have lost many hairs trying to hunt down bugs on their `keydown` handlers; don't make the same mistake.

It's common to throttle the rate of events fired when the user holds down a key. In the examples below, we'll look at how to throttle on `keydown` by testing the `repeat` attribute to see if one key is being held down.

### keypress

Similar to `keydown`, `keypress` returns the previous value when you access `event.target.value`. However, it only fires for keys that product a character value, so for example the Escape key is off-limits, as are `Alt`, `Shift`, `Ctrl` and `Meta`.

Here's the event data obtained by pressing `w`one time:

| key      | value  |
| -------- | ------ |
| altKey   | false  |
| charCode | 119    |
| code     | "KeyW" |
| ctrlKey  | false  |
| key      | "w"    |
| keyCode  | 119    |
| location | 0      |
| metaKey  | false  |
| repeat   | false  |
| shiftKey | false  |
| which    | 119    |

{% hint style="warning" %}
Note that the `keypress` event is technically deprecated even if it's still widely used.
{% endhint %}

### keyup

While `keyup` is the direct counterpart of `keydown` there are some important differences.

Throttling or debouncing is usually *not* required as the event doesn't fire until the key has been released.

`event.target.value` returns the value of the text box as it currently appears, with any new changes reflected.

`keyup` will *not* fire if you paste text into an input element. It doesn't care that anything has changed; it's *only* aware of keys being pressed.

### input

Introduced in 1999, the new member of the key event family wasn't available in IE until version 9.

A close cousin of `change` and `blur`, `input` events can be used to manage the state of `input`, `textarea` and `select` elements. `input` is fired every time the `value` of the element changes, including when text is pasted. `change` only fires when the `value` is committed, such as by pressing the enter key or selecting a value from a list of options. `blur` fires when focus is lost, *even if nothing changed*.

Like `keypress`, `input` cannot give you access to non-character keycodes such as Escape. It should not require debounce because the event is not fired until after any change has occurred. You can access `event.target.value` and see the current value of the element.

However, the real power of `input` (and it's sister event `beforeinput`) is that they give you **boss powers**: the `data` attribute on the event is a string containing the change made, which could be a single character or a pasted novel. Meanwhile, the `inputType` attribute tells you what kind of change was responsible for the event being fired. With this information, you have the ability to create a timeline log of all changes to a document and even replay them in either direction later.

Getting into the details of how `contenteditable` works is far beyond the scope of this document, but you can find more information on what's possible in the [W3C Input Events spec](https://www.w3.org/TR/input-events-1/#interface-InputEvent-Attributes).

You might also consider checking out [Trix](https://trix-editor.org/), the editor library created by the team behind Rails, Stimulus, TurboLinks and ActionCable.

## Real-world examples

### keydown throttle

First, let's tackle a creative use of `throttle`. We're going to allow the user to mash their keyboard without spamming the server with Reflex updates. However, **we only want to throttle if they are holding down a single key**:

{% tabs %}
{% tab title="event\_controller.js" %}

```javascript
import { Controller } from 'stimulus'
import StimulusReflex from 'stimulus_reflex'
import { throttle } from 'lodash-es'

export default class extends Controller {
  connect () {
    StimulusReflex.register(this)
    this.throttleKeydown = throttle(this.throttleKeydown, 1000)
  }

  keydown (event) {
    event.repeat
      ? this.throttleKeydown(event)
      : this.stimulate('EventReflex#keydown', event.key)
  }

  throttleKeydown (event) {
    this.stimulate('EventReflex#keydown', event.key)
  }
}
```

{% endtab %}

{% tab title="event\_reflex.py" %}

```python
class EventReflex(Reflex):
  def keydown(key)
    # do something with key press
    return key
```

{% endtab %}

{% tab title="index.html" %}

```markup
<div data-controller="event">
  <input type="text" data-action="keydown->event#keydown">
</div>
```

{% endtab %}
{% endtabs %}


# Working with Forms

Forms fly business class on StimulusReflex Airways ✈️

## Single source of truth

While stateless form submissions have technically always suffered from the "last update wins" problem, it's only in recent years that developers have created interfaces that need to respond to changing application state in real-time.

There are a few guiding principles that we adhere to when building a technology that can change the page you're on, even while you're busy working on something important. One of the biggest wins associated with keeping the web server as the single source of truth about the state of your application and its data is that you don't have to worry about the synchronization of state with the client. Whatever you see on your screen is the same thing that you would see if you hit refresh. This makes developing applications with django-sockpuppet faster and significantly less complicated than equivalent solutions which make use of SPAs like React.

However, **django-sockpuppet will never overwrite the value of a text input or textarea element if it has active focus in your browser**. This exception is important because there's no compelling UI experience where you want to change the contents of an input element *while the user is typing into it*.

We've worked really hard to make sure that developers can update other aspects of the active text input element. For example, it's possible to change the background color or even mark the element as disabled while you're typing into it. However, all attempts to overwrite the input element's value will be silently suppressed.

If you need to filter or constrain the contents of a text input, consider using a client-side library such as [Cleave.js](https://nosir.github.io/cleave.js/) instead of trying to circumvent the Single Source of Truth mechanisms, which are there to protect your users from their fellow collaborators.

Note that this concept only applies to the active text input element. Any elements which are marked with `data-reflex-permanent` will not be morphed in any way.

## Form submission

Django-sockpuppet gathers all of the attributes on the element that initiates a Reflex. All of this data gets packed into an object that is made available to your Reflex action method through the `element` accessor. You can even [scoop up the attributes of parent elements](https://sockpuppet.argpar.se/reflexes#inheriting-data-attributes-from-parent-elements). This leaves form submission in the cold, though... doesn't it? 🥶

### The `params` accessor

*Heck no!* If a Reflex is called on a `form` element - or a **child** of that `form` element - then the data for the whole form will be properly serialized and made available to the Reflex action method as the `params` accessor. `params` is a dictionary that you can input into a django form as data, and then validate that data as you normally would.

One of the most exciting benefits of this design is that autosaving the data in your form becomes as simple as adding `data-reflex="change->Post#update"` to each field. Since the field is inside the parent `form` element, all inputs are automatically serialized and sent to your Reflex class.

You are free to add additional business logic on the client using the Reflex [lifecycle callbacks](https://sockpuppet.argpar.se/lifecycle) in your Stimulus controllers.

Reflex actions called outside of a form will still have a `params` instance variable; it will be an empty dictionary.

{% hint style="danger" %}
If you call a full-page update Reflex outside of a form that has unsaved data, you will lose the data in the form. You will also lose the data if you throw your laptop into a volcano. 🌋
{% endhint %}

#### Modifying form data before sending to the server

Should you need to modify the contents of your params before the Reflex sends the data to the server, you can use the `before` callbacks to do so:

```javascript
document.addEventListener('stimulus-reflex:before', event => {
  const { params } = event.target.reflexData
  event.target.reflexData.params = { ...params, foo: true, bar: false }
})
```

#### A note about \<input type="file"> fields

At the time of this writing, **forms that upload files are unsupported by django-sockpuppet**. We suggest that you design your UI in such a way that files can be uploaded directly, making use of the standard django-form upload techniques. You might need to use `data-reflex-permanent` so that you don't lose UI state when a Reflex is triggered.

As websockets is a text-based protocol that doesn't guarantee packet delivery or the order of packet arrival, it is not well-suited to uploading binary files. This is an example of a problem best solved with vanilla Django.

#### Resetting a Submitted Form

If you submit a form via django-sockpuppet, and the resulting DOM diff doesn't touch the form, you will end up with stale data in your form `<input>` fields. You're going to need to clear your form so the user can add more data.

One simple technique is to use a Stimulus controller to reset the form after the Reflex completes successfully. We'll call this controller `reflex-form` and we'll use it to set a target on the first text field, as well as an action on the submit button:

```markup
<form data-controller="form">
    <input type="text" data-form-target="focus">
    <button data-action="click->form#submit"></button>
</form>
```

This controller will make use of the [Promise](https://sockpuppet.argpar.se/lifecycle#promises) returned by the `stimulate` method:

```javascript
// my_app/javascript/form_controller.js
import { Controller } from 'stimulus';

export default class extends Controller {
  static targets = ['focus']
  submit (e) {
    e.preventDefault()
    this.stimulate(this.data.get('reflex')).then(() => {
      this.element.reset()
      // optional: set focus on the freshly cleared input
      this.focusTarget.focus()
    })
  }
}
```


# Security & Authentication

What to keep in mind when safe-guarding your applications.

## Authentication

When a reflex is executed you will have access to the underlying session, and as such you will be able to tell whether the user is authenticated or not. Even if the user is not logged it will work as expected.

{% tabs %}
{% tab %}

```python
from sockpuppet.reflex import Reflex

class ExampleReflex(Reflex):
    def check_auth(self):
        user = self.request.user

        if user.is_authenticated:
            # Here you could add a variable that shows that the user is
            # authenticated, or perform other lookups or whatever need be.
        else if user.is_anonymous:
            # Based on this case you could take other measures that changes
            # the context.
```

{% endtab %}
{% endtabs %}

Above you can see an example of what the reflex could look like. Once the `check_auth` method of the reflex method is called from the frontend the template will be updated according to your logic in the reflex.

## Security

If the website uses https, it will be using a secure websocket, another concern when it comes to security are cross-site request forgery (CSRF). You can read more about how this works for [django-channels](https://channels.readthedocs.io/en/stable/topics/security.html).

By default django-sockpuppet is using `AllowedHostsOriginValidator` which means that a websocket can only be opened from the same domains in `ALLOWED_HOSTS`.

In the [setup](https://sockpuppet.argpar.se/setup-django) stage you defined `ASGI_APPLICATION` to `sockpuppet.routing.application`. So if you need to *not* use the origin validator for any reason you'll need to create a routing file of your own and update the `settings.py` file to reflect that.

If you create your own routing, the only thing to keep in mind is that the javascript expects that the path to the websocket is `/ws/sockpuppet-sync`.


# Troubleshooting

![](https://cdn.vox-cdn.com/thumbor/2q97YCXcLOlkoR2jKKEMQ-wkG9k=/0x0:900x500/1200x800/filters:focal%28378x178:522x322%29/cdn.vox-cdn.com/uploads/chorus_image/image/49493993/this-is-fine.0.jpg)

## Logging

### Client-Side

You might want to know the order in which your Reflexes are called, how long it took to process each Reflex or what the Reflex response payload contains. Luckily you can enable Reflex logging to your browser's Console Inspector.

![](/files/-M7RckRhejMnEOZLhAm6)

There are two ways to enable client debugging in your StimulusReflex instance.

You can provide `debug: true` to the initialize options like this:

{% code title="app/javascript/controllers/index.js" %}

```javascript
StimulusReflex.initialize(application, { consumer, debug: true })
```

{% endcode %}

You can also set debug mode after you've initialized StimulusReflex. This is especially useful if you just want to log the Reflex calls in your development environment:

{% code title="app/javascript/controllers/index.js" %}

```javascript
StimulusReflex.initialize(application, { consumer })
if (process.env.ENVIRONMENT === 'development') StimulusReflex.debug = true
```

{% endcode %}

### Server-Side

To get debug logging for Sockpuppet you need to make some modifications to `LOGGING` in `settings.py`. Below you can see an example logging configuration that enables debug-level logging Sockpuppet.

```python
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'root': {
        'handlers': ['console'],
        'level': 'DEBUG'
    },
    'handlers': {
        'sockpuppet': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        }
    },
    'formatters': {
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'loggers': {
        'sockpuppet': {
            'level': 'DEBUG',
            'handlers': ['sockpuppet']
        }
    }
}
```


