Skip to content

Form Controls and Model Field Annotations (Suggestion) #1047

Closed

Description

@DanWahlin

One concept that's available in some other frameworks is the ability to apply "annotations" directly to fields/properties. That way a model that's used in multiple views/apps is ready to go without much work in situations where the requirements stay the same from view to view or app to app. It's an optional approach but one that I've liked in some situations since it makes things easier to maintain IMO and allows an individual field/property to describe it's individual requirements. I haven't seen where TypeScript annotations can be applied yet so this may not even be possible, but I thought I'd throw this pseudo-code out there.

class Address {
 @Field([required, maxLength=100, two-way])
  street: string;

 @Field([required, maxLength=50, two-way])
  city: string;

 @Field([required, minLength=2, maxLength=2, two-way])
  state: string;

 @Field([required, zipCodeValidator, two-way])
  zip: string;

 @Field([required, one-way])
  residential: boolean;
} 

The "two-way" and "one-way" options (totally made these up :-)) could be used to control how binding occurs for each field so that this line could be eliminated potentially: this.form.changes.forEach(() => this.form.writeTo(this.address));

Some people don't like this type of information in the model (heard this many times with other frameworks that do it) but the nice thing with this approach is that you can do it whatever way you'd like. Using the FormBuilder.group() technique as shown at http://angularjs.blogspot.com/2015/03/forms-in-angular-2.html?m=1 or by annotating the model (assuming TypeScript annotations allow for that - I haven't seen the restrictions yet). FormBuilder.group() might look like this if the model is annotated:

this.address = new Address();
//FormBuilder injected into the constructor as "fb"
this.form = this.address

If a model defines it's own annotations but a fb.group() is also defined with individual form control fields in it then the local field definitions would "win" and be used instead of the model annotations. That would allow the flexibility to use either approach and even override the default model annotations as needed. People who don't want model annotations would simply use the standard fb.group() technique.

Activity

  1. changed the title [-]Form Controls and Field Annotations (Suggestion)[/-] [+]Form Controls and Model Field Annotations (Suggestion)[/+] on Mar 22, 2015
  2. vsavkin commented on Mar 26, 2015

    @vsavkin
    Contributor

    I think this is a good idea for many scenarios. We can spike it on top of the core forms module.

    I am thinking it should be a separate module for the following reason: the core forms module is compiled both to JS and Dart. If you want to reduce boilerplate to the minimum, you may have to take advantage of some language features that is available only in TS or only in Dart.

  3. added this to the milestone on Apr 28, 2015
  4. aeigenmann commented on May 20, 2015

    @aeigenmann

    +1

  5. removed this from the milestone on May 29, 2015
  6. 13 remaining items

  7. Lcng commented on Sep 9, 2016

    @Lcng

    I would like this form..
    `class Address {
    @required()
    @maxlength({value = 20})
    street: string;

    @required({message = 'Please enter the city.'})
    @minlength({value = 10, message = 'The minimum length of city value is 10.'})
    city: string;

    // ..
    } `

  8. added this to the Backlog milestone on Jan 23, 2018
  9. snebjorn commented on Apr 17, 2018

    @snebjorn

    I think at lot of inspiration/lessons can be taken from Tag Helpers in ASP,NET Razor

    class Address {
        @required()
        @maxlength({value = 20})
        @display({name = 'Street'})
        street: string;
    
        @required({message = 'Please enter the city.'})
        @minlength({value = 10, message = 'The minimum length of city value is 10.'})
        city: string;
    
        @required()
        @datatype({name = 'checkbox'}) // could be inferred from the type
        residential: boolean;
    
        @required()
        @zipCodeValidator() // custom validator
        zip: string;
    
        // ..
    }

    Usage in component

    ngOnInit() {
        this.myForm = new FormGroup<Address>();
    }

    Usage in view

    <form [formGroup]="myForm" novalidate (ngSubmit)="save(myForm.value, myForm.valid)">
    
        <!-- Form controls -->
    
        <button type="submit">Submit</button>
    </form>

    In the form controls section we could do either

    <fieldset>
        <label>Street</label>
        <input type="text" formControlName="street">
        <small [hidden]="myForm.controls.street.valid || (myForm.controls.street.pristine && !submitted)">
            Name is required (maximum 20 characters).
        </small>
    </fieldset>

    Or rely on templates

    <fieldset>
        <label label-for="street"></label>
        <input editor-for="street">
        <small validation-for="street"></small>
    </fieldset>

    Which probably could be simplified to

    <fieldset controlName="street"></fieldset>

    They all would generate the same end result, but with different customizability

    Taking it ever further. It would be possible to generate the entire form. (Inspired by Html.EditorForModel)

    <form [formGroup]="myForm" novalidate (ngSubmit)="save(myForm.value, myForm.valid)">
    
        <editor-for-group></editor-for-group>
    
        <button type="submit">Submit</button>
    </form>

    I've been quite happy with how the Razer tag helpers solved this requirement. So yeah an angular version heavily inspired by it would be neat.

    What is the motivation

    My end goal is to be able to generate forms based on the OpenAPI spec. Validation rules can be defined in it. https://swagger.io/specification/#parameterObject

    This would allow me to just write the backend, then use tools like NSwag to generate the WebAPI models and services. My forms would generated from the API models and presto large parts of the frontend have been auto generated :)

    This would also make it rather easy to apply API changes to the frontend as all that needs to be done is to re-generate the models. A bit naive - yes. But imagine the conf demos :D

  10. splincode commented on Jul 24, 2018

    @splincode
    Contributor

    @DanWahlin Is the issue relevant?

  11. DanWahlin commented on Jul 24, 2018

    @DanWahlin
    ContributorAuthor

    For a lot of people doing forms work I think it's still relevant since it offers a model-based way to apply validation to fields. It was a suggestion back in 2015 though and at this point I think the only way it'll ever be available in the framework is through a 3rd party project.

  12. splincode commented on Jul 24, 2018

    @splincode
    Contributor
  13. snebjorn commented on Jul 24, 2018

    @snebjorn

    @splincode thous two deal with transforming JSON fetched from an API into proper classes. This issues is about using classes/models as a basis for a form. A model-based form if you will.

  14. splincode commented on Jul 24, 2018

    @splincode
    Contributor

    @snebjorn I think the Angular team is not able to switch to a declarative style, because they use reactive forms as a goal

  15. splincode commented on Jul 24, 2018

    @splincode
    Contributor

    @snebjorn @mhevery Already many enthusiasts have tried to implement this and successfully in the form of a third-party solution ngx-dynamic-form-builder

    https://github.com/EndyKaufman/ngx-dynamic-form-builder

  16. DanWahlin commented on Jul 24, 2018

    @DanWahlin
    ContributorAuthor

    Since this concept was originally suggested back in 2015 and the Angular team doesn't have any plans to implement it at this point, I'm going to go ahead and close it.

  17. angular-automatic-lock-bot commented on Sep 13, 2019

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  18. locked and limited conversation to collaborators on Sep 13, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions