我创建了以下组件,以避免重复在表单字段中添加前缀/后缀/图标的样板html:
Measurements.aggregate([
{"$match" : {"createdAt":{"$gt":new Date(Date.now() - 24*60*60 * 1000)}}},
{"$sort":{"createdAt":-1}},
{"$group":{
"_id":{"$hour":"$createdAt"},
"first":{"$first":"$$ROOT"},
"last":{"$last":"$$ROOT"}
}}
])
目标是像这样的HTML:
@Component({
selector: "fancy-input",
template: `
<span matPrefix>$ </span>
<span matSuffix>.00</span>
<mat-icon matSuffix>'visibility'</mat-icon>
`,
})
export class FancyInputComponent {}
但是,似乎<mat-form-field style="width: 100%">
<input matInput placeholder="How much?" formControlName="amount">
<fancy-input></fancy-input>
</mat-form-field>
元素仍然存在于DOM中,这导致前缀,后缀和图标无法正确呈现:
有没有办法从DOM中替换/删除<fancy-input>
元素?
我尝试将选择器更改为属性(即<fancy-input>
)。如果我然后将其添加到"[fancy-input]"
元素,则输出将呈现为该元素的子元素,并将被忽略。我无法将属性添加到input
,因为它已经是一个组件。
有没有更好的方法来实现这一目标?
答案 0 :(得分:1)
一种可能性是将整个mat-form-field
包含在花哨的自定义组件中,如this stackblitz所示。通过调整this answer中给出的模板和代码,添加width
样式属性并处理input
事件,获得了自定义组件的以下代码。请注意,该组件可以容纳额外的内容。
import { Component, Input, ViewChild, ElementRef, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormControl } from '@angular/forms';
export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FancyInputComponent),
multi: true
};
@Component({
selector: "fancy-input",
template: `
<mat-form-field [style.width]="width">
<input #input matInput type="text" placeholder="{{placeholder}}" [value]="value" (input)="onInput($event, input.value)">
<span matPrefix>$ </span>
<span matSuffix>.00</span>
<mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility' : 'visibility_off'}}</mat-icon>
<ng-content></ng-content>
</mat-form-field>`,
providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR],
})
export class FancyInputComponent {
// for mat-icon visibility
hide = true;
// width style attribute of the input control
@Input() width: string;
// placeholder input
@Input() placeholder: string;
//current form control input. helpful in validating and accessing form control
@Input() c: FormControl = new FormControl();
// set true if we need not show the asterisk in red color
@Input() optional: boolean = false;
//@Input() v:boolean = true; // validation input. if false we will not show error message.
// errors for the form control will be stored in this array
errors: Array<any> = ['This field is required'];
// get reference to the input element
@ViewChild('input') inputRef: ElementRef;
//Lifecycle hook. angular.io for more info
ngAfterViewInit() {
// RESET the custom input form control UI when the form control is RESET
this.c.valueChanges.subscribe(
() => {
// check condition if the form control is RESET
if (this.c.value == "" || this.c.value == null || this.c.value == undefined) {
this.innerValue = "";
this.inputRef.nativeElement.value = "";
}
}
);
}
//The internal data model for form control value access
private innerValue: any = '';
// event fired when input value is changed . later propagated up to the form control using the custom value accessor interface
onInput(e: Event, value: any) {
//set changed value
this.innerValue = value;
// propagate value into form control using control value accessor interface
this.propagateChange(this.innerValue);
//reset errors
this.errors = [];
//setting, resetting error messages into an array (to loop) and adding the validation messages to show below the field area
for (var key in this.c.errors) {
if (this.c.errors.hasOwnProperty(key)) {
if (key === "required") {
this.errors.push("This field is required");
} else {
this.errors.push(this.c.errors[key]);
}
}
}
}
//get accessor
get value(): any {
return this.innerValue;
};
//set accessor including call the onchange callback
set value(v: any) {
if (v !== this.innerValue) {
this.innerValue = v;
}
}
//propagate changes into the custom form control
propagateChange = (_: any) => { }
//From ControlValueAccessor interface
writeValue(value: any) {
console.log(value);
this.innerValue = value;
}
//From ControlValueAccessor interface
registerOnChange(fn: any) {
this.propagateChange = fn;
}
//From ControlValueAccessor interface
registerOnTouched(fn: any) {
}
}
父组件会将其包含在其模板中:
<div [formGroup]="form" class="example-container">
<fancy-input formControlName="amount1" [width]="'100%'" [placeholder]="'My amount'" [c]="form.controls.amount1" [(ngModel)]="amount1Value"></fancy-input>
<fancy-input formControlName="amount2" [width]="'100%'" [placeholder]="'Your amount'" [c]="form.controls.amount2" [(ngModel)]="amount2Value">
<span>Other stuff</span>
</fancy-input>
</div>
使用相应的代码:
export class ParentComponent {
amount1Value = 72.18;
amount2Value = "";
form = new FormGroup({
amount1: new FormControl(),
amount2: new FormControl(),
});
}