Cookbook ======== Short, repeatable recipes for everyday Wijjit tasks. Each entry links to a working example so you can copy-paste with confidence. Form validation with inline errors ---------------------------------- Source: ``examples/advanced/login_form.py`` 1. Bind text inputs to state keys (``id="username"``/``"password"``). 2. Use ``@app.on_action("login")`` to validate fields; populate ``state.form_errors`` with error messages. 3. Render errors next to inputs using template conditionals: .. code-block:: jinja {% if state.form_errors.username %} {% text class="error" %}{{ state.form_errors.username }}{% endtext %} {% endif %} For a transient toast instead of inline text, call ``app.notify(state.form_errors["username"], severity="error")`` from your action handler. 4. Clear errors when the form succeeds or users edit the fields. Persisting data between sessions -------------------------------- Source: tutorial todo app (see :doc:`../getting_started/tutorial`) 1. Load data from disk when constructing ``Wijjit``; store a ``next_id`` counter in state. 2. Define helper ``persist()`` that writes ``state["todos"]`` back to JSON. 3. Call ``persist()`` inside ``@app.on_action`` handlers and/or ``state.watch("todos", ...)`` so every change is saved. 4. Keep file paths under ``Path.home()`` or configurable via environment variables. Multi-view navigation with keyboard shortcuts --------------------------------------------- Source: ``examples/widgets/centered_dialog.py`` (modal navigation) and ``examples/widgets/dropdown_demo.py`` (actions) 1. Register multiple views with ``@app.view("home", default=True)`` and ``@app.view("settings")``. 2. Use ``@app.on_action("go_settings")`` to call ``app.navigate("settings")``. 3. Add keyboard shortcuts via ``@app.on_key("ctrl+,")`` to trigger the same action. 4. Store breadcrumbs or parameters in ``app.current_view_params`` if you need to restore filters/search terms when returning. Background tasks & progress indicators -------------------------------------- Source: ``examples/basic/async_demo.py`` and ``examples/advanced/download_simulator.py`` 1. Mark the handler ``async def`` (Wijjit awaits coroutines). 2. Update ``state["progress"]`` inside the coroutine and call ``await asyncio.sleep(...)`` between iterations. 3. Render ``progress`` via ``{% progressbar value=state.progress %}{% endprogressbar %}``. 4. For CPU-bound work, opt into the built-in executor with ``app.config["RUN_SYNC_IN_EXECUTOR"] = True`` (and optionally ``app.config["EXECUTOR_MAX_WORKERS"] = N``) so blocking sync handlers run off the event loop and keep the UI responsive. Modal confirmations before destructive actions ---------------------------------------------- Source: ``examples/widgets/modal_with_button_demo.py`` 1. Define a template snippet with ``{% confirmdialog action_ok="confirm_delete" action_cancel="cancel_delete" %}``. 2. Show the dialog by updating a boolean in state or by returning overlay metadata from the view. 3. Handle the confirm/cancel actions with ``@app.on_action``; call ``app.overlay_manager.pop_layer(LayerType.MODAL)`` when done. 4. Use ``trap_focus=True`` and ``dimmed_background=True`` for critical workflows. Data tables with selection -------------------------- Source: ``examples/widgets/table_demo.py`` 1. Prepare rows as list of dicts; include ``selected_row`` in state. 2. Pass ``rows`` and ``columns`` to the table tag; use ``action="select_row_{{ row.id }}"`` on buttons/checkboxes inside cells. 3. Handle selection in a generic ``EventType.ACTION`` handler by parsing the action id (``select_row_`` prefix). 4. Reflect selection elsewhere (details pane, status bar, modal). Notifications & status feedback ------------------------------- Source: ``examples/widgets/notification_demo.py`` and ``examples/widgets/statusbar_demo.py`` 1. For transient toasts, call ``app.notify("Saved!", severity="success")``. The notification auto-dismisses after ``duration`` seconds (pass ``duration=None`` to make it persistent). 2. For persistent hints, bind a ``state.status`` string to a ``statusbar`` or text block. 3. Combine with success/error tones to give clear guidance after actions. Theming and styling tweaks -------------------------- Source: ``examples/widgets/spinner_demo.py`` + :doc:`../user_guide/styling` 1. Create a custom ``Theme`` with overrides (button colors, frame borders). 2. Register and activate it via ``app.renderer.theme_manager.register_theme(custom_theme)`` followed by ``app.renderer.theme_manager.set_theme(custom_theme.name)`` during startup or in response to user preference toggles. 3. Give elements a ``class`` in the template (e.g. ``{% button class="danger" %}``) and define that class in the theme — templates style by class, not by inline colour dicts. 4. Snapshot the UI after theme changes to catch regressions. Have a recipe to add? Drop a runnable snippet in ``examples/`` and update this page so others can benefit. Refer back to :doc:`../user_guide/core_concepts` for a deeper explanation of how these patterns plug into the runtime.