This repository has been archived on 2023-08-07. You can view files and clone it, but cannot push or open issues or pull requests.
temple/guides/your-first-template.md

217 lines
6.3 KiB
Markdown
Raw Permalink Normal View History

# Your First Template
A Temple template is written inside of the `Temple.temple/1` macro. Code inside there will be compiled into efficient Elixir code by the configured EEx engine.
Local functions that have a corresponding HTML5 tag are reserved and will be used when generated your markup. Let's take a look at a basic form written with Temple.
```elixir
defmodule MyApp.FormExample do
import Temple
def form_page() do
assigns = %{title: "My Site | Sign Up", logged_in: false}
temple do
"<!DOCTYPE html>"
html do
head do
meta charset: "utf-8"
meta http_equiv: "X-UA-Compatible", content: "IE=edge"
meta name: "viewport", content: "width=device-width, initial-scale=1.0"
link rel: "stylesheet", href: "/css/app.css"
title do: @title
end
body do
if @logged_in do
header class: "header" do
ul do
li do
a href: "/", do: "Home"
end
li do
a href: "/logout", do: "Logout"
end
end
end
end
form action: "", method: "get", class: "form-example" do
div class: "form-example" do
label for: "name", do: "Enter your name:"
input type: "text", name: "name", id: "name", required: true
end
div class: "form-example" do
label for: "email", do: "Enter your email:"
input type: "email", name: "email", id: "email", required: true
end
div class: "form-example" do
input type: "submit", value: "Subscribe!"
end
end
end
end
end
end
end
```
This example showcases an entire HTML page made with Temple! Let's dive a little deeper everything we're seeing here.
Through out this guide, you will see code that includes features that are explained later on. Feel free to skip ahead to read on, or just keep reading. It will all make sense eventually!
## Text Nodes
The text node is a basic building block of any HTML document. In Temple, text nodes are represented by Elixir string literals.
The very first line of the previous example is our doc type, emitted into the final document with `"<!DOCTYPE html>"`. This is a text node and will be emitted into the document as-is.
Note: String _literals_ are emitted into text nodes. If you are using string interpolation with the `#{some_expression}` syntax, that is treated as an expression and will be evaluated in whichever way the configured engine evaluates expression. By default, the `EEx.SmartEngine` doesn't do any escaping of expressions, so that could still be emitted as-is, or even as HTML to be interpreted by your web browser.
## Void Tags
Void tags are HTML5 tags that do not have children, meaning they are "self closing".
We can observe these in the previous example as the `<input>` tag. You'll note that the tag does not have a `:do` key or a `do` block.
## Non-void Tags
Non-void tags are HTML5 tags that _do_ have children. You are probably most familiar with these type of tags, as they include the famous `<div></div>` and `<span></span>`.
These tags can enclose their children nodes with either a `do/end` block or the inline `:do` keyword.
### Whitespace
Nonvoid tags that use the `do/end` syntax will be emitted _with_ internal whitespace.
```elixir
temple do
div class: "foo" do
# children
end
end
```
...will emit markup that looks like...
```html
<div class="foo">
<!-- children -->
</div>
```
Note: The Elixir comment _will not_ be rendered into an HTML comment. This is just used in the example. (This does sound like a good feature though...)
Nonvoid tags that use the `:do` keyword syntax will be emitted _without_ internal whitespace. This allows you to correctly use the `:empty` CSS psuedo-selector in your stylesheet.
```elixir
temple do
p class: "alert alert-info", do: "Your account was recently updated!"
end
```
...will emit markup that looks like...
```html
<p class="alert alert-info">Your account was recently updated!</p>
```
## Attributes
Dynamic Attributes (#190) * Move directories for ast tests to match convention * feat!: Rename `:let` to `:let!` We use the "bang" style as the reserved keyword to differentiate it from other possible attributes. * feat: use Phoenix.HTML as the default engine I am choosing to leverage this library in order to quickly get dynamic attributes (see #183) up and running. This also ensures that folks who wish to use Temple outside of a Phoenix project with get some nice HTML functions as well as properly escaped HTML out of the box. This can be made optional if Temple becomes decoupled from the render and it including HTML specific packages becomes a strange. * feat: Allow user to make their own Component module The component module is essentially to defer compiling functions that the user might not need. The component, render_slot, and inner_block functions are only mean to be used when there isn't another implementation. In the case of a LiveView application, LiveView is providing the component runtime implementation. This was causing some compile time warnings for temple, because it was using the LiveView engine at compile time (for Temple, not the user's application) and LiveView hadn't been compiled or loaded. So, now we defer this to the user to make their own module and import it where necessary. * feat: Pass dynamic attributes with the :rest! attribute The :rest! attribute can be used to pass in a dynamic list of attributes to be mixed into the static ones at runtime. Since this cannot be properly escaped by any engine, we have to mark it as safe and then allow the function to escape it for us. I decided to leverage the `attributes_escape/1` function from `phoenix_html`. There isn't really any point in making my own version of this or vendoring it. Now you can also pass a variable as the attributes as well if you only want to pass through attributes from a calling component. The :rest! attribute also works with components, allowing you to pass a dynamic list of args into them. Fixes #183 * Move test components to their own file. * docs(components): Update documentation on Temple.Components * docs(guides): Mention attributes_escape/1 function in the guides * chore(test): Move helper to it's own module * feat: rest! support for slots * docs(guides): Dynamic attributes * ci: downgrade runs-on to support OTP 23
2023-01-21 11:44:29 +00:00
Temple leverages `Phoenix.HTML.attributes_escape/1` internally, so you can refer to it's documentation for all of the details.
Dynamic Attributes (#190) * Move directories for ast tests to match convention * feat!: Rename `:let` to `:let!` We use the "bang" style as the reserved keyword to differentiate it from other possible attributes. * feat: use Phoenix.HTML as the default engine I am choosing to leverage this library in order to quickly get dynamic attributes (see #183) up and running. This also ensures that folks who wish to use Temple outside of a Phoenix project with get some nice HTML functions as well as properly escaped HTML out of the box. This can be made optional if Temple becomes decoupled from the render and it including HTML specific packages becomes a strange. * feat: Allow user to make their own Component module The component module is essentially to defer compiling functions that the user might not need. The component, render_slot, and inner_block functions are only mean to be used when there isn't another implementation. In the case of a LiveView application, LiveView is providing the component runtime implementation. This was causing some compile time warnings for temple, because it was using the LiveView engine at compile time (for Temple, not the user's application) and LiveView hadn't been compiled or loaded. So, now we defer this to the user to make their own module and import it where necessary. * feat: Pass dynamic attributes with the :rest! attribute The :rest! attribute can be used to pass in a dynamic list of attributes to be mixed into the static ones at runtime. Since this cannot be properly escaped by any engine, we have to mark it as safe and then allow the function to escape it for us. I decided to leverage the `attributes_escape/1` function from `phoenix_html`. There isn't really any point in making my own version of this or vendoring it. Now you can also pass a variable as the attributes as well if you only want to pass through attributes from a calling component. The :rest! attribute also works with components, allowing you to pass a dynamic list of args into them. Fixes #183 * Move test components to their own file. * docs(components): Update documentation on Temple.Components * docs(guides): Mention attributes_escape/1 function in the guides * chore(test): Move helper to it's own module * feat: rest! support for slots * docs(guides): Dynamic attributes * ci: downgrade runs-on to support OTP 23
2023-01-21 11:44:29 +00:00
### Dynamic Attributes
Dynamic Attributes (#190) * Move directories for ast tests to match convention * feat!: Rename `:let` to `:let!` We use the "bang" style as the reserved keyword to differentiate it from other possible attributes. * feat: use Phoenix.HTML as the default engine I am choosing to leverage this library in order to quickly get dynamic attributes (see #183) up and running. This also ensures that folks who wish to use Temple outside of a Phoenix project with get some nice HTML functions as well as properly escaped HTML out of the box. This can be made optional if Temple becomes decoupled from the render and it including HTML specific packages becomes a strange. * feat: Allow user to make their own Component module The component module is essentially to defer compiling functions that the user might not need. The component, render_slot, and inner_block functions are only mean to be used when there isn't another implementation. In the case of a LiveView application, LiveView is providing the component runtime implementation. This was causing some compile time warnings for temple, because it was using the LiveView engine at compile time (for Temple, not the user's application) and LiveView hadn't been compiled or loaded. So, now we defer this to the user to make their own module and import it where necessary. * feat: Pass dynamic attributes with the :rest! attribute The :rest! attribute can be used to pass in a dynamic list of attributes to be mixed into the static ones at runtime. Since this cannot be properly escaped by any engine, we have to mark it as safe and then allow the function to escape it for us. I decided to leverage the `attributes_escape/1` function from `phoenix_html`. There isn't really any point in making my own version of this or vendoring it. Now you can also pass a variable as the attributes as well if you only want to pass through attributes from a calling component. The :rest! attribute also works with components, allowing you to pass a dynamic list of args into them. Fixes #183 * Move test components to their own file. * docs(components): Update documentation on Temple.Components * docs(guides): Mention attributes_escape/1 function in the guides * chore(test): Move helper to it's own module * feat: rest! support for slots * docs(guides): Dynamic attributes * ci: downgrade runs-on to support OTP 23
2023-01-21 11:44:29 +00:00
To render dynamic attributes into a tag, you can pass them with the reserved attribute `:rest!`.
```elixir
2023-01-21 12:08:22 +00:00
assigns = %{
Dynamic Attributes (#190) * Move directories for ast tests to match convention * feat!: Rename `:let` to `:let!` We use the "bang" style as the reserved keyword to differentiate it from other possible attributes. * feat: use Phoenix.HTML as the default engine I am choosing to leverage this library in order to quickly get dynamic attributes (see #183) up and running. This also ensures that folks who wish to use Temple outside of a Phoenix project with get some nice HTML functions as well as properly escaped HTML out of the box. This can be made optional if Temple becomes decoupled from the render and it including HTML specific packages becomes a strange. * feat: Allow user to make their own Component module The component module is essentially to defer compiling functions that the user might not need. The component, render_slot, and inner_block functions are only mean to be used when there isn't another implementation. In the case of a LiveView application, LiveView is providing the component runtime implementation. This was causing some compile time warnings for temple, because it was using the LiveView engine at compile time (for Temple, not the user's application) and LiveView hadn't been compiled or loaded. So, now we defer this to the user to make their own module and import it where necessary. * feat: Pass dynamic attributes with the :rest! attribute The :rest! attribute can be used to pass in a dynamic list of attributes to be mixed into the static ones at runtime. Since this cannot be properly escaped by any engine, we have to mark it as safe and then allow the function to escape it for us. I decided to leverage the `attributes_escape/1` function from `phoenix_html`. There isn't really any point in making my own version of this or vendoring it. Now you can also pass a variable as the attributes as well if you only want to pass through attributes from a calling component. The :rest! attribute also works with components, allowing you to pass a dynamic list of args into them. Fixes #183 * Move test components to their own file. * docs(components): Update documentation on Temple.Components * docs(guides): Mention attributes_escape/1 function in the guides * chore(test): Move helper to it's own module * feat: rest! support for slots * docs(guides): Dynamic attributes * ci: downgrade runs-on to support OTP 23
2023-01-21 11:44:29 +00:00
data: [data_foo: "hi"]
}
temple do
Dynamic Attributes (#190) * Move directories for ast tests to match convention * feat!: Rename `:let` to `:let!` We use the "bang" style as the reserved keyword to differentiate it from other possible attributes. * feat: use Phoenix.HTML as the default engine I am choosing to leverage this library in order to quickly get dynamic attributes (see #183) up and running. This also ensures that folks who wish to use Temple outside of a Phoenix project with get some nice HTML functions as well as properly escaped HTML out of the box. This can be made optional if Temple becomes decoupled from the render and it including HTML specific packages becomes a strange. * feat: Allow user to make their own Component module The component module is essentially to defer compiling functions that the user might not need. The component, render_slot, and inner_block functions are only mean to be used when there isn't another implementation. In the case of a LiveView application, LiveView is providing the component runtime implementation. This was causing some compile time warnings for temple, because it was using the LiveView engine at compile time (for Temple, not the user's application) and LiveView hadn't been compiled or loaded. So, now we defer this to the user to make their own module and import it where necessary. * feat: Pass dynamic attributes with the :rest! attribute The :rest! attribute can be used to pass in a dynamic list of attributes to be mixed into the static ones at runtime. Since this cannot be properly escaped by any engine, we have to mark it as safe and then allow the function to escape it for us. I decided to leverage the `attributes_escape/1` function from `phoenix_html`. There isn't really any point in making my own version of this or vendoring it. Now you can also pass a variable as the attributes as well if you only want to pass through attributes from a calling component. The :rest! attribute also works with components, allowing you to pass a dynamic list of args into them. Fixes #183 * Move test components to their own file. * docs(components): Update documentation on Temple.Components * docs(guides): Mention attributes_escape/1 function in the guides * chore(test): Move helper to it's own module * feat: rest! support for slots * docs(guides): Dynamic attributes * ci: downgrade runs-on to support OTP 23
2023-01-21 11:44:29 +00:00
div id: "foo", rest!: @data do
"Hello, world!"
end
end
```
Dynamic Attributes (#190) * Move directories for ast tests to match convention * feat!: Rename `:let` to `:let!` We use the "bang" style as the reserved keyword to differentiate it from other possible attributes. * feat: use Phoenix.HTML as the default engine I am choosing to leverage this library in order to quickly get dynamic attributes (see #183) up and running. This also ensures that folks who wish to use Temple outside of a Phoenix project with get some nice HTML functions as well as properly escaped HTML out of the box. This can be made optional if Temple becomes decoupled from the render and it including HTML specific packages becomes a strange. * feat: Allow user to make their own Component module The component module is essentially to defer compiling functions that the user might not need. The component, render_slot, and inner_block functions are only mean to be used when there isn't another implementation. In the case of a LiveView application, LiveView is providing the component runtime implementation. This was causing some compile time warnings for temple, because it was using the LiveView engine at compile time (for Temple, not the user's application) and LiveView hadn't been compiled or loaded. So, now we defer this to the user to make their own module and import it where necessary. * feat: Pass dynamic attributes with the :rest! attribute The :rest! attribute can be used to pass in a dynamic list of attributes to be mixed into the static ones at runtime. Since this cannot be properly escaped by any engine, we have to mark it as safe and then allow the function to escape it for us. I decided to leverage the `attributes_escape/1` function from `phoenix_html`. There isn't really any point in making my own version of this or vendoring it. Now you can also pass a variable as the attributes as well if you only want to pass through attributes from a calling component. The :rest! attribute also works with components, allowing you to pass a dynamic list of args into them. Fixes #183 * Move test components to their own file. * docs(components): Update documentation on Temple.Components * docs(guides): Mention attributes_escape/1 function in the guides * chore(test): Move helper to it's own module * feat: rest! support for slots * docs(guides): Dynamic attributes * ci: downgrade runs-on to support OTP 23
2023-01-21 11:44:29 +00:00
will render to
```html
Dynamic Attributes (#190) * Move directories for ast tests to match convention * feat!: Rename `:let` to `:let!` We use the "bang" style as the reserved keyword to differentiate it from other possible attributes. * feat: use Phoenix.HTML as the default engine I am choosing to leverage this library in order to quickly get dynamic attributes (see #183) up and running. This also ensures that folks who wish to use Temple outside of a Phoenix project with get some nice HTML functions as well as properly escaped HTML out of the box. This can be made optional if Temple becomes decoupled from the render and it including HTML specific packages becomes a strange. * feat: Allow user to make their own Component module The component module is essentially to defer compiling functions that the user might not need. The component, render_slot, and inner_block functions are only mean to be used when there isn't another implementation. In the case of a LiveView application, LiveView is providing the component runtime implementation. This was causing some compile time warnings for temple, because it was using the LiveView engine at compile time (for Temple, not the user's application) and LiveView hadn't been compiled or loaded. So, now we defer this to the user to make their own module and import it where necessary. * feat: Pass dynamic attributes with the :rest! attribute The :rest! attribute can be used to pass in a dynamic list of attributes to be mixed into the static ones at runtime. Since this cannot be properly escaped by any engine, we have to mark it as safe and then allow the function to escape it for us. I decided to leverage the `attributes_escape/1` function from `phoenix_html`. There isn't really any point in making my own version of this or vendoring it. Now you can also pass a variable as the attributes as well if you only want to pass through attributes from a calling component. The :rest! attribute also works with components, allowing you to pass a dynamic list of args into them. Fixes #183 * Move test components to their own file. * docs(components): Update documentation on Temple.Components * docs(guides): Mention attributes_escape/1 function in the guides * chore(test): Move helper to it's own module * feat: rest! support for slots * docs(guides): Dynamic attributes * ci: downgrade runs-on to support OTP 23
2023-01-21 11:44:29 +00:00
<div id="foo" data-foo="hi">
Hello, world!
</div>
```
## Elixir Expressions
Any Elixir expression can be used anywhere inside of a Temple template. Here are a few examples.
```elixir
temple do
h2 do: "Members"
ul do
for member <- @members do
li do: member
end
end
end
```
### Match Expressions
Match expression are handled slightly differently. Generally if you are assigning an expression to a variable (a match), you are going to use that binding later and do _not_ want to emit it into the document.
So, match expressions are _not_ emitted into the document. They are functionally equivalent to the `<% .. %.` syntax of `EEx`. The expression is evaluated, but not included in the rendered document.
Typically you should not be writing this type of expression inside of your template, but if you wanted to declare an alias, you would need to write the following to not emit the alias into the document.
```elixir
temple do
_ = alias My.Deep.Module
div do
Module.func()
end
end
```
## Assigns
Since Temple uses the `EEx.SmartEngine` by default, you are able to use the assigns feature.
The assigns feature allows you to ergonomically access the members of a `assigns` variable by the `@` macro.
The assign variable just needs to exist within the scope of the template (the same as a normal `EEx` template that uses `EEx.SmartEngine`), it can be a function parameter or created inside the function.
```elixir
def card(assigns) do
temple do
div class: "card" do
section class: "card-header" do
@name
end
section class: "card-body" do
@bio
end
if Enum.any?(@socials) do
section class: "card-footer" do
for social <- @socials do
a href: social.link do
social.name
end
end
end
end
end
end
end
```