gflo
gflo

Reputation: 306

Vapor 3 - How to populate array property of model from Leaf template form

I have a Group, User and an App model. Within my Group model, I have a property var apps: [App] and I create a sibling relationship bw Group and User.

In my WebsiteController, I have 2 handlers:

  1. createGroupHandler which handles the GET request at /groups/create
  2. createGroupPostHandler which handles the POST request at /groups/create

My problem is that after selecting app objects in my form in my createGroup.leaf template, when I create my new group no apps are being populated into my apps array.

I created structs to represent the context being rendered in my createGroupHandler and to handle the data being passed in the post request for createGroupPostHandler

struct CreateGroupContext: Encodable {
    let title = "Create Group"
    let users: Future<[User]>
    let apps: Future<[App]>
}

struct CreateGroupData: Content {
    let name: String
    let apps: [App]
    let users: [String]?
}

The apps are properly loaded into the form but despite selecting them like in the image below, they aren't added to the array enter image description here

My createGroupPostHandler looks like below. I don't know how to grab the selected apps and populate my apps: [App] array when I create my group is my issue, I feel like this is where I should be doing that, but I don't know how to grab that from the Leaf template.

func createGroupPostHandler(_ req: Request, data: CreateGroupData) throws -> Future<Response> {
        let group = Group(name: data.name, apps: data.apps)
        return group.save(on: req).flatMap(to: Response.self) { group in
            guard let id = group.id else {
                throw Abort(.internalServerError)
            }

            var userSaves: [Future<Void>] = []
            for user in data.users ?? [] {
                try userSaves.append(User.addUser(user, to: group, on: req))
            }

            let redirect = req.redirect(to: "/groups/\(id)")
            return userSaves.flatten(on: req).transform(to: redirect)
        }
    }

This is what my createGroup.leaf looks like: enter image description here

Upvotes: 1

Views: 970

Answers (1)

Nick
Nick

Reputation: 5200

I use index notation of naming input tags in HTML, such as score[0], score[1], etc. Vapor is then quite happy to parse this and create a dictionary (okay, technically not an array!).

Use this notation in the Leaf file:

#for(result in results) {
    <input type="text" name="score[#(index)]" value="#(result.score)">
}

The controller function:

struct SaveResult:Content
{
    var score:[Int:String]
}

func saveRoll(_ request:Request) throws -> Future<View>
{
    return try request.content.decode(SaveResult.self).flatMap
    {
        result in
        for i in 0...(result.score.count) - 1
        {
            print( detail.score[i] )
        }
        return try self.index(request)
    }
}

Upvotes: 1

Related Questions