ypergen

- take a break from javascript

Back to documentation

Client commands

When in a hypergen context commands can be sent to the frontend for execution. A command is a list where the first element is a dotted path to a javascript function available from window or in the executing script. The remaining elements are used as arguments to the function.

This command alerts the user:

["alert", "Whats up?"]

Commands lives in the hypergen.commands list in the global context. So to manually add commands one would:

from hypergen.context import context

def my_view_or_callback(request):
    context.hypergen.commands.append(["alert", "Whats up?"])

The function command() available in hypergen.liveview is normally used as a shortcut:

from hypergen.liveview import command
        
def my_view_or_callback(request):
    command("console.log", "Whats up?", [1, 2, 3])

Examples of client commands

Run a generic javascript command

It must be available in the window scope.

@action(perm=NO_PERM_REQUIRED)
def alert(request):
    command("alert", "I am lert")

Run a generic javascript command 2

Manually returns commands.

@action(perm=NO_PERM_REQUIRED)
def alert2(request):
    commands = [["alert", "This is an alert!"]]
    return HttpResponse(dumps(commands), status=200, content_type='application/json')

Serialization

Data can move a round in different ways:

  1. server->client: As arguments to the callback (cb) function on e.g. onclick events on html elements.
  2. client->server: As arguments to @action functions.
  3. server->client: As arguments to client commands.

Consider this template function:

def show_button():
    button(
        "run",
        id_="serialization",
        onclick=callback(
        serialization,
        {
        "string": "hi",
        "int": 42,
        "float": 9.9,
        "list": [1, 2, 3],
        "range": range(1, 10, 2),
        "tuple": (1, 2, 3),
        "dict": {"key": "value"},
        "set": {1, 2, 3},
        "frozenset": frozenset({1, 2, 3}),
        "date": date(2022, 1, 1),
        "datetime": datetime(2022, 1, 1, 10, 11, 23),},
        ),
    )

