Built-in Template Events

Finch injects a set of built-in variables and helper functions into every Jinja template. These are accessible via special prefixes alongside your own addParam() data.

Global Variables

{{ isLocalDebug }}   {# true when running in debug mode #}
{{ data }}          {# map of all params added via rq.addParam() #}
{{ session }}       {# server-side session map #}
{{ $rq }}           {# the Request object #}

Asset Helpers

{{ assets.js() }}      {# renders all <script> tags added via rq.addAsset() #}
{{ assets.css() }}     {# renders all <link> tags #}
{{ assets.dataJs() }}  {# renders data attributes for JS access #}

Route and URL Helpers ($e)

{{ $e.route }}                              {# the matched route's rendered path, e.g. '/users/42' — the specific path/extraPath that matched this request #}
{{ $e.routePath }}                          {# the route's canonical full path pattern, e.g. '/users/{id}' (always via the primary `path`, even if an `extraPath` is what matched) #}
{{ $e.routeKey }}                           {# the route's `key`, e.g. 'users.show' — NOT the same as $e.route #}
{{ $e.isKey('root.panel') }}                {# true if current route's key matches #}
{{ $e.hasKey(['root.panel', 'root.form']) }} {# true if current route's key is one of the keys #}

{{ $e.routeUrl('key') }}                           {# URL for a named route #}
{{ $e.routeUrl('users.show', {'id': '42'}) }}       {# URL with path params #}
{{ $e.routeUrl('search', {}, {'q': 'hello'}) }}     {# URL with query params #}

{{ $e.uri }}            {# full request URI, percent-encoded as a single string #}
{{ $e.uriString }}      {# full request URI, NOT encoded #}
{{ $e.path }}           {# request path only, percent-encoded as a single string #}
{{ $e.pathString }}     {# request path only, NOT encoded #}
{{ $e.isPath('/example/form') }}  {# true if current path matches #}
{{ $e.endpoint }}       {# the matched endpoint path #}

{{ $e.url('/about') }}                    {# build an absolute URL from a path #}
{{ $e.urlParam('/users', {'id': '5'}) }}   {# URL with appended query params #}
{{ $e.urlToLanguage('fa') }}              {# current URL switched to another language #}

Despite the names, $e.uri/$e.path are percent-encoded strings, not a Dart Uri object or a list of path segments — use $e.uriString/$e.pathString when you need the raw, unencoded value.

{{ $e.getCookie('theme', 'light') }}  {# read a cookie with default value; always a plain (unencrypted) read #}

Language Helpers

{{ $e.ln }}            {# current language code, e.g. 'en' #}
{{ $e.langs }}         {# list of all configured languages: [{code, label, contry}] #}

$e.dir and the label/contry fields inside each $e.langs entry are not read from a plain "dir" key in your language file — they look up dedicated translation keys named language.<code>_dir, language.<code>_label, and language.<code>_contry (note: contry, not country — this is a real spelling quirk in the framework, not a typo in this doc). If those keys don't exist in your language files, .tr.write() silently falls back to returning the key itself, so {{ $e.dir }} would literally render the text language.en_dir instead of ltr.

The Finch example project's own language files only define a plain "dir" key and don't define these language.* keys, so $e.dir/$e.langs[].label/$e.langs[].contry render unresolved out of the box. The example project's own layout works around this by using {{ $t('dir') }} instead (a direct lookup of the plain "dir" key) — see the Templates complete example. Either add the language.<code>_dir/_label/_contry keys to every language file yourself, or prefer $t('dir') / your own translation keys over $e.dir and the label/contry fields of $e.langs.

Utility Helpers

{{ $e.widgetPath('partials/nav') }}   {# appends the configured widget extension to a path, e.g. 'partials/nav.j2.html' — does not prepend widgetsPath #}
{{ $e.randomString(8) }}             {# random string of 8 characters (default length is 4 if omitted) #}
{{ $e.toString(value) }}             {# coerce any value to string #}

Translation

{{ $t('logo.title') }}                          {# translate a key #}
{{ $t('greeting', {'name': user.name}) }}       {# translate with parameters #}

Nested Data Navigation

{{ $n('user/address/city', 'Unknown') }}  {# navigate nested params safely #}

Debug Dump

{{ dump(data) }}   {# dump any variable visually in the browser (debug only) #}

Custom Local Events

Define your own global functions in app.dart using Request.localEvents:

Request.localEvents.addAll({
  'currentYear': () => DateTime.now().year,
  'appName': () => 'My App',
});

Access them in templates with the $l. prefix:

<footer>© {{ $l.currentYear() }} {{ $l.appName() }}</footer>