PyBladePyBlade

Events

PyBlade Live offers a robust event system that you can use to communicate between different components on the page. Because it uses browser events under the hood, you can also use PyBlade Live's event system to communicate even with plain vanilla JavaScript.

To trigger an event, you may use the emit() method from anywhere inside your component and listen for that event from any other component on the page.

Dispatching events

To dispatch an event from a PyBlade Live component, you can call the emit() method, passing it the event name and any additional data you want to send along with the event.

Below is an example of dispatching a post-created event from a PostCreate component:

from pyblade import LiveComponent

class PostCreate(LiveComponent):

    def save(self):
        ...
        self.emit("post-created") 

In this example, when the emit() method is called, the post-created event will be emited, and every other component on the page that is listening for this event will be notified.

Pro tip

You may use the dispatch() method to dispatch events. It works the same way as emit(). Choose which makes sense to you !

You can pass additional data with the event by passing the data as the second parameter to the emit() method:

self.emit('post-created', title=post.title)

Listening for events

To listen for an event in a PyBlade Live component, add the @on decorator above the method you want to be called when a given event is dispatched:

from pyblade import LiveComponent

class Dashboard(LiveComponent):

	@on('post-created') 
    def update_post_list(self, title: str):
        ...

Now, when the post-created event is dispatched from the PostCreate component, a network request will be triggered and the update_post_list() method will be called.

As you can see, additional data sent with the event will be provided to the method as its first argument.

Listening for dynamic event names

Occasionally, you may want to dynamically generate event listener names at run-time using data from your component.

For example, if you wanted to scope an event listener to a specific Database model, you could append the model's ID to the event name when dispatching like so:

from pyblade import LiveComponent
from app.models import Post


class PostUpdate(LiveComponent):
    post: Post

    def update(self, id: int):
        ...
        self.emit(f"post-updated.{post.id}") 

And then listen for that specific model:

from pyblade import LiveComponent
from app.models import Post

class PostDetail(LiveComponent):

    post: Post

	@on(f"post-updated.{post.id}") 
    def refresh_post(self):
        ...

If the above post model had an ID of 3, the refresh_post() method would only be triggered by an event named: post-updated.3.

Listening for events from specific child components

PyBlade Live allows you to listen for events directly on individual child components in your PyBlade template like so:

<div>
    <live:edit-post pb:saved="refresh">

    <!-- ... -->
</div>

In the above scenario, if the edit-post child component dispatches a saved event, the parent's refresh will be called and the parent will be refreshed.

Instead of passing refresh, you can pass any method you normally would to something like pb:click. Here's an example of calling a close() method that might do something like close a modal dialog:

<live:edit-post pb:saved="close">

If the child dispatched parameters along with the request, for example self.emit('saved', post_id=1), you can forward those values to the parent method using the following syntax:

<live:edit-post pb:saved="close(event.detail.post_id)">

Dispatching directly to another component

If you want to use events for communicating directly between two components on the page, you can use the emit().to() modifier.

Below is an example of the PostCreate component dispatching the post-created event directly to the Dashboard component, skipping any other components listening for that specific event:

from pyblade import LiveComponent

class postCreate(LiveComponent):

    def save(self):
		self.emit("post-created").to("Dashboard") 

Dispatching a component event to itself

Using the emit().self() modifier, you can restrict an event to only being intercepted by the component it was triggered from:

from pyblade import LiveComponent

class postCreate(LiveComponent):

    def save(self):
		self.emit("post-created").self() 

Dispatching events from PyBlade templates

You can dispatch events directly from your PyBlade templates using the emit() function. This is useful when you want to trigger an event from a user interaction, such as a button click:

<button pb:click="emit('show-post-modal', id={{ post.id }})">
    EditPost
</button>

In this example, when the button is clicked, the show-post-modal event will be dispatched with the specified data.

If you want to dispatch an event directly to another component you can use the emit().to() modifier:

<button pb:click="emit('show-post-modal', id={{ post.id }}).to('PostList')">
    EditPost
</button>

In this example, when the button is clicked, the show-post-modal event will be dispatched directly to the PostList component.

Using JavaScript to interact with events

PyBlade Live's event system becomes much more powerful when you interact with it from JavaScript. Any JavaScript on the page can hear the events components emit, and emit events that components listen for.

Listening for events inside component scripts

In a component's @script, listen for an event with $pb.$on():

@script
<script>
    $pb.$on('post-created', (data) => {
        //
    });
</script>
@endscript

The callback is given the data the event carries. It hears the event whichever component emitted it, and stops being called once its own component has left the page.

$on() returns a function that stops listening sooner:

const stop = $pb.$on('post-created', () => { /* ... */ });

stop();

Emitting events from component scripts

Emit an event from a component's @script with $pb.$emit():

@script
<script>
    $pb.$emit('post-created');
</script>
@endscript

The event is emitted by the component the script belongs to, exactly as emit() in a pb:click would emit it: every component listening for it with @on hears it. $pb.$dispatch() is the same thing under another name.

Pass any parameters as an object, the second argument of $emit():

@script
<script>
    $pb.$emit('post-created', { refresh_posts: true });
</script>
@endscript

Those parameters reach both PyBlade Live classes and JavaScript listeners. Here's the refresh_posts parameter received by a PyBlade Live class:

from pyblade import LiveComponent
from pyblade.decorators import on


class PostList(LiveComponent):

    @on("post-created")
    def handle_new_post(self, refresh_posts: bool = False):
        ...

And by a JavaScript listener:

@script
<script>
    $pb.$on('post-created', (data) => {
        let refreshPosts = data.refresh_posts;

        // ...
    });
</script>
@endscript

Listening for events from global JavaScript

Outside a component, use the PyBlade object. PyBlade.on() listens for an event emitted by any component on the page:

<script>
    document.addEventListener('live:init', () => {
        PyBlade.on('post-created', (event) => {
            console.log(event.detail.refresh_posts);
        });
    });
</script>

Unlike $pb.$on(), PyBlade.on() hands the callback the browser event itself, with the parameters in event.detail. The live:init event is dispatched on the document once PyBlade has started, so a script that runs before it can wait for it.

PyBlade.on() returns a cleanup function that removes the listener:

<script>
    document.addEventListener('live:init', () => {
        let cleanup = PyBlade.on('post-created', (event) => {
            //
        });

        // Calling "cleanup()" will un-register the above event listener...
        cleanup();
    });
</script>

Emitting events from global JavaScript

PyBlade.emit() emits an event from outside any component. It reaches every component listening for it, as one emitted by a component would:

PyBlade.emit('post-created', { refresh_posts: true });

PyBlade.dispatch() is the same thing under another name.

On this page