The most popular python data types are supported. Notice that pythons json.dumps force converts tuples to lists :(

@action(perm=NO_PERM_REQUIRED, target_id="serialized")
def serialization(request, round_tripped_data):
    write(pformat(round_tripped_data))
Press the button!

Hypergen commands

These are the commands hypergen provides, see how they are implemented at the source.

morph

Takes an id and the content to replace it with.

@action(perm=NO_PERM_REQUIRED)
def morph(request):
    command("hypergen.morph", "morphed", "MORPHED!")
Old content

Uses the great DOM diff/patching tool morphdom.

remove

Takes an id and removes it.

@action(perm=NO_PERM_REQUIRED)
def remove(request):
    command("hypergen.remove", "remove-me")
Still here

hide

Takes an id and hides it.

@action(perm=NO_PERM_REQUIRED)
def hide(request):
    command("hypergen.hide", "hide-me")
Still displayed

redirect

Takes an url and redirects to it.

@action(perm=NO_PERM_REQUIRED)
def redirect(request):
    command(
        "hypergen.redirect",
        "https://github.com/runekaagaard/django-hypergen/blob/main/src/hypergen/static/hypergen/hypergen.js#:~:text=redirect"
    )

Show sourcesactions.py
from hypergen.imports import *
from pprint import pformat
from django.http.response import HttpResponse

@action(perm=NO_PERM_REQUIRED)
def alert(request):
    command("alert", "I am lert")

@action(perm=NO_PERM_REQUIRED)
def alert2(request):
    commands = [["alert", "This is an alert!"]]
    return HttpResponse(dumps(commands), status=200, content_type='application/json')

@action(perm=NO_PERM_REQUIRED, target_id="serialized")
def serialization(request, round_tripped_data):
    write(pformat(round_tripped_data))

@action(perm=NO_PERM_REQUIRED)
def morph(request):
    command("hypergen.morph", "morphed", "MORPHED!")

@action(perm=NO_PERM_REQUIRED)
def remove(request):
    command("hypergen.remove", "remove-me")

@action(perm=NO_PERM_REQUIRED)
def hide(request):
    command("hypergen.hide", "hide-me")

@action(perm=NO_PERM_REQUIRED)
def redirect(request):
    command(
        "hypergen.redirect",
        "https://github.com/runekaagaard/django-hypergen/blob/main/src/hypergen/static/hypergen/hypergen.js#:~:text=redirect"
    )
templates.py
from hypergen.imports import *
from datetime import date, datetime
import inspect

from commands.actions import *

def fn(title, description, fun):
    if title:
        h3(title)
    if description:
        p(description)
    pre(code(inspect.getsource(fun)))

def show_button():
    button(
        "run",
        id_="serialization",
        onclick=callback(
        serialization,
        {
        "string": "hi",
        "int": 42,
        "float": 9.9,
        "list": [1, 2, 3],
        "range": range(1, 10, 2),
        "tuple": (1, 2, 3),
        "dict": {"key": "value"},
        "set": {1, 2, 3},
        "frozenset": frozenset({1, 2, 3}),
        "date": date(2022, 1, 1),
        "datetime": datetime(2022, 1, 1, 10, 11, 23),},
        ),
    )

def commands():
    h2("Client commands")
    p(
        "When in a hypergen context commands can be sent to the frontend for execution. A command is a list where the first element is a dotted path to a javascript function available from ",
        code("window"), " or in the executing script. The remaining elements are used as arguments to the function.",
        sep=" ")
    p("This command alerts the user:")
    pre(code('["alert", "Whats up?"]'))
    p("Commands lives in the", code("hypergen.commands"),
        "list in the global context. So to manually add commands one would:", sep=" ")
    pre(
        code("""
from hypergen.context import context

def my_view_or_callback(request):
    context.hypergen.commands.append(["alert", "Whats up?"])
""".strip()))

    p("The function", code("command()"), "available in hypergen.liveview is normally used as a shortcut:", sep=" ")
    pre(
        code("""
from hypergen.liveview import command
        
def my_view_or_callback(request):
    command("console.log", "Whats up?", [1, 2, 3])
    """.strip()))

    h2("Examples of client commands")
    fn("Run a generic javascript command", "It must be available in the window scope.", alert)
    button("run", id_="alert", onclick=callback(alert))

    fn("Run a generic javascript command 2", "Manually returns commands.", alert2)
    button("run", id_="alert2", onclick=callback(alert2))

    h3("Serialization")
    p("Data can move a round in different ways:")
    ol(
        li("server->client: As arguments to the callback (cb) function on e.g. onclick events on html elements."),
        li("client->server: As arguments to @action functions."),
        li("server->client: As arguments to client commands."),
    )
    fn(None, "Consider this template function:", show_button)
    fn(
        None, "The most popular python data types are supported. Notice that pythons json.dumps force converts "
        "tuples to lists :(", serialization)

    show_button()
    pre(code("Press the button!", id_="serialized"))

    h2("Hypergen commands")
    p(
        "These are the commands hypergen provides, see how they are implemented at ",
        a(
        "the source", href=
        "https://github.com/runekaagaard/django-hypergen/blob/main/src/hypergen/static/hypergen/hypergen.js#:~:text=morph"
        ), ".")

    fn("morph", "Takes an id and the content to replace it with.", morph)
    button("run", id_="morph", onclick=callback(morph))
    span("Old content", id_="morphed")
    p("Uses the great DOM diff/patching tool ", a("morphdom", href="https://github.com/patrick-steele-idem/morphdom"),
        ".")

    fn("remove", "Takes an id and removes it.", remove)
    button("run", id_="remove", onclick=callback(remove))
    span("Still here", id_="remove-me")

    fn("hide", "Takes an id and hides it.", hide)
    button("run", id_="hide", onclick=callback(hide))
    span("Still displayed", id_="hide-me")

    fn("redirect", "Takes an url and redirects to it.", redirect)
    button("run", id_="redirect", onclick=callback(redirect))
callbacks.py
from hypergen.imports import *
from pprint import pformat
from django.http.response import HttpResponse

@action(perm=NO_PERM_REQUIRED)
def alert(request):
    command("alert", "I am lert")

@action(perm=NO_PERM_REQUIRED)
def alert2(request):
    commands = [["alert", "This is an alert!"]]
    return HttpResponse(dumps(commands), status=200, content_type='application/json')

@action(perm=NO_PERM_REQUIRED, target_id="serialized")
def serialization(request, round_tripped_data):
    write(pformat(round_tripped_data))

@action(perm=NO_PERM_REQUIRED)
def morph(request):
    command("hypergen.morph", "morphed", "MORPHED!")

@action(perm=NO_PERM_REQUIRED)
def remove(request):
    command("hypergen.remove", "remove-me")

@action(perm=NO_PERM_REQUIRED)
def hide(request):
    command("hypergen.hide", "hide-me")

@action(perm=NO_PERM_REQUIRED)
def redirect(request):
    command(
        "hypergen.redirect",
        "https://github.com/runekaagaard/django-hypergen/blob/main/src/hypergen/static/hypergen/hypergen.js#:~:text=redirect"
    )
views.py
from hypergen.imports import *

from website.templates2 import base_example_template, show_sources

from commands import templates

@liveview(perm=NO_PERM_REQUIRED)
def commands(request):
    with base_example_template():
        templates.commands()

        show_sources(__file__)
urls.py
from hypergen.hypergen import autourls
from commands import views, actions

app_name = 'commands'

urlpatterns = autourls(views, app_name)
urlpatterns += autourls(actions, app_name)