Subsections of Making templates
Pages
GOERP uses golang templates while assembling the pages, so any options that are supported by go templates may be applied here. Please refer to the official docs to get more information. Pages creation process is like writing html code, including some features from go templating system.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
</body>
</html>
Alternatively, GOERP have in-build layout system, so one layout can be re-used on many pages. All default layouts located in the partials section of the editor. Layouts have suffix -layout.
Here is example for the complex page, using layouts:
{{ template "dev-v2-layout" . }}
{{ define "title-block" }} Title of the tab {{ end }}
{{ define "content-block" }}
All content goes here, basically <main></main> should be in this section
{{ end }}
{{ define "js-block" }}
All js code goes here, <script></script>
{{ end }}
Content partials
This section describes content partials, which have go-html content only. For java-script and css partials check āJS and CSS partialsā section.
Partial is a part of document that can be re-used in several pages, which may be convenient if application consists of several pages with same content in some places. Letās say we have application where navigation content repeated in every page, with partial we can put this content in one template and re-use it in every page. Sounds very convenient, but still they have some restrictions:
- Partials can also contain unlimited partials, but the maximum depth (nested levels) is currently limited to 5
- Partials cannot have js and css imports/blocks
Create partial
To create a partial, go to the template editor
and pick Create -> Create new template, then define name and select type Partial from the dropdown.
Editor will generate very simple initial code for the partial and append suffix -partial
to the
template name:
{{ define "my-cool-partial" }}
<!-- Feel free to write your awesome component using HTML and powerful templating options -->
{{ end }}
So lets update newly created partial with some content
{{ define "my-cool-partial" }}
<h1>Hello Goerp!</h1>
{{ end }}
and now let’s inject partial into the page:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
{{ template "my-cool-partial" . }}
</body>
</html>
Passing data to the partial
In the last section we created simple partial and injected into the page. There is dot in the end
of partial injection statement, which means that we are passing all data that was sent from back-end
with the response to the partial. In this case we can use any available variable field, for example
changing our <h1>Hello Goerp!</h1>
to the <h1>Hello Goerp! Client code {{ .Data.Session.ClientCode }}</h1>
will print the client code number.
However, in some cases we may want to pass specific set of variables instead of all available ones.
In this case we can use in-build function that will produce variable of the key-value pairs (check
Built-in helper functions topic mkMap
func for more details). So we need to update our partial to use the variable:
<h1>Hello Goerp! Client code {{ .clientCode }}</h1>
and then pass this variable while injecting
the template in our page: {{ template "my-cool-partial" mkMap "clientCode" 123456 }}
. Or we can
pass any part of available in page data, this will also work: {{ template "my-cool-partial" .Data.Session }}
and then <h1>Hello Goerp! Client code {{ .ClientCode }}; session key {{ .SessionKey }}</h1>
JS and CSS partials
According to the new CSP (Content Security Policy) requirements, all inline css and js content will be blocked by browser.
Samples that would be blocked:
<p style="padding: 5px;">blocked</p>
<script>console.log("blocked")</script>
<header><stile>html {padding:5px;}</style></header>
<button onclick="funccall()">blocked</button>
Valid samples:
<header><link rel="stylesheet" href="{{ "partial-css" | staticFileLink }}"></header>
<header><link rel="stylesheet" href="https://link.to.my.css"></header>
<body><script src="{{ staticFileLink "partial-js" }}"></script></body>
To create a css/js partial, go to the template editor
and pick Create -> Create new template, then define name and select type JS or CSS respectively.
Editor will create empty file and append suffix -css
or -js
respectively to the
template name. Now we can write any valid css/js code there, just like we would do in regular
.js
or .css
files.
Now, when static partial is ready, we can link it with the page like this:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
{{/* Link css partial, as an option, calling linking function in a pipe */}}
<link rel="stylesheet" href="{{ "partial-css" | staticFileLink }}">
<title>Document</title>
</head>
<body>
{{/* Page content */}}
{{/* Link js partial, as an option, using regular linking function call */}}
<script src="{{ staticFileLink "partial-js" }}"></script>
</body>
</html>
Java-script and css partials are static files, and they are not part of the goerp template.
To link our templates with the static partials (js, css), we can use helper function staticFileLink
like this: <link rel="stylesheet" href="{{ "partial-css" | staticFileLink }}">
. Or make linking as
usual css and js imports: <link rel="stylesheet" href="https://link.to.css.file">
Layouts
GOERP have in-build layout system, so one layout can be re-used on many pages. This feature would be very useful if application have many templates (pages) because with layouts we can encapsulate all general html into one component (e.g. css and js dependencies, general html like header, footer, menu, etc…).
Layouts usage may look similar to the regular partial logic, however, they behave in absolutely different way. The main difference between layout and regular partial is that the former one contains code placeholders that are replaced with actual payload on the related page template, and the latter one doesn’t have any placeholders and contains only code that is related to this specific partial.
In other words:
- in case of layouts, page template exports go-html content to the layout
through
block
keywords bydefine
‘ing those blocks; - in case of partial, page template imports go-html content from the partial through
the
template
notation; - both, layout and partial, should be defined inside page by using
template
keyword, the only difference is that layouts must be defined at very beginning of the page template. - both, layout and partial cannot include other partials as dependencies
Under pages topic we already covered briefly layouts feature. Let’s dive into more details now.
Very simple example (layout)
{{ define "simple-layout" }}
<!-- Let's say we want to encapsulate page content that is the same for all pages in our application -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Default Erply styles -->
<link rel="stylesheet" href="https://assets.erply.com/bo-prototype/_style.css">
<!-- Maybe some custom styles -->
<link rel="stylesheet" href="https://assets.my-company.net/style.css">
<!-- Regular fonts from google -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- With this block we are creating placeholder which may be filled from the page template -->
<!-- Here we may inject page specific css links or static file dependencies -->
<!-- Please note, all block definition names must have -block suffix at the end -->
{{ block "css-block" . }} {{ end }}
<!-- Add page specific title -->
<title>
{{ block "title-block" . }} Default title {{ end }}
</title>
</head>
<body>
<menu class="menu-container">
<!-- This menu will be included into all dependent pages -->
</menu>
{{ block "content-block" . }}
<main>
<p>
Another cool feature of the placeholder blocks is that they may contain default content,
some kind of fall back in case if dependent page decides to leave this block undefined. So,
if page that is using this layout doesn't have <code>{{ define "content-block" }} {{ end
}}</code>
section then <strong>this message shown by default</strong>.
</p>
</main>
{{ end }}
<!-- Another sample how to pass some data from templates to the js using inputs -->
<input type="hidden" id="AUT_SESSION" value="{{ toJson .Session }}">
<input type="hidden" id="REQUEST_STATS" value="{{ toJson .Data.RequestStats }}">
<!-- global js dependencies -->
<script src="/assets/js/automat.deps.js" type="application/javascript"></script>
<script src="/assets/js/menu.bundle.js" type="application/javascript"></script>
<!-- placeholder for js dependencies, use static files to import js code -->
{{ block "js-block" . }} {{ end }}
</body>
</html>
{{ end }}
First of all, we are defining name of the layout, which must end with layout suffix. Don’t worry,
suffix will be added automatically when creating new template from editor and selecting layout
template type. So, for our sample during layout creation we enter simple
name and -layout
added
by default.
Next, we have some regular page content that is, potentially, repeated on every page in our application.
The first block that would be replaced by content from the page that will use this layout is
{{ block "css-block" . }} {{ end }}
. Then we have title placeholder:
{{ block "title-block" . }} Default title {{ end }}
and then placeholder for the biggest part
of the page, - main section: {{ block "content-block" . }} {{ end }}
. Check default (fall back)
content of this block in the sample. Finally, we have js placeholder where we can include any
js related to the page, please refer to css and js partials for more information on how to include
js and css code.
While defining blocks inside layout, always put -block
suffix in names. If name will end with
something else then goerp parser will process them as regular partials and mess up template
parameters. This may lead to appear some unknown partials and mey produce unexpected behavior.
Very simple example (page)
Now, when we have defined the layout, we may want to use it inside our pages. Let’s say it 10th page of our application:
<!-- Page creation always starting from importing our layout -->
{{ template "simple-layout" . }}
<!-- Title for our 10th page -->
{{ define "title-block" }} 10th page of application {{ end }}
<!-- The biggest part, - content -->
{{ define "content-block" }}
<!-- Display errors -->
{{ range .Data.Errors }}
<div class="error-row">
<span>{{ . }}</span>
</div>
{{ end }}
<!-- Include partials -->
{{ template "employee-query-form-partial" . }}
<!-- Write regular goerp template code -->
<table>
<thead>
<tr>
<th>Id</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
<tbody>
{{ range .Data.AccountAdminApi.EmployeeList }}
<tr>
<td>{{ .Id }}</td>
<td>{{ .FirstName }}</td>
<td>{{ .LastName }}</td>
</tr>
{{ end }}
</tbody>
</table>
<!-- Include js -->
{{ define "js-block" }}
<script src="{{ staticFileLink "my-cool-js" }}"></script>
{{ end }}
{{ end }}
First of all, we are importing layout to the page.
Notice dot at the end of layout definition? Yes, we can pass any data to the layout, with the dot we are passing everything that page have. However, we can pass some custom data and, for example, add tabs logic to the layout. Just a tip.
Next, we are defining title of our page with
{{ define "title-block" }} 10th page of application {{ end }}
. Next, in our template we have
css-block
, but we don’t need to include any css links/deps here, so we just skip this block.
Next goes content of the page, the biggest part. There we can use other templates-partials and
goerp templating logic.
Finally, we have js block implementation which includes static file.
When to use layouts?
Layouts are powerful feature of the goerp template engine. However, it is quite complex and may produce some wierd behaviour if it is used in a wrong way. Better to avoid using layouts if they are not simplifying your work, just follow KISS principles. Same advice regarding partials. For example, if your application have 3 pages with simple form and a couple of tables, then maybe would be more convenient to write all content inside one template-page, so don’t need to jump between partials during development or maintaining.
Here is a short tips list when to use layout and when to avoid it:
Layouts are handy when
- My application have many pages with repeated content (such as menu, tabs, dependencies, etc.)
- I have many applications that reuses same code on every page. Please note, in this case you still need to create duplicate layout and give it a new name, but you need just copy-paste the content.
Avoid layouts when
- My application have few pages and duplicating similar content wouldn’t take much effort