v0.21

views

introduction

dframework ships with a custom view engine. it uses a string to function evaluation model that compiles your templates into raw javascript functions. this means you can write standard javascript directly inside your templates while utilizing convenient directives for common tasks.

creating views

views are stored in the views directory and must use the .d file extension. they are referenced using dot notation. a file at views/app/home/index.d is referenced as app.home.index.

1<!-- views/greetings/hello.d -->
2<html>
3 <body>
4 <h1>hello, {{ name }}!</h1>
5 </body>
6</html>

you return views from your controllers using the render() helper.

1export default class GreetingController {
2 async hello() {
3 return render('greetings.hello', { name: 'world' });
4 }
5}

for security reasons, you cannot pass dangerous javascript global objects (process, require, global, etc.) into your view data. the engine strictly validates the provided context and will throw an error if tampering is detected.

displaying data

you display data passed to your views by wrapping the variable in double curly braces. the view engine automatically escapes the output to prevent xss attacks.

1hello, {{ user.name }}.
2the current timestamp is {{ Date.now() }}.

if you need to render unescaped html, use the {!! !!} syntax.

1{!! user.bio_html !!}

you can convert objects to json strings using the @json directive.

1<script>
2 const userState = @json(user);
3</script>

control structures

the view engine provides shortcuts for common javascript control structures.

if statements

1@if (user.isAdmin)
2 <button>admin panel</button>
3@elseif (user.isModerator)
4 <button>mod panel</button>
5@else
6 <span>standard user</span>
7@endif

loops

1@foreach (users as user)
2 <li>{{ user.name }}</li>
3@endforeach
4
5@for (let i = 0; i < 10; i++)
6 <span>{{ i }}</span>
7@endfor

the @forelse directive is a convenient way to loop over arrays with a fallback if the array is empty.

1@forelse (tracks as track)
2 <li>{{ track.title }}</li>
3@empty
4 <li>no tracks found.</li>
5@endforelse

layouts and includes

you can define a master layout and extend it from child views. use @yield to mark where content should be injected, and @section in the child view to provide that content.

1<!-- views/layouts/app.d -->
2<html>
3 <head>
4 <title>app</title>
5 </head>
6 <body>
7 <nav>...</nav>
8 <main>
9 @yield('content')
10 </main>
11 </body>
12</html>
1<!-- views/home.d -->
2@extends('layouts.app')
3
4@section('content')
5 <h1>welcome home</h1>
6@endsection

you can include partials using the @include directive.

1@include('components.header')

javascript execution

since the view engine compiles directly to javascript functions, you can execute arbitrary server side code directly inside your templates. use the @js() directive for a single line of javascript, or the @js ... @endjs block for multiple lines.

1@js(const maxLimit = 50;)
2
3@js
4 let sortedUsers = users.sort((a, b) => b.score - a.score);
5 sortedUsers = sortedUsers.slice(0, maxLimit);
6@endjs
7
8@foreach (sortedUsers as user)
9 <li>{{ user.name }}</li>
10@endforeach

localization

you can translate strings using the @t() directive. the framework automatically resolves the current locale from the request context and fetches the corresponding translation string.

1<h1>@t('home.welcome_message')</h1>
2<p>@t('home.unread_count', { count: 5 })</p>

pagination

the view engine includes a built in @pagination directive that automatically generates html pagination links for a paginated result object returned by the model .paginate() method.

if you leave the block empty, the engine automatically generates standard pagination controls with icons and page numbers.

1<!-- renders standard pagination controls -->
2@pagination(users)
3@endpagination

by default, if the request comes from the spa router (dSPAHttpRequest), the links will automatically trigger spa navigation instead of a full page reload. if you need the pagination links to update a specific dom element via the spa router (e.g. replacing just a table body), you can pass a css selector as the third argument.

1<!-- updates the #user-list container instead of a full page transition -->
2@pagination(users, true, '#user-list')
3@endpagination

custom pagination html

if you provide content inside the @pagination block, the engine skips the default html generation and renders your custom html instead.

inside the block, the engine automatically exposes four variables that you can use to build your custom controls:

1@pagination(users)
2 <div class="custom-pagination">
3 <p>page {{ current }} of {{ last }}</p>
4
5 @if (prev)
6 <a href="{{ prev }}" class="btn">previous</a>
7 @endif
8
9 @if (next)
10 <a href="{{ next }}" class="btn">next</a>
11 @endif
12 </div>
13@endpagination

the framework evaluates the block only if there is more than one page (last > 1). if there is only one page, the entire @pagination block is skipped and nothing is rendered.

automatic form handling

when you create an html <form> with a mutating method (e.g. POST, PUT), the view engine automatically injects a hidden _csrf input field and the current csrf token value. you do not need to manually output csrf tokens in your forms.

additionally, the engine injects a <meta name="csrf-token"> tag into the document's <head>, allowing your ajax requests to pick up the token.

anti peeping protection

dframework includes a built in anti peeping script that disables context menus, blocks common developer tools keyboard shortcuts, and triggers infinite debugger loops if the console is forced open.

this behavior can be toggled globally via app.antiPeeping in your configuration, or overridden per response using the antiPeeping(false) chainable helper in your controller.

view compilation

in the local environment, the framework uses a custom jit compiler and provides detailed error traces mapped directly to your original .d files.

in the production environment, views are ahead of time compiled into highly optimized .dc (.d compiled) files. the framework verifies the cryptographic integrity of these .dc files on startup to ensure your templates have not been tampered with.