Server Side Swift with Vapor - Part 19: Powerful | Ray Wenderlich

Learn how to build powerful templates using for loops and if expressions to generate HTML.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/4493-server-side-swift-with-vapor/lessons/19

It should be noted in the index.leaf file for the for loop, everything has to be on the single line in the loop (as shown in the video). Initially I let auto-complete format my HTML and when it was on separate lines, I got TemplateKit errors. This would suck if there’s a very long set of variables to use. Unless there is some formatting that is allowed…

JT

Yeah the #for(acronym in acronyms) must be on the same line, but it should work with things inside the brackets on separate lines, so your code shouldn’t get too long

Hmmm, that wasn’t working before… Perhaps with one of the updates it fixed it. I swear it wouldn’t let me put elements on different lines before. :slight_smile:

1 Like

How do we debug when this isn’t working? In my handler I’m assigning my equivalent of your acronyms.

func indexHandler(_ req: Request) throws -> Future<View> {
    return Client.query(on: req).all().flatMap(to: View.self) { clients in
        print(clients[0].contractStart)
        print(clients[0].pointsPerMonth)
        let context = ClientIndexContent(clients: clients.isEmpty ? nil : clients.sorted { $0.name.caseInsensitiveCompare($1.name) == .orderedAscending })
        return try req.leaf().render("client", context)
    }
}

Those two print statements are showing data that I would expect to see in the Xcode console window. In my table on the view though I only see two of the properties of the object showing any data. All my lines just say things like this:

<td>#(client.pointsPerMonth)</td>

The browser isn’t showing any errors in the console. Even if I type a completely invalid properly name there, like client.fooey it doesn’t print out anything.

@gargoyle Leaf will ignore empty variables to stop crashes when things don’t exist. What does your for loop look like in Leaf?

The way to debug if you’ve double checked spellings etc is to step into the code. Ensure that the context you pass in contains what you expect (check the variable name as well) then you can step into TemplateKit and look at the AST stuff that debugs it.

@0xtim It looks like so:

    #for(client in clients) {
      <tr>
        <td>
          <a href="/clients/#(client.id)/edit"><img src="/images/edit.png"></a>&nbsp;<a href="/clients/#(client.id)/delete"><img src="/images/delete.png"></a>
        </td>
        <td>#(client.name)</td>
        <td>#(client.pointsPerMonth)</td>

The name prints out, but then the pointsPerMonth is blank. From my previous post you can see that I’m printing out the exact same variable name in both spots, and since I see the one from Xcode’s console, I should see at least the first one in the HTML too :frowning:

@gargoyle is pointsPerMonth a non-optional property? Or is it a relation or computed property?

It’s just a normal Double, defined in the class as var pointsPerMonth = 0.0 and shows in the PostgreSQL database as points_per_month | double precision | not null. I’m using CodingKey to rename pointsPerMonth to points_per_month in the database itself.

Hmm could be a bug in Leaf - definitely looks like it should work! You can try breakpointing through TemplateConstant.swift and printing stuff out or register an issue on TemplateKit

Are leaf templates able to read computed properties? It doesn’t seem like it’s trying to do so.

@gargoyle no they can’t since they use Codable. If you want to pass in computed properties you’ll need a new data structure and add the computed properties in Swift in that