Frontends
- Custom Templates
- Loading static files
- Hooks
- Server disconnect
- View Start Timeout
- Input Event Timeout
- Sending Custom Messages
- Client To Server
- Server To Client
- Writing A Custom Frontend View
Interactive views need a frontend view to run before them. The frontend view is supposed to load the Lona Javascript client, define the basic layout of the website and load all static files.
More information: Basic Concept
Lona has a default frontend view build in that uses an overrideable template. To customize your Lona application you can override the template or the entire view.
Custom Templates
Lona uses settings.FRONTEND_TEMPLATE which is set to lona/frontend.html by default. You can reset this value or provide a template under this path.
The default frontend template (shown below) is split up in lona/header.html, lona/footer.html and lona/frontend.js. Therefore the parts before and after the views can be overridden for styling and Javascript can be added to lona/frontend.js.
The default frontend template includes lona/style.css which can be overridden.
<!-- templates/lona/frontend.html -->
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
{{ Lona.load_stylesheets() }}
<link href="{{ Lona.load_static_file('lona/style.css') }}" rel="stylesheet">
<link href="{{ Lona.settings.STATIC_URL_PREFIX }}/favicon.ico" type="image/x-icon" rel="icon">
</head>
<body>
{% include "lona/header.html" %}
<div id="lona"></div>
{% include "lona/footer.html" %}
{{ Lona.load_scripts() }}
<script>
window.addEventListener('load', () => {
const lona_context = new Lona.LonaContext({
target: '#lona',
title: 'Lona',
update_address_bar: true,
update_title: true,
follow_redirects: true,
follow_http_redirects: true,
});
{% if Lona.settings.CLIENT_AUTO_RECONNECT %}
const auto_reconnect = true;
{% else %}
const auto_reconnect = false;
{% endif %}
let first_connect = true;
lona_context.add_connect_hook((lona_context) => {
// reconnect
// reload whole page in case static files changed
if(!first_connect) {
window.location = window.location;
return;
}
// first connect
first_connect = false;
});
// disconnect
lona_context.add_disconnect_hook((lona_context) => {
document.querySelector('#lona').innerHTML = `{% include "lona/disconnect-message.html" %}`;
if(!auto_reconnect) {
return;
}
setTimeout(() => {
lona_context.reconnect({
// we don't create a window and start a view on the server
// since we reload the page once the client reconnects anyway
create_window: false,
});
}, {{ Lona.settings.CLIENT_AUTO_RECONNECT_TIMEOUT }});
});
// waiting for server messages
lona_context.add_view_timeout_hook((lona_context, lona_window) => {
lona_window.set_html(`{% include "lona/waiting-for-server-message.html" %}`);
});
lona_context.add_input_event_timeout_hook((lona_context, lona_window) => {
alert(`{% include "lona/waiting-for-server-message.html" %}`);
});
// custom hooks
{% include "lona/frontend.js" %}
// setup
lona_context.setup();
window['lona_context'] = lona_context;
});
</script>
</body>
</html>
Loading static files
To load static files, that are not part of a widget, Lona defines a filter.
<link href="{{ Lona.load_static_file('style.css') }}" rel="stylesheet">
Hooks
The Lona context has a number of hooks that are called for different events.
Server disconnect
This hook gets called when the webbsocket connection is lost.
lona_context.add_disconnect_hook(function(lona_context, event) {
document.querySelector('#lona').innerHTML = 'Server disconnected';
});
View Start Timeout
When the server is under heavy load it can happen that a view does not start immediately. This hook is used to give the user feedback that the view gets started soon, but the client is waiting for the server to respond.
The timeout is triggered when the view needs longer than settings.CLIENT_VIEW_START_TIMEOUT to start. The default is 2 seconds.
lona_context.add_view_timeout_hook(function(lona_context, lona_window) {
lona_window.set_html('Waiting for server...');
});
Input Event Timeout
When the server is under heavy load it can happen that an input event cant be handled immediately. This hook is used to give the user feedback that the event gets handled soon, but the client is waiting for the server to respond.
The timeout is triggered when the server needs longer than settings.CLIENT_INPUT_EVENT_TIMEOUT to handle the input event. The default is 2 seconds.
lona_context.add_input_event_timeout_hook(function(lona_context, lona_window) {
alert('Waiting for server...');
});
Sending Custom Messages
To implement custom features in your frontend like desktop notifications, Lona supports custom messages. Your messages can contain anything, but may not start with lona: because thats the prefix for the Lona protocol.
Client To Server
On the server all messages get handled by middlewares.
// templates/lona/frontend.html
lona_context.send('custom-message:foo');
# middlewares.py
class CustomMessagesMiddleware:
def handle_websocket_message(self, data):
if not data.message.startswith('custom-message:'):
return data
message = data.message.split(':', 1)[1]
print(message)
More information: Middlewares
Server To Client
The client has a system in place similar to Lona middlewares. You can add a list of message handlers, that get incoming messages passed in, in the order of their registration. If a message handler returns the given message, the message gets passed to the next message handler. If not the message is regarded as handled.
# views.py
from lona import LonaView
class CustomMessageView(LonaView):
def handle_request(self, request):
self.send_str('custom-message:foo')
lona_context.add_message_handler(function(lona_context, raw_message) {
if(!raw_message.startsWith('custom-message:')) {
return raw_message;
};
alert(raw_message);
});