Diiaablo
Diiaablo

Reputation: 176

Cannot render a computed property with Leaf in Vapor 3

I've been trying to render a computed property using the templating engine Leaf with Vapor 3, but so far no luck. If the same property is stored, everything works fine. If I make it computed, nothing is displayed in the web page. I can understand the reasons why such solution wouldn't be working, but I'd like to know if there's any way to make it working.

Code for the element to render:

struct SocialLinkContext: Encodable, CSSStyleable {
    let hyperlinkURI: String
    let classes: [String]
    //let styleClasses: String

    init(hyperlinkURI: String, classes: [String]) {
        self.hyperlinkURI = hyperlinkURI
        self.classes = classes
        //self.styleClasses = classes.joined(separator: " ")
    }

    //Seems not to be supported by Leaf
    var styleClasses: String {
        return self.classes.joined(separator: " ")
    }
}

Part of the main_page.leaf file:

...
<div class="row banner">
    <div class="banner-text">
        <h1 class="responsive-headline">Hello, world!</h1>
        <hr />
    </div>
    #embed("social_links")
</div>
...

The social_links.leaf file:

<ul class="social">
    #for(socialContext in socialLinks) {
        <li>
            <a href="#(socialContext.hyperlinkURI)">
                <i class="#(socialContext.styleClasses)"></i>
            </a>
        </li>
    }
</ul>

The PersonalPageContext to be passed to the View Renderer:

struct PersonalWebPageContext: Encodable {
    ...
    let socialLinks: [SocialLinkContext] = [...]
    ...
}

And finally, the PersonalWebPageViewController controller:

private func serveWebPage(req: Request) throws -> Future<View> {
    return try req.view().render("main_page", PersonalWebPageContext())
}

Upvotes: 10

Views: 500

Answers (1)

Caleb Kleveter
Caleb Kleveter

Reputation: 11494

This is because Leaf uses Codable to convert your context type to data that Leaf can use. Now by default, Codable does not encode or decode computed properties. If you want to do that, you have to manually implement the encode(to:) method.

struct SocialLinkContext: Encodable, CSSStyleable {
    let hyperlinkURI: String
    let classes: [String]

    var styleClasses: String {
        return self.classes.joined(separator: " ")
    }

    init(hyperlinkURI: String, classes: [String]) {
        self.hyperlinkURI = hyperlinkURI
        self.classes = classes
    }

    func encode(to encoder: Encoder)throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.hyperlinkURI, forKey: .hyperlinkURI)
        try container.encode(self.classes, forKey: .classes)
        try container.encode(self.styleClasses, forKey: .styleClasses)
    }

    enum CodingKeys: String, CodingKey {
        case hyperlinkURI, classes, styleClasses
    }
}

Upvotes: 11

Related Questions