如何在Angular中创建可重用的输入字段?

huangapple go评论88阅读模式
英文:

How can i create reusable input field in Angular?

问题

我正在尝试创建一个可重复使用的字段,就像我们在react中所做的那样。但我未能成功。我需要一些建议或指导来解决我的问题。实际上,我创建了一个包含输入字段的组件。现在我想在Angular表单中的任何地方使用该字段。请帮助我解决这个问题。

每当我尝试提交时,它总是返回undefined。

任何解决方案都会受到欢迎!

登录表单

<Modal [isOpen]="true" title="Login" actionLabel="Sign in" [onSubmit]="handleSubmit">
  <form [formGroup]="loginForm">
    <div class="flex flex-col gap-4">
      <app-input placeholder="Email" type="email" controlName="email"
        (formControlChange)="handleFormControl('email',$event)" />
      <app-input placeholder="Password" type="password" controlName="password"
        (formControlChange)="handleFormControl('password',$event)" />
    </div>
  </form>
</Modal>
export class LoginModalComponent implements OnInit {

  loginForm!: FormGroup;

  constructor(private fb: FormBuilder) { }

  ngOnInit(): void {
    this.loginForm = this.fb.group({
      email: new FormControl(''),
      password: new FormControl('')
    })
  }

  handleFormControl(formControlName: string, formControl: FormControl) {
    this.loginForm.setControl(formControlName, formControl);
  }

  handleSubmit(): void {
    console.log(this.loginForm);
  }
}

输入组件

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

@Component({
  selector: 'app-input',
  template: `
    <input
        [placeholder]="placeholder"
        [formControl]="formControl"
        class="
          w-full
          p-4
          text-lg
          bg-black
          border-2
          border-neutral-800
          rounded-md
          outline-none
          text-white
          focus:border-sky-500
          focus:border-2
          transition
          disabled:bg-neutral-900
          disabled:opacity-70
          disabled:cursor-not-allowed
        "
        [type]="type"
    >
  `,
  styles: [
  ]
})
export class InputComponent implements OnInit {
  @Input('placeholder') placeholder: string = '';
  @Input('controlName') controlName: string = '';
  @Input('type') type: string = 'text';
  @Input() required: boolean = false;
  @Input() email: boolean = false;

  @Output() formControlChange = new EventEmitter<FormControl>();
  formControl!: FormControl;

  ngOnInit(): void {
    this.initializeFormControl()
    this.subscribeToValueChange()
  }

  initializeFormControl(): void {
    const validators = [];
    if (this.required) {
      validators.push(Validators.required)
    }

    if (this.email) {
      validators.push(Validators.email);
    }

    this.formControl = new FormControl('', validators);
  }

  subscribeToValueChange(): void {
    this.formControl.valueChanges.subscribe((value) => {
      this.formControlChange.emit(this.formControl);
    })
  }
}
英文:

I am trying to create a reusable field as we do in react. But I failed to do that. I need some suggestions or guidance to fix my issue. Actually, I created a component that holds the input field. Now I want to use that field everywhere in the Angular forms. Please help me to fix this.

>Whenever i try to submit it always return undefined.

Any solution appreciated!

Login Form

