Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: autoform #28

Open
ghost opened this issue Nov 14, 2015 · 13 comments
Open

Feature: autoform #28

ghost opened this issue Nov 14, 2015 · 13 comments

Comments

@ghost
Copy link

ghost commented Nov 14, 2015

Creation and validation of forms is IMO the most boring part of web-development. Let's make it fun again. The use-case is:

  1. End-developer describes their data once.
  2. Goal toolkit generates the code that can be used for both form generation and its validation.

We could possibly use struct tags for that. E.g.:

type User struct {
    Name        string `autoform:"required=true,minsize=2,maxsize=100"`
    Email       string `autoform:"email=true"`
    Age         int    `autoform:"min=13,max=150"`
    CountryCode string `autoform:"length=2"`
    CardNumber  string
}

Though this is not type safe. Alternatively, we can apply a "magic methods" approach we use for the controllers.

Related functionality in other frameworks

  1. https://docs.djangoproject.com/en/1.7/topics/forms/modelforms/
  2. https://www.playframework.com/documentation/2.1.0/JavaForms
@ghost ghost added this to the v0.0.3 milestone Nov 14, 2015
@ghost ghost mentioned this issue Jan 27, 2016
@xpbliss
Copy link

xpbliss commented Jan 31, 2016

Looking forward to the autoform !

@ghost
Copy link
Author

ghost commented Feb 4, 2016

OK, first the validation part of autoforms. I lean toward the following option:

  • There is a models package in user's app.
  • A combination of struct tags and methods is used:
package models

type User struct {
    Name string `autoform:"Required(true) LenRange(2,64)"`
    Age  int `autoform:"Min(12) Max(120)"`
}

// `field` is a name of struct's field (e.g. "Name" or "Age").
// `value` is what we receive from user (it's automatically converted from `string`).
//
// the rest of parameters are extracted from struct tags.

func (m *User) Required(field, value string, required bool) error {
    if value == "" {
        return fmt.Errorf("%v must not be empty", field)
    }
    return nil
}

func (m *User) Min(field string, value int, min int) error {
    if value > min {
        return fmt.Errorf("%v must be less than %d", field, min, max)
    }
    return nil
}

func (m *User) LenRange(field, value string, min, max int) error {...}
func (m *User) Max(field string, value int, max int) error {...}
  • There will be packages with default validators that would let shorten the example above to the following:
type User struct {
    *text.Validators
    *integers.Validators

    Name string `autoform:"Required(true) LenRange(2,64)"`
    Age  int `autoform:"Min(12) Max(120)"`
}

So the validators won't be built in but rather the mechanism will be extendible and customizable.


Now a few things:

  • I'm not really comfortable with the syntax of the struct tags. There is probably a way to improve it:
`Required(true) LenRange(2,64)`
  • The field, value string part of validators' input arguments looks ugly. Too much boilerplate code.
  • Consider the following use-case: password and repeat password fields. How can we validate it with this approach. Field name and value is not enough for this. We want to know the values of neighbouring fields.

@ghost ghost self-assigned this Feb 4, 2016
@ghost ghost modified the milestones: v0.0.2, v0.0.3 Feb 4, 2016
@ghost ghost mentioned this issue Feb 13, 2016
7 tasks
@ghost ghost removed the in-progress label Feb 17, 2016
@ghost
Copy link
Author

ghost commented Mar 6, 2016

@xpbliss Thank you for your input, I'll check those packages out.

@xpbliss
Copy link

xpbliss commented Mar 9, 2016

Why not release new updates for the goal?

@xpbliss
Copy link

xpbliss commented May 23, 2016

How about the auto form?

@hqdo
Copy link

hqdo commented May 31, 2016

Since this a code gen tool, would it be a good idea to generate the struct from a database schema ?

@hqdo
Copy link

hqdo commented May 31, 2016

With regards to the validation case for password, confirm password, you probably need a method to validate a form in it's entirety after all fields have been validated. It may not always make sense to add a validation to a specific field. This method will probably need access to the form post parameters as well.

@ghost
Copy link
Author

ghost commented May 31, 2016

With regards to the validation case for password, confirm password, you probably need a method to validate a form in it's entirety after all fields have been validated.

@hqdo, yeah you're right. I'll keep this use-case in mind.

Since this a code gen tool, would it be a good idea to generate the struct from a database schema?

Yes, it is a good idea. For now the priority is to implement autoform to support:

  • Auto binding of forms.
// SomeAction is a sample action. Its form's fields will be automatically binded
// to the values in r.Request.Form.
func (c *Controller) SomeAction(form MyForm) http.Handler {
}
  • Auto validation of form's fields.
if errs := form.Errors() {
    ...
}
  • Auto generation of forms.
c.Context["myForm"] = form
return c.Render()

As soon as this is ready we can focus on generation of structs from DB schema.

@hqdo
Copy link

hqdo commented May 31, 2016

Why not make goal generate everything statically from a defined schema (could be the Struct in your examples) or from database ? binding, validation, forms (template) all statically generated.

This means no magic, no reflection (during runtime) and much easier for the user to customize in a real application. I've found auto generators (eg. django) impressive for quick demos, but are a pain to customise when you start needing to make changes to the runtime generated stuff.

@ghost
Copy link
Author

ghost commented May 31, 2016

@hqdo Well, that's how we are going to do it. Everything is generated from structs. And then can be used from actions. No runtime reflection is intended.

@xpbliss
Copy link

xpbliss commented May 31, 2016

looking forward to it !
the soon the better!

@hqdo
Copy link

hqdo commented May 31, 2016

@alkchr sorry i may have misunderstood. seeing the following code from your example, i thought the form rendering would be a black box method. Sounds good if everything is generated and we can freely edit the default output.

c.Context["myForm"] = form
return c.Render()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants