Frontend #
This chapter will describe possible solutions for connecting your frontend (JS and CSS) and backend (your Nette application). If you are working on many legacy projects, you have already seen different solutions. Many of these solutions probably does not even have a structure of the frontend.
This chapter (and further chapters) will describe two possible solutions:
- JQuery + Loader which will help you get some structure in your legacy projects,
- Webpack Encore for a new projects, as clean as possible solution for your application.
How to use styles? #
The best solution is to define your styles in SCSS (Syntactically Awesome Style Sheet). SCSS is a preprocessor scripting language that is a superset of CSS. It provides additional features and functionalities that are not available in regular CSS. SCSS syntax is very similar to CSS, but it allows for the use of variables, nesting, mixins, and other programming constructs.
SCSS is a preprocessor scripting language
So it can not be used in your browser without the preprocessor. Therefore, you need to implement processing (or we can say in this case that it is compilation) to css, which can be handled by web browser.
We will discuss compiling of SCSS to CSS again in two possible ways:
- preprocessing in PHP using Loader,
- building with NodeJS using Webpack.
How to use javascript? #
The best solution is to define your javascript as separate modules. So if you are using javascript in your latte templates, it is not a best practice. The best way is to define a pattern for html classes which will be used to define your logic and connect it with the javascript using listeners on a given actions e.g. snippet loading.
Also, the javascript files can be concatenated into bigger files, minified to speed up loading, minified with source maps for better debugging, etc. You can also use Typescript to make you code more sustainable. Typescript will be compiled to javascript to run in the web browser.
AJAX (Asynchronous JavaScript and XML) s a set of web development techniques that uses various web technologies on the client-side to create asynchronous web applications. Nette has already implemented logic for this behavior. It is called snippets, which can be described as a part of the page which can be replaced using javascript. Therefore, you need to define your javascript logic in separate module, which will make your code clean and AJAX loading really easy to handle.
Layout definition #
To make your application AJAX-friendly, you will need to define your @layout.latte with a root snippet named content.
This will provide a root snippet, which will redraw the whole page using AJAX.
Define app/Presenters/templates/@layout.latte as follows:
{import 'components/flashes.latte'}
{import 'components/forms.latte'}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title n:snippet="title">{ifset title}{include title|stripHtml} | {/ifset}PROJECT-NAME</title>
<link rel="icon" href="{$basePath}/favicon.ico">
{* APPEND STYLES HERE *}
</head>
<body class="vh-100">
<div class="h-100 transition-auto transition-dim" n:snippet="content">
{include flashes}
{include content}
</div>
{* APPEND SCRIPTS HERE *}
</body>
</html>
And append to your app/Presenters/BasePresenter.php:
protected function beforeRender(): void
{
if ($this->isAjax() && !$this->isControlInvalid()) {
$this->redrawControl('title');
$this->redrawControl('content');
}
}
What it will do? It is super simple. If your request comes from AJAX, it will redraw your page title and content snippet.
Condition !$this->isControlInvalid() will handle request which will come from e.g. control, where is defined which part of the page should be redrawn.
For example, pagination of a datagrid will load only datagrid content.
If the request comes from your presenter (without definition which part should be redrawn), it will redraw whole page (the content snippet).
Flash Messages #
Commonly used flash messages should be handled in @layout.latte.
In many cases, you will need to show flash messages in your controls.
For example when user do some action inside control, you will also need to display flash message inside the control.
For these purposes, it is a best practice just include your flash message template in your control template. Therefore, it is a great idea define it as a block which can be included anywhere.
Define app/Presenters/templates/components/flashes.latte as follows:
{define flashes}
<div class="alerts" n:snippet="flashes">
{foreach $flashes as $flash}
{include flash-message $flash->type, $flash->message, dismissible => true}
{/foreach}
</div>
{/define}
{define flash-message string $type, string $message, bool $dismissible = false}
<div class="alert alert-{$type}{if $dismissible} alert-dismissible{/if} solid fade show" role="alert">
<div class="ms-2 me-4">
{_$message|noescape}
</div>
<button n:if="$dismissible" type="button" class="btn-close" data-bs-dismiss="alert" aria-hidden="true">
<i class="bi bi-x"></i>
</button>
</div>
{/define}
Compatibility of these latte codes can be different for different latte versions
In some older versions of Latte, arguments of blocks cannot be passed as a regular variables. In these cases, you will need to handle it on your own using variable
$_args.If it is possible, consider updating your dependency
latte/latteto newer version.