&lt;Modal [isOpen]=&quot;true&quot; title=&quot;Login&quot; actionLabel=&quot;Sign in&quot; [onSubmit]=&quot;handleSubmit&quot;&gt;
&lt;form [formGroup]=&quot;loginForm&quot;&gt;
&lt;div class=&quot;flex flex-col gap-4&quot;&gt;
&lt;app-input placeholder=&quot;Email&quot; type=&quot;email&quot; controlName=&quot;email&quot;
(formControlChange)=&quot;handleFormControl(&#39;email&#39;,$event)&quot; /&gt;
&lt;app-input placeholder=&quot;Password&quot; type=&quot;password&quot; controlName=&quot;password&quot;
(formControlChange)=&quot;handleFormControl(&#39;password&#39;,$event)&quot; /&gt;
&lt;/div&gt;
&lt;/form&gt;
&lt;/Modal&gt;
export class LoginModalComponent implements OnInit {
loginForm!: FormGroup;
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
this.loginForm = this.fb.group({
email: new FormControl(&#39;&#39;),
password: new FormControl(&#39;&#39;)
})
}
handleFormControl(formControlName: string, formControl: FormControl) {
this.loginForm.setControl(formControlName, formControl);
}
handleSubmit(): void {
console.log(this.loginForm);
}
}

Input Component

import { Component, Input, Output, EventEmitter, OnInit } from &#39;@angular/core&#39;;
import { FormControl, Validators } from &#39;@angular/forms&#39;;
@Component({
selector: &#39;app-input&#39;,
template: `
&lt;input
[placeholder]=&quot;placeholder&quot;
[formControl]=&quot;formControl&quot;
class=&quot;
w-full
p-4
text-lg
bg-black
border-2
border-neutral-800
rounded-md
outline-none
text-white
focus:border-sky-500
focus:border-2
transition
disabled:bg-neutral-900
disabled:opacity-70
disabled:cursor-not-allowed
&quot;
[type]=&quot;type&quot;
&gt;
`,
styles: [
]
})
export class InputComponent implements OnInit {
@Input(&#39;placeholder&#39;) placeholder: string = &#39;&#39;;
@Input(&#39;controlName&#39;) controlName: string = &#39;&#39;;
@Input(&#39;type&#39;) type: string = &#39;text&#39;;
@Input() required: boolean = false;
@Input() email: boolean = false;
@Output() formControlChange = new EventEmitter&lt;FormControl&gt;();
formControl!: FormControl;
ngOnInit(): void {
this.initializeFormControl()
this.subscribeToValueChange()
// this.formControlChange.emit(this.formControl);
}
initializeFormControl(): void {
const validators = [];
if (this.required) {
validators.push(Validators.required)
}
if (this.email) {
validators.push(Validators.email);
}
this.formControl = new FormControl(&#39;&#39;, validators);
}
subscribeToValueChange(): void {
this.formControl.valueChanges.subscribe((value) =&gt; {
this.formControlChange.emit(this.formControl);
})
}
}

答案1

得分: 1

你的代码中我看不到 "submit" 部分。但你的代码也能正常工作。这是主要组件:

import 'zone.js/dist/zone';
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { InputComponent } from './input/input.component';

@Component({
  selector: 'my-app',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule, FormsModule, InputComponent],
  template: `
    <h1>Hello from {{name}}!</h1>
    <a target="_blank" href="https://angular.io/start">
      Learn more about Angular 
    </a>

    <form [formGroup]="loginForm" (ngSubmit)="handleSubmit()">
      <div class="flex flex-col gap-4">
        <app-input placeholder="Email" type="email" controlName="email"
          (formControlChange)="handleFormControl('email',$event)" />
        <app-input placeholder="Password" type="password" controlName="password"
          (formControlChange)="handleFormControl('password',$event)" />
      </div>
      <button>SUBMIT</button>
    </form>
  `,
})
export class App implements OnInit {
  name = 'Angular';
  loginForm!: FormGroup;

  constructor(private fb: FormBuilder) { }

  ngOnInit(): void {
    this.loginForm = this.fb.group({
      email: new FormControl(''),
      password: new FormControl('')
    })
  }

  handleFormControl(formControlName: string, formControl: FormControl) {
    this.loginForm.setControl(formControlName, formControl);
  }

  handleSubmit(): void {
    console.log(this.loginForm.value);
  }
}

bootstrapApplication(App);

这是输入组件:

import { CommonModule } from '@angular/common';
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';

@Component({
  selector: 'app-input',
  template: `
    <input
        [placeholder]="placeholder"
        [formControl]="formControl"
        class="
          w-full
          p-4
          text-lg
          bg-black
          border-2
          border-neutral-800
          rounded-md
          outline-none
          text-white
          focus:border-sky-500
          focus:border-2
          transition
          disabled:bg-neutral-900
          disabled:opacity-70
          disabled:cursor-not-allowed
        "
        [type]="type"
    >
  `,
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule],
  styles: [
  ]
})
export class InputComponent implements OnInit {
  @Input('placeholder') placeholder: string = '';
  @Input('controlName') controlName: string = '';
  @Input('type') type: string = 'text';
  @Input() required: boolean = false;
  @Input() email: boolean = false;

  @Output() formControlChange = new EventEmitter<FormControl>();
  formControl!: FormControl;

  ngOnInit(): void {
    this.initializeFormControl()
    this.subscribeToValueChange()
  }

  initializeFormControl(): void {
    const validators = [];
    if (this.required) {
      validators.push(Validators.required)
    }

    if (this.email) {
      validators.push(Validators.email);
    }

    this.formControl = new FormControl('', validators);
  }

  subscribeToValueChange(): void {
    this.formControl.valueChanges.subscribe((value) => {
      this.formControlChange.emit(this.formControl);
    })
  }
}

我只添加了 (ngSubmit) 并更改了 handleSubmit() 来打印 value。在 Stackblitz 上尝试并告诉我是否有任何问题。

英文:

In your code I see no "submit" part. But your code work as well. This is the main component:

import &#39;zone.js/dist/zone&#39;;
import { Component, OnInit } from &#39;@angular/core&#39;;
import { CommonModule } from &#39;@angular/common&#39;;
import { bootstrapApplication } from &#39;@angular/platform-browser&#39;;
import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule } from &#39;@angular/forms&#39;;
import { InputComponent } from &#39;./input/input.component&#39;;
@Component({
selector: &#39;my-app&#39;,
standalone: true,
imports: [CommonModule, ReactiveFormsModule, FormsModule, InputComponent],
template: `
&lt;h1&gt;Hello from {{name}}!&lt;/h1&gt;
&lt;a target=&quot;_blank&quot; href=&quot;https://angular.io/start&quot;&gt;
Learn more about Angular 
&lt;/a&gt;
&lt;form [formGroup]=&quot;loginForm&quot; (ngSubmit)=&quot;handleSubmit()&quot;&gt;
&lt;div class=&quot;flex flex-col gap-4&quot;&gt;
&lt;app-input placeholder=&quot;Email&quot; type=&quot;email&quot; controlName=&quot;email&quot;
(formControlChange)=&quot;handleFormControl(&#39;email&#39;,$event)&quot; /&gt;
&lt;app-input placeholder=&quot;Password&quot; type=&quot;password&quot; controlName=&quot;password&quot;
(formControlChange)=&quot;handleFormControl(&#39;password&#39;,$event)&quot; /&gt;
&lt;/div&gt;
&lt;button&gt;SUBMIT&lt;/button&gt;
&lt;/form&gt;
`,
})
export class App implements OnInit {
name = &#39;Angular&#39;;
loginForm!: FormGroup;
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
this.loginForm = this.fb.group({
email: new FormControl(&#39;&#39;),
password: new FormControl(&#39;&#39;)
})
}
handleFormControl(formControlName: string, formControl: FormControl) {
this.loginForm.setControl(formControlName, formControl);
}
handleSubmit(): void {
console.log(this.loginForm.value);
}
}
bootstrapApplication(App);

And this the input component:

import { CommonModule } from &#39;@angular/common&#39;;
import { Component, Input, Output, EventEmitter, OnInit } from &#39;@angular/core&#39;;
import { FormControl, ReactiveFormsModule, Validators } from &#39;@angular/forms&#39;;
@Component({
selector: &#39;app-input&#39;,
template: `
&lt;input
[placeholder]=&quot;placeholder&quot;
[formControl]=&quot;formControl&quot;
class=&quot;
w-full
p-4
text-lg
bg-black
border-2
border-neutral-800
rounded-md
outline-none
text-white
focus:border-sky-500
focus:border-2
transition
disabled:bg-neutral-900
disabled:opacity-70
disabled:cursor-not-allowed
&quot;
[type]=&quot;type&quot;
&gt;
`,
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
styles: [
]
})
export class InputComponent implements OnInit {
@Input(&#39;placeholder&#39;) placeholder: string = &#39;&#39;;
@Input(&#39;controlName&#39;) controlName: string = &#39;&#39;;
@Input(&#39;type&#39;) type: string = &#39;text&#39;;
@Input() required: boolean = false;
@Input() email: boolean = false;
@Output() formControlChange = new EventEmitter&lt;FormControl&gt;();
formControl!: FormControl;
ngOnInit(): void {
this.initializeFormControl()
this.subscribeToValueChange()
// this.formControlChange.emit(this.formControl);
}
initializeFormControl(): void {
const validators = [];
if (this.required) {
validators.push(Validators.required)
}
if (this.email) {
validators.push(Validators.email);
}
this.formControl = new FormControl(&#39;&#39;, validators);
}
subscribeToValueChange(): void {
this.formControl.valueChanges.subscribe((value) =&gt; {
this.formControlChange.emit(this.formControl);
})
}
}

I have only added the (ngSubmit) and changed the handleSubmit() to print the value. Try it on Stackblitz and tell me if anything goes wrong.

答案2

得分: 1

你可以使用 NG_VALUE_ACCESSOR 实现来创建可重用的自定义表单控件组件。

示例链接:https://stackblitz.com/edit/angular-module-4xcvpm?file=src%2Fapp%2Fmodel.compoennt.ts

input.component.ts

import { Component, Input, forwardRef, OnInit } from '@angular/core';
import {
  ControlValueAccessor,
  NG_VALUE_ACCESSOR,
  Validators,
  FormControl,
  ValidatorFn,
} from '@angular/forms';

@Component({
  selector: 'app-input',
  template: `
    <input
        [placeholder]="placeholder"
        [formControl]="formControl"
        class="
          w-full
          p-4
          text-lg
          bg-black
          border-2
          border-neutral-800
          rounded-md
          outline-none
          text-white
          focus:border-sky-500
          focus:border-2
          transition
          disabled:bg-neutral-900
          disabled:opacity-70
          disabled:cursor-not-allowed
        "
        [type]="type"
    >
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputComponent),
      multi: true,
    },
  ],
  styles: [],
})
export class InputComponent implements OnInit, ControlValueAccessor {
  @Input('placeholder') placeholder: string = '';
  @Input('type') type: string = 'text';
  @Input() required: boolean = false;
  @Input() email: boolean = false;

  formControl!: FormControl;
  onTouched: any;
  onChange: any;

  ngOnInit(): void {
    const validators: ValidatorFn[] = [];

    if (this.required) {
      validators.push(Validators.required);
    }

    if (this.email) {
      validators.push(Validators.email);
    }

    this.formControl = new FormControl('', validators);
  }

  writeValue(value: any): void {
    this.formControl.setValue(value);
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
    this.formControl.valueChanges.subscribe(fn);
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    isDisabled ? this.formControl.disable() : this.formControl.enable();
  }
}

login-modal.component.ts

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

@Component({
  selector: 'app-login-modal',
  template: `
    <Modal [isOpen]="true" title="Login" actionLabel="Sign in" (submit)="handleSubmit()">
      <form [formGroup]="loginForm">
        <div class="flex flex-col gap-4">
          <app-input placeholder="Email" type="email" required="true" email="true" formControlName="email"></app-input>
          <app-input placeholder="Password" type="password" required="true" formControlName="password"></app-input>
        </div>
      </form>
    </Modal>
  `,
  styles: [],
})
export class LoginModalComponent implements OnInit {
  loginForm!: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.loginForm = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', Validators.required],
    });

    this.loginForm.get('email')!.valueChanges.subscribe((value) => {
      console.log('email value changed:', value);
    });

    this.loginForm.get('password')!.valueChanges.subscribe((value) => {
      console.log('password value changed:', value);
    });
  }

  handleSubmit(): void {
    console.log('hello');
    console.log(this.loginForm.value);
  }
}

modal.component.ts

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'Modal',
  template: `
    <div *ngIf="isOpen" class="modal">
      <h2>{{ title }}</h2>
      <ng-content></ng-content>
      <button (click)="submit.emit()">{{ actionLabel }}</button>
    </div>
  `,
})
export class ModalComponent {
  @Input() isOpen: boolean = false;
  @Input() title: string = '';
  @Input() actionLabel: string = '';
  @Output() submit = new EventEmitter<void>();
}
英文:

You can use NG_VALUE_ACCESSOR implementation for reusable custom form control components

demo : https://stackblitz.com/edit/angular-module-4xcvpm?file=src%2Fapp%2Fmodel.compoennt.ts

input.component.ts

import { Component, Input, forwardRef, OnInit } from &#39;@angular/core&#39;;
import {
ControlValueAccessor,
NG_VALUE_ACCESSOR,
Validators,
FormControl,
ValidatorFn,
} from &#39;@angular/forms&#39;;
@Component({
selector: &#39;app-input&#39;,
template: `
&lt;input
[placeholder]=&quot;placeholder&quot;
[formControl]=&quot;formControl&quot;
class=&quot;
w-full
p-4
text-lg
bg-black
border-2
border-neutral-800
rounded-md
outline-none
text-white
focus:border-sky-500
focus:border-2
transition
disabled:bg-neutral-900
disabled:opacity-70
disabled:cursor-not-allowed
&quot;
[type]=&quot;type&quot;
&gt;`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() =&gt; InputComponent),
multi: true,
},
],
styles: [],
})
export class InputComponent implements OnInit, ControlValueAccessor {
@Input(&#39;placeholder&#39;) placeholder: string = &#39;&#39;;
@Input(&#39;type&#39;) type: string = &#39;text&#39;;
@Input() required: boolean = false;
@Input() email: boolean = false;
formControl!: FormControl;
onTouched: any;
onChange: any;
ngOnInit(): void {
const validators: ValidatorFn[] = [];
if (this.required) {
validators.push(Validators.required);
}
if (this.email) {
validators.push(Validators.email);
}
this.formControl = new FormControl(&#39;&#39;, validators);
}
writeValue(value: any): void {
this.formControl.setValue(value);
}
registerOnChange(fn: any): void {
this.onChange = fn;
this.formControl.valueChanges.subscribe(fn);
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
isDisabled ? this.formControl.disable() : this.formControl.enable();
}
}

login-modal.component.ts

import { Component, OnInit } from &#39;@angular/core&#39;;
import { FormBuilder, FormGroup, Validators } from &#39;@angular/forms&#39;;
@Component({
selector: &#39;app-login-modal&#39;,
template: `
&lt;Modal [isOpen]=&quot;true&quot; title=&quot;Login&quot; actionLabel=&quot;Sign in&quot; (submit)=&quot;handleSubmit()&quot;&gt;
&lt;form [formGroup]=&quot;loginForm&quot;&gt;
&lt;div class=&quot;flex flex-col gap-4&quot;&gt;
&lt;app-input placeholder=&quot;Email&quot; type=&quot;email&quot; required=&quot;true&quot; email=&quot;true&quot; formControlName=&quot;email&quot;&gt;&lt;/app-input&gt;
&lt;app-input placeholder=&quot;Password&quot; type=&quot;password&quot; required=&quot;true&quot; formControlName=&quot;password&quot;&gt;&lt;/app-input&gt;
&lt;/div&gt;
&lt;/form&gt;
&lt;/Modal&gt;
`,
styles: [],
})
export class LoginModalComponent implements OnInit {
loginForm!: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit(): void {
this.loginForm = this.fb.group({
email: [&#39;&#39;, [Validators.required, Validators.email]],
password: [&#39;&#39;, Validators.required],
});
this.loginForm.get(&#39;email&#39;)!.valueChanges.subscribe((value) =&gt; {
console.log(&#39;email value changed:&#39;, value);
});
this.loginForm.get(&#39;password&#39;)!.valueChanges.subscribe((value) =&gt; {
console.log(&#39;password value changed:&#39;, value);
});
}
handleSubmit(): void {
console.log(&#39;heloo&#39;);
console.log(this.loginForm.value);
}
}

modal.component.ts

import { Component, Input, Output, EventEmitter } from &#39;@angular/core&#39;;
@Component({
selector: &#39;Modal&#39;,
template: `
&lt;div *ngIf=&quot;isOpen&quot; class=&quot;modal&quot;&gt;
&lt;h2&gt;{{ title }}&lt;/h2&gt;
&lt;ng-content&gt;&lt;/ng-content&gt;
&lt;button (click)=&quot;submit.emit()&quot;&gt;{{ actionLabel }}&lt;/button&gt;
&lt;/div&gt;
`,
})
export class ModalComponent {
@Input() isOpen: boolean = false;
@Input() title: string = &#39;&#39;;
@Input() actionLabel: string = &#39;&#39;;
@Output() submit = new EventEmitter&lt;void&gt;();
}

huangapple
  • 本文由 发表于 2023年6月11日 20:56:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76450563.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定