您可以使用 ContentChild 或 ContentChildren。当你想寻找 NgModel 时,你的指令就变成了
@ContentChild(NgControl, { static: true }) input
someFunc() {
this.input.control.setValue(null);
}
(NgControl) 是任何带有 [ngModel] 的输入或带有 [formControl] 的输入,或者如果我们正在使用 ReactiveForm,则为 formControlName
我们可以有一个 app.component 像
<div hello >
<input [ngModel]="value"/>
</div>
<button (click)="do()">clear</button>
export class AppComponent {
@ViewChild(HelloDirective,{static:true}) wrapper
do()
{
this.wrapper.someFunc()
}
}
见stackblitz
注意:真的要清理输入,不需要创建指令,但我认为这是更复杂的东西的一部分
更新当我们想在 Angular Material 中添加一个“清除”按钮时,docs 的第 4 个示例提供了一个机会
<mat-form-field class="example-form-field">
<input matInput type="text" placeholder="Clearable input" [(ngModel)]="value">
<button mat-button *ngIf="value" matSuffix mat-icon-button aria-label="Clear" (click)="value=''">
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
要了解如何使用 select 实现功能,请参阅 stackoverflow question and answer
但问题是,我们可以做一个指令来实现一些类似的吗? (答案是真的)。但是我们的指令必须使用 Renderer2 并且 - 我还没有找到其他方法 - 通过 .css 创建一个交叉。
.css 就像
.close {
position: absolute;
right: -1rem;
top: -2.5rem;
width: .8rem;
height: .8rem;
opacity: 0.7;
}
.close:hover {
opacity: 1;
}
.close:before, .close:after {
position: absolute;
left: .4rem;
content: ' ';
height: .7rem;
width: 1px;
background-color: #333;
}
.close:before {
transform: rotate(45deg);
}
.close:after {
transform: rotate(-45deg);
}
指令变为(简要说明见注释)
@Directive({
selector: '[clear]',
host: { //we need an "extra" space to the rigth
'[style.margin-right]': '"1.5rem"',
}
})
export class ClearDirective implements OnInit {
div: any;
@ContentChild(NgControl) control; //we get the "input"
@HostListener('click', ['$event']) click($event) {
//we add a HostListener "click" and take account is we click
//in the "cross" we created, is a span with class="close"
if ($event.target.getAttribute('class') == 'close') {
this.control.control.setValue(null); //remember, the control is the NgControl
$event.stopPropagation();
}
}
constructor(private renderer: Renderer2, private el: ElementRef) { }
ngOnInit() {
//we need take account about when the value of the control is "something"
//or none
if (this.control)
this.control.valueChanges.subscribe((value) => {
if (!value) { //if no value
this.clearCross() //remove the cross
}
else {
if (!this.div) { //if not yet the cross
this.createCross() //create
}
}
})
}
createCross() {
//we wan create some like
/*
<div class="mat-form-field-suffix">
<span class="close"></span>
</div>
*/
this.div = this.renderer.createElement('div');
this.renderer.addClass(this.div, "mat-form-field-suffix")
const span = this.renderer.createElement('span')
this.renderer.addClass(span, "close")
this.renderer.appendChild(this.div, span);
this.renderer.appendChild(this.el.nativeElement, this.div);
}
clearCross() {
if (this.div) //simply remove the "cross"
{
this.renderer.removeChild(this.el.nativeElement, this.div)
this.div=null
}
}
你可以使用like,例如
<mat-form-field clear>
<input matInput type="text" placeholder="Clearable input" [(ngModel)]="value">
</mat-form-field>
嗯,完整的示例(基于@SammerKant 的分叉堆栈闪电战)是here
Update 2,好吧,我不喜欢的一些是指令超出了输入。我真的不喜欢它。所以,同理,稍微改一下css来调整位置,新指令在this stackblitz
export class ClearDirective implements OnInit {
div: any;
constructor(@Optional() private control:NgControl,private renderer: Renderer2, private el: ElementRef) { }
ngOnInit() {
if (this.control)
this.control.valueChanges.subscribe((value) => {
if (!value) {
this.clearCross()
}
else {
if (!this.div) {
this.createCross()
}
}
})
}
createCross() {
this.div = this.renderer.createElement('div');
this.renderer.addClass(this.div, "wrapper")
const span = this.renderer.createElement('span')
this.renderer.addClass(span, "close")
this.renderer.appendChild(this.div, span);
this.renderer.insertBefore(this.renderer.parentNode(this.el.nativeElement),this.div,this.el.nativeElement,);
this.renderer.listen(this.div, 'click', ($event) => {
this.control.control.setValue(null);
$event.stopPropagation();
});
}
clearCross() {
if (this.div)
{
this.renderer.removeChild(this.renderer.parentNode(this.el.nativeElement), this.div)
this.div=null
}
}
}