angular 4 own form element component

2019-05-29 10:54发布

I am beginner in angular and I may need a little help with this thing. First of all I will just show how could it work then I will write about how I want it to work.

This is the regular way when you work with ngModel:

    <form>
      <input name="name" placeholder="name" [(ngModel)]="model.name" value="" />
      <select name="select" [(ngModel)]="model.select">
         <option value="1">..</option>
         ...
      </select>
      <input type="button" value=" click me" />
    </form>

This is good enough if you don't want an own appearance for the select. My aim is to create a component for this where I can create divs and other contents that I can design to fit to the future display of this form element. On the other hand I want to keep the comfort of the ngModel. So the template should look like this in my aim:

    <form>
      <input name="name" placeholder="name" [(ngModel)]="model.name" value="" />
      <app-select label="label" name="select" [placeholder]="'placeholder'" [(ngModel)]="select" [options]="options"></app-select >
      <input type="button" value=" click me" />
    </form>

I already tried to create something like this but I have failed, sadly :( Could some who has more experience then me help me with this please? Thanks your time and answers in advance!

I can recreate something minimal if necessary here: https://stackblitz.com/

1条回答
太酷不给撩
2楼-- · 2019-05-29 11:10

Here is how I do this form my forms (keep in mind I am using reactive forms with the form builder, not template driven forms with ngModel):

First, I create a custom FormModule in a directory called forms.

Next, inside there I have various form components. Let's use the LoginFormComponent for example.

Here is the LoginFormComponent:

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
    selector: 'login-form',
    templateUrl: './login-form.component.html'
})
export class LoginFormComponent implements OnInit {
    @Output() onChange: EventEmitter<FormGroup> = new EventEmitter();

    form: FormGroup;

    constructor(private fb: FormBuilder) {
        this.form = this.fb.group({
            email: [null, Validators.email],
            password: [null, [Validators.required, Validators.minLength(6), Validators.maxLength(60)]]
        });

        this.form.valueChanges.subscribe(() => this.onChange.emit(this.form));
    }

    ngOnInit() {
        this.onChange.emit(this.form);
    }
}

Here is the HTML:

<form autocomplete="off" [formGroup]="form">
    <div class="form-group">
        <label for="username">Email *</label>
        <input type="text" id="username" class="form-control" placeholder="Enter your email address" formControlName="email" autofocus>
        <control-messages [control]="form.get('email')"></control-messages>
    </div>

    <label for="password">Password *</label>
    <input type="password" id="password" class="form-control" placeholder="Enter your password" formControlName="password">
    <control-messages [control]="form.get('password')" [label]="'Password'"></control-messages>
</form>

Now in any custom component that is within a module that imports FormModule, I can do the following:

app.component.html:

<login-form (onChange)="form = $event"></login-form>

<button (click)="submit()">Submit</button>

app.component.ts:

@Component({...})
export class AppComponent {
    form: FormGroup;

    submit() {
        // Do something with `this.form`.
    }
}

The benefit of this sort of design may not be super obvious right away, but it enables us to do a few things:

  1. First, we can re-use the login-form component anywhere in our app as long as we import our custom FormModule. Any if we change it, it will be updated everywhere automatically. Keeps things DRY.
  2. Next, we may not want the same submit button/text every where, and we may want it to do different things in different locations, so the login-form component only contains the form itself, and any submission logic is handled outside of it for reuse-ability. This matters when you have forms that are used for both creation and editing. The button on edit can save data, where as the button on create can create something new. If you put the submit button inside the form component, this reuse isn't easily feasible.

For slightly more complex forms, say a form that can take in some default values, take a look at this example TagFormComponent:

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

import { Tag } from 'shared';

@Component({
    selector: 'tag-form',
    templateUrl: './tag-form.component.html'
})
export class TagFormComponent implements OnInit {
    @Output() onChange: EventEmitter<FormGroup> = new EventEmitter();

    form: FormGroup;

    constructor(private fb: FormBuilder) {
        this.form = this.fb.group({
            name: [null, Validators.email]
        });

        this.form.valueChanges.subscribe(() => {
            this.onChange.emit(this.form);
        });
    }

    @Input()
    set tag(tag: Tag) {
        if (tag) {
            this.form.patchValue(tag);
        }
    }

    ngOnInit() {
        this.onChange.emit(this.form);
    }
}

This one follows the same general idea as the LoginFormComponent, except this one can pass in some default values for the form. Like this:

<tag-form [tag]="tag" (onChange)="form = $event"></tag-form>
查看更多
登录 后发表回答