Vapor 3 rendering complex call in Leaf

202 Views Asked by At

Here's the call that works with my API, but I need to render the result in leaf. This currently works fine with the API. Just not sure how where I'm grabbing the teams.

func getTeamsWithUsersForEvent(_ req: Request) throws -> Future<[TeamWithUsers]> {
        let currentID =  try req.parameters.next(Int.self)
        print("currentID \(currentID)")
        return Team.query(on: req).filter(\Team.eventID == currentID).sort(\Team.rawScore, .descending).range(..<5).all().flatMap(to: [TeamWithUsers].self) { team in
            try team.map { team in
                try team.users.query(on: req).all().map { users in
                    var score: Int
                    if users.isEmpty {
                        score = 0
                    }else{
                        score = team.rawScore / users.count
                    }
                    return TeamWithUsers(
                        id: team.id,
                        name: team.name,
                        icon: team.icon,
                        eventID: team.eventID,
                        rawScore: team.rawScore,
                        //users: users,
                        count: users.count,
                        teamScore: score
//                        teamScore: team.rawScore / users.count
                    )
                }
            }.flatten(on: req)
        }
    }

struct TeamWithUsers: Content {
    let id: Int?
    let name: String
    let icon: String
    let eventID: Event.ID
    let rawScore: Int
    //let users: [User]
    let count: Int?
    let teamScore: Int
}

Here's the what I've started. This is my context.

struct AllTeamsContext: Encodable {
  let title: String
  let teams: [TeamWithUsers]
}

And I've created the Leaf template called "Leaders" which just shows a table with TeamWithUsers per row.

#if(count(teams) > 0) {
  <table class="table table-bordered table-hover">
    <thead class="thead-light">
      <tr>
        <th>Name</th>
        <th>Icon</th>
        <th>Score</th>
      </tr>
    </thead>
    <tbody>
      #for(team in teams) {
        <tr>
          <td>#(team.name)</td>
          <td>#(team.icon)</td>
          <td>#(team.teamScore)</td>
        </tr>
      }
    </tbody>
  </table>
} else {
  <h2>There aren’t any teams yet.</h2>
}
1

There are 1 best solutions below

3
On

EDIT: changed answer in the light of the additional information in the comments.

Whilst you could change your existing function to return the view directly, I personally would leave it as-is and call it from a second that renders the view. Try putting this in the same router as your current function:

func teamsView(_ req: Request) throws -> Future<View> {
   let currentID =  try req.parameters.next(Int.self)
   let teamsContext = getTeamsWithUsersForEvent(request, currentID)
   return request.view().render("Leaders", teamsContext )
}

Then, in your routes.swift or equivalent, put:

router.grouped("admin","teams") teamsRoute.get("leaders", Int.parameter, use: teamsView)

Move the parameter into the tsamsview function above and then you need to modify your original function header to be:

func getTeamsWithUsersForEvent(_ req: Request, _ currentID:Int) throws -> Future<[TeamWithUsers]>

Then remove your original parameter extraction from this function.

As your router is grouped your view should be visible on:

http://localhost:8080/admin/teams/nnn/leaders

where nnn is what will get translated into currentID.

ALTERNATIVELY:

If this is getting a bit messy (and in spite of my first comment about preferring to keep the functions separate), you could always alter your existing function to:

func getTeamsWithUsersForEvent(_ req: Request) throws -> Future<View> {
    let currentID =  try req.parameters.next(Int.self)
    print("currentID \(currentID)")
    let teamContext = Team.query(on: req).filter(\Team.eventID == currentID).sort(\Team.rawScore, .descending).range(..<5).all().flatMap(to: [TeamWithUsers].self) { team in
        try team.map { team in
            try team.users.query(on: req).all().map { users in
                var score: Int
                if users.isEmpty {
                    score = 0
                } else {
                    score = team.rawScore / users.count
                }
                return TeamWithUsers(
                    id: team.id,
                    name: team.name,
                    icon: team.icon,
                    eventID: team.eventID,
                    rawScore: team.rawScore,
                    //users: users,
                    count: users.count,
                    teamScore: score
                )
            }
        }.flatten(on: req)
    }
    return request.view().render("Leaders", teamContext )
}