search_index_weight: -10


Basic Concept
=============

Writing HTML
------------

Lona uses a combination of `Jinja <https://jinja.palletsprojects.com/>`_ and
`Lona nodes </api-reference/html.html>`_ to generate HTML.

When writing a single page application, only a small fraction of the generated
HTML ever changes. The basic layout, the banner, navigation and footer always
stay the same. Therefore this part of a Lona application gets generated using
a Jinja template (more information:
`Lona Frontends </api-reference/frontends.html>`_).

The part of the application that changes is generated by a
`Lona View </api-reference/views.html>`_, using
`Lona nodes </api-reference/html.html>`_, HTML strings or a Jinja
template.


Workflow
--------

Lets say you have view that counts to ten:

.. code-block:: python

    # views/count_to_ten.py

    from lona.html import HTML, H1, P, Div
    from lona.view import LonaView


    class CountToTen(LonaView):
        def handle_request(self, request):
            counter = Div('0')

            html = HTML(
                H1('Count To Ten'),
                P('Counter: ', counter),
            )

            for i in range(1, 11):
                counter.set_text(i)
                self.show(html)

                self.sleep(1)

and the URL to this view is ``/count-to-ten/``

.. code-block:: python

    # routes.py

    routes = [
        Route('/count-to-ten/', 'views/count_to_ten.py::CountToTen')
    ]

When you open ``http://localhost:8080/count-to-ten/`` with your browser, your
view gets not started directly, but Lona starts your
{{ link('api-reference/frontends.rst', 'frontend') }} view. This view
is supposed to serve the basic layout of your page (banner, navigation, footer
and so on) and the Lona Javascript client.

The Javascript client then opens a Websocket connection to the server and sends
the message ``lona:[1,null,101,["http://localhost:8080/count-to-ten/",null]]``.

Lona will then start your view and will send the HTML given to ``show()`` in
a similar message. All Lona messages are JSON encoded, the payload of the
message will look like this:

.. code-block:: text

    [WIDGET,
     '663758909164540',
     '',
     [[NODE,
       '663758908759614',
       'h1',
       [],
       [],
       {},
       {},
       [[TEXT_NODE, '663758908791192', 'Count To Ten']]],
      [NODE,
       '663758908960048',
       'p',
       [],
       [],
       {},
       {},
       [[TEXT_NODE, '663758908983761', 'Counter: '],
        [NODE,
         '663758909061401',
         'div',
         [],
         [],
         {},
         {},
         [[TEXT_NODE, '663788860494138', '0']]]]]],
      {}]

The encoded HTML gets rendered to browser readable HTML by the Javascript
client

.. code-block:: html

    <h1 data-lona-node-id="663758908759614">
      Count To Ten
    </h1>
    <p data-lona-node-id="663758908960048">
      Counter:
      <div data-lona-node-id="663758909061401">
        0
      </div>
    </p>


The HTML gets only send entirely once, because the view
only updates the div named ``counter`` before calling ``show()`` again.

Every Lona HTML node has a unique id stored in ``data-lona-node-id``. When a
node gets updated Lona sends updates only for that specific node.


Asynchronous Code
-----------------

Lona is based on asyncio and uses
`aiohttp <https://docs.aiohttp.org/en/stable/>`_ as its HTTP server. So Lona is
asynchronous internally, but provides an asynchronous API that does not
use Pythons ``async`` and ``await`` syntax.

.. code-block:: python

    from lona.html import HTML, H1, Button
    from lona import LonaView


    class MyLonaView(LonaView):
        def handle_request(self, request):
            html = HTML(
                H1('Click The Button'),
                Button('Click Me'),
            )

            self.show(html)

            # This call blocks until the button is clicked
            input_event = self.await_click()

            self.show(H1('button was clicked'))

Why is that? Asyncio implements cooperative multitasking. That means Asyncio
requires every piece of code, running on its ioloop, to be fully compliant.

Lets say Lona would use ``await`` for its asynchronous API and you want to
write a view that pulls all data from a database and save it again:

.. code-block:: python

    from lona.html import HTML, H1, Button
    from orm.models import MyModel
    from lona import LonaView


    class MyLonaView(LonaView):
        async def handle_request(self, request):
            html = HTML(
                H1('Click The Button to update database'),
                Button('Click Me'),
            )

            self.show(html)

            input_event = await self.await_click()

            # pull data from the database and update in the database
            for model in MyModel.objects.all():
                model.update_table()

When the button is clicked the view does not release the ioloop scope until
the all database operations are finished. In this time, nothing else can
happen. The ioloop is blocked (potential forever).  This is no obvious problem
for developers that are not that familiar with asyncio, and issues like that
first show up in production, in a multi user environment.

You could move all blocking code to a thread (thats what Lona internally does
with your whole view) to not block the ioloop, but that splits the view logic
in multiple pieces of code, what introduces complexity.

Lona uses multi-threading which allows for self-contained views without the
need to implement cascading callbacks. This does not mean you cant use
``await`` in your views if you really have to (more information:
`LonaView.await_sync() </api-reference/views.html#lonaview-await-sync-awaitable>`_

**More information on resource management:**
`Resource Management </api-reference/deployment.html#resource-management>`_
