vapor-docs/2.0/docs/leaf/leaf.md

202 lines
5.5 KiB
Markdown

!!! warning
This section may contain outdated information.
# Leaf
Welcome to Leaf. Leaf's goal is to be a simple templating language that can make generating views easier. There are plenty of great templating languages, so use what's best for you -- maybe that's Leaf! The goals of Leaf are:
- Small set of strictly enforced rules
- Consistency
- Parser first mentality
- Extensibility
## Syntax
### Structure
Leaf Tags are made up of 4 Elements:
- Token: `#` is the Token
- Name: A `string` that identifies the tag
- Parameter List: `()` May accept 0 or more arguments
- Body (optional): `{}` Must be separated from the Parameter List by a space
There can be many different usages of these 4 elements depending on the Tag's implementation. Let's look at a few examples of how Leaf's built-in Tags might be used:
- `#()`
- `#(variable)`
- `#import("template")`
- `#export("link") { <a href="#()"></a> }`
- `#index(friends, "0")`
- `#loop(friends, "friend") { <li>#(friend.name)</li> }`
- `#raw() { <a href="#raw">Anything goes!@#$%^&*</a> }`
### Using the `#` token in HTML
The `#` token cannot be escaped. Use the `#()` or `#raw() {}` Tag to output a `#` in a Leaf Template. `#()` => `#`
### Raw HTML
All Leaf output is escaped by default. Use the `#raw() {}` Tag for unescaped output.
`#raw() { <a href="#link">Link</a> }` => `<a href="#link">Link</a>`
> IMPORTANT! Make sure you are not using the `#raw() {}` Tag with user input.
### Chaining
The double token: `##` indicates a chain. It can be applied to any standard Tag. If the previous Tag fails, the chained Tag will be given an opportunity to run.
```
#if(hasFriends) ##embed("getFriends")
```
### Leaf's built-in Tags
#### Token: `#()`
```
#() #()hashtags #()FTW => # #hashtags #FTW
```
#### Raw: `#raw() {}`
```
#raw() {
Do whatever w/ #'s here, this code won't be rendered as leaf document and is not escaped.
It's a great place for things like Javascript or large HTML sections.
}
```
#### Equal: `#equal(lhs, rhs) {}`
```
#equal(leaf, leaf) { Leaf == Leaf } => Leaf == Leaf
#equal(leaf, mustache) { Leaf == Mustache } =>
```
#### Variable: `#(variable)`
```
Hello, #(name)!
```
#### Loop: `#loop(array, "item") {}`
```
#loop(friends, "friend") {
#(offset). #(friend.name)
}
```
The body of a `#loop` can access an `index` variable corresponding to the index of the array. There is also an `offset` variable which is the index plus 1.
#### Index: `#index(array, _ index: String)`
```
Hello, #index(friends, "0")!
Hello, #index(friends, "best")!
```
Note: array indexes are always strings.
#### If - Else: `#if(bool) ##else() { this }`
```
#if(entering) {
Hello, there!
} ##if(leaving) {
Goodbye!
} ##else() {
I've been here the whole time.
}
```
Note that `#if` requires a boolean variable. If you need to do a comparison then `#equal` is more appropriate. You can chain `#equal` in the same way as `#if`:
```
#equal(state, "0") {
<span class="green">Normal</span>
} ##equal(state, "1") {
<span class="orange">Warning</span>
} ##else() {
<span class="red">Alert</span>
}
```
#### Import: `#import("template")`
#### Export: `#export("template") { Leaf/HTML }`
#### Extend: `#extend("template")`
#### Embed: `#embed("template")`
> When using these Layout Tags, omit the template file's .leaf extension.
```
/// base.leaf
<!DOCTYPE html>
#import("html")
/// html.leaf
#extend("base")
#export("html") { <html>#embed("body")</html> }
/// body.leaf
<body></body>
```
Leaf renders `html.leaf` as:
```
<!DOCTYPE html>
<html><body></body></html>
```
### Custom Tags
Look at the existing tags for advanced scenarios, let's look at a basic example by creating `Index` together. This tag will take two arguments, an array, and an index to access.
```swift
class Index: BasicTag {
let name = "index"
func run(arguments: [Argument]) throws -> Node? {
guard
arguments.count == 2,
let array = arguments[0].value?.nodeArray,
let index = arguments[1].value?.int,
index < array.count
else { return nil }
return array[index]
}
}
```
We can now register this Tag in our `main.swift` file with:
```swift
if let leaf = drop.view as? LeafRenderer {
leaf.stem.register(Index())
}
```
And use it just like we did [above](#index).
> Note: Use of non-alphanumeric characters in Tag Names is **strongly discouraged** and may be disallowed in future versions of Leaf.
## Syntax Highlighting
### Atom
[language-leaf](https://atom.io/packages/language-leaf) by ButkiewiczP
### Xcode
It is not currently possible to implement Leaf Syntax Highlighting in Xcode, however, using Xcode's HTML Syntax Coloring can help a bit. Select one or more Leaf files and then choose Editor > Syntax Coloring > HTML. Your selected Leaf files will now use Xcode's HTML Syntax Coloring. Unfortunately the usefulness of this is limited because this association will be removed when `vapor xcode` is run.
There appears to be a way to [make Xcode file associations persist](http://stackoverflow.com/questions/9050035/how-to-make-xcode-recognize-a-custom-file-extension-as-objective-c-for-syntax-hi) but that requires a bit more kung-fu.
### VS Code
[html-leaf](https://marketplace.visualstudio.com/items?itemName=Francisco.html-leaf) by FranciscoAmado
### CLion & AppCode
Some preliminary work has been done to implement a Leaf Plugin for CLion & AppCode but lack of skill and interest in Java has slowed progress! If you have IntelliJ SDK experience and want to help with this, message Tom Holland on [Vapor Slack](http://vapor.team)