实现的mockup如下:
初次尝试:假设我们已经创建好了一个自定义Component,其selector为fa-input, 首先看看如何使用:
@Component({
selector: 'app-root',
template: `
<h1>FA Input</h1>
<fa-input icon="envelope" (value)="onNewValue($event)"></fa-input>
<fa-input icon="lock" (value)="onNewValue($event)"></fa-input>
`
})
export class AppComponent {
onNewValue(val) {
console.log(val);
}
}
自定义控件fa-input的实现代码:
@Component({
selector: 'fa-input',
template: `
<i class="fa" [ngClass]="classes"></i>
<input (focus)="inputFocus = true" (blur)="inputFocus = false"
(keyup)="value.emit(input.value)" #input>
`,
styleUrls: ['./fa-input.component.css']
})
export class FaInputComponent {
@Input() icon: string;
@Output() value = new EventEmitter<string>();
inputFocus = false;
get classes() {
const cssClasses = {
fa: true
};
cssClasses['fa-' + this.icon] = true;
return cssClasses;
}
@HostBinding('class.focus')
get focus() {
console.log(this.inputFocus);
return this.inputFocus;
}
}
css:
:host {
border: 1px solid grey;
}
input {
border: none;
outline: none;
}
:host(.focus) {
border: 1px solid blue;
}
(1) icon属性决定待显示的图标外观。该自定义控件的使用者需要传入。
(2) the component has a custom output event named value, that emits new values whenever the value of the text input changes. 一个自定义的名为value的事件发生器,当text input发生变化时,将新的值通知控件使用者。
(3) 检测原生html input字段的focus和blur事件,设置对应的标志位inputFocus. 再将inputFocus通过@HostBinding绑定到host元素的focus CSS样式上。
目前这个设计有一些问题:
(1) 没有考虑到HTML原生的input字段的某些属性,比如:
<input type="email" autocomplete="off" placeholder='Email'>
为了解决这个问题,需要把这些属性逐一手动迁移到自开发Component中去,但这样一来,自开发组件的template会膨胀不少:
@Component({
selector: 'fa-input',
template: `
<i class="fa" [ngClass]="classes"></i>
<input (focus)="inputFocus = true" (blur)="inputFocus = false"
(keyup)="value.emit(input.value)" #input
[placeholder]="placeholder"
[type]="type",
[autocomplete]="autocomplete">
`
})
export class FaInputComponent {
...
@Input() placeholder:string;
@Input() type:string;
@Input() autocomplete:string;
...
}
这种设计的问题在于,消费者使用自定义控件时,完全不知道自定义控件背后包含一个元素的input字段,这样,消费者即使想使用原生input字段的某些属性,也缺乏一种优雅的机制传入到自定义Component中。
先看改进版本的自定义Component如何消费:
@Component({
selector: 'app-root',
template: `
<h1>FA Input</h1>
<fa-input icon="envelope">
<input type="email" placeholder="Email">
</fa-input>
`})
export class AppComponent {
}
一个明显的区别,就是原生input控件的出现。
新版使用ng-content的实现:
@Component({
selector: 'fa-input',
template: `
<i class="fa" [ngClass]="classes"></i>
<ng-content></ng-content>
`,
styleUrls: ['./fa-input.component.css']
})
export class FaInputComponent {
@Input() icon: string;
get classes() {
const cssClasses = {
fa: true
};
cssClasses['fa-' + this.icon] = true;
return cssClasses;
}
}
更多Jerry的原创文章,尽在:“汪子熙”: