Episode #351

Leaf Templates

Series: Server-side Swift with Vapor

13 minutes
Published on August 24, 2018

This video is only available to subscribers. Get access to this video and 578 others.

Leaf is Vapor's component for rendering dynamic templates. Rather than writing HTML strings by hand in our router, we can write leaf templates that allow us to mix HTML with code. Since Leaf is a separate package, we will show how to integrate this into your project from scratch, to get an overview of how dependencies are assembled in a Vapor project.

Episode Links

Starting from Scratch

We'll start a new project and start building it:

$ vapor new leaf-example
$ cd leaf-example
$ vapor build

Once this is done, we'll generate an Xcode project for it...

$ vapor xcode

Removing Unused Code

This project comes with some stuff we don't need (yet). Let's delete it so we can focus just on Leaf templates.

In Package.swift, remove the package line for fluent-sqlite as well as the dependency FluentSQLite in the targets section.

We'll also delete the Controllers and Models folder.

In the configure.swift, delete all of the lines that mention databases, migrations, or Fluent. We'll cover this later.

Now build and ensure there are no errors.

Adding Leaf

In Package.swift, we'll add the dependency:

.package(url: "https://github.com/vapor/leaf.git", from: "3.0.1")

The version number here matters, and you should choose the latest version that is compatible with your version of Vapor.

We'll also need to ensure that our App target is properly linked to this new dependency. To do that, update the dependencies line to include "Leaf" like this:

    .target(name: "App", dependencies: ["Vapor", "Leaf"])

Now that we've declared that we depend on Leaf, we need to tell vapor to download and build it again:

$ vapor build

Since we've changed our project's settings, we'll also need to regenerate the Xcode project:

$ vapor xcode

Configuring Leaf

Open up configure.swift. We need to import Leaf at the top of the file, then add these lines to the configure function:

try services.register(LeafProvider())
config.prefer(LeafRenderer.self, for: ViewRenderer.self)

Using Leaf

Let's go to our routes file and add a route that renders a view.

router.get("hello") { req in 
    return try req.view().render("hello")

This will look for Resources/Views/hello.leaf, so we'll need to create those directories and then create an empty file called hello.leaf.

<h1>Hello from Leaf!</h1>

If we build and run this we can visit http://localhost:8080/hello in our browser to see our new template being rendered!

Passing Dynamic Data to Leaf

Let's create a new route that accepts a username, like hello/ben. We'll pass this data to the view with some structured data.

The context we'll pass to the view can be anything that conforms to Codable.

struct UserPage {
    var user: String

Then we can define our route that passes this data to the view...

router.get("hello", String.parameter) { req in 
    let user = try req.parameters.next(String.self)
    let context = UserPage(user: user)
    return try req.view().render("hello", context)

This may confuse the compiler a bit, and we'll need to help it out by describing our return type explicitly like this:

router.get("hello", String.parameter) { req -> Future<View> in 

What is a future? Don't worry about this for now, we will dig into this in detail in a future episode.

Now, back in hello.leaf, add the following:

<h2>You are logged in as #(user)</h2>

Note that the context object we passed is directly accessible to the template. The local members of the object become variables that we can access in the template.

We'll look at this in more depth later in this series.

Make sure to re-build and run the server to pick up the route changes we made. Then navigate to http://localhost:8080/hello/ben and see your dynamic data!

This episode uses Xcode 9.4.1, Swift 4.1, Leaf 3.0.1, Vapor 3.0.8.