1、应用场景
我们开发了一个虚拟键盘的组件,然后将虚拟键盘的组件全局挂载到根目录上去,通过某个属性去控制虚拟键盘的显示与否即可,但是,有一个难点,就是在使用到的组件中如果通过虚拟键盘的Enter
键将虚拟键盘的值传到我们想要的组件中去呢?service服务几乎不考虑,因为你可以将值存到服务中去,但是你在使用到的那个组件中如何去知道用户点击了enter键呢?
2、手动封装一个类似Vue中的Bus事件分发
定义一个interface
,中间有两个属性,也就是事件名称以及传递的数据值。
import { Injectable } from '@angular/core';
import { Subject, Subscription } from 'rxjs';
import { filter, map } from 'rxjs/operators';
interface IScreenEvent {
eventType: string;
eventData: any;
}
@Injectable()
export class EmitBusUtils {
private _subject = new Subject<IScreenEvent>();
public emit(eventType: string, eventData: any) {
this._subject.next({
eventType,
eventData,
});
}
subscribe(eventType: string, listener: (value: any) => void): Subscription {
return this._subject.asObservable()
.pipe(
filter((value, index) => value.eventType === eventType),
map((value, index) => value.eventData),
).subscribe(listener);
}
}
3、部分虚拟键盘组件的代码
虚拟键盘可以通过原生js的dom操作等实现或者可以去参考开源库等不同的插件的代码实现,在封装的虚拟键盘的组件中,我们想要实现值的传递,就需要在用户按下enter键的时候,去emit数据出去,并且关闭虚拟键盘,将虚拟键盘上的值填入我们想要的对应的地方去。
import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { EmitBusUtils } from '@core/utils/emit-bus.utils';
import { AppService } from 'app/app.service';
declare var aKeyboard: any;
@Component({
selector: 'virtual-keyboard',
template: `
<div class="box" >
<ion-icon
class="delete-icon delete"
(click)="onClose()"
src="assets/icon/close-outline.svg"
></ion-icon>
<ion-input
(touchstart)="onTouchStart($event)"
(touchmove)="onTouchMove($event)"
(touchend)="onTouchEnd($event)"
id="inputKeyboard"
placeholder=" 按住此区域可进行拖拽"
readonly
></ion-input>
<div id="main"></div>
</div>
`,
styles: [
`
.box {
position: fixed;
width: 300px;
height: 240px;
box-shadow: 2px 2px 4px rgb(0 0 0 / 10%);
top: 100px;
left: 300px;
}
#input {
margin: auto;
background: #fff;
border-top-left-radius: 0.3rem;
border-top-right-radius: 0.3rem;
border-left: 1px solid #f7ecec;
border-top: 1px solid #f7ecec;
border-right: 1px solid #f7ecec;
}
ion-icon.drag-icon {
font-size: 2rem;
position: absolute;
left: -18px;
top: 4px;
}
ion-icon.delete-icon {
font-size: 1.5rem;
position: absolute;
right: -5px;
top: -19px;
color: #ccc;
}
`,
],
})
export class KeyboardComponent implements AfterViewInit {
_isShow; // 是否显示
@Input()
set isShow(value: any) {
this._isShow = value;
}
get isShow() {
return this._isShow;
}
@Output() change: EventEmitter<boolean> = new EventEmitter<boolean>();
@Output() sendData: EventEmitter<any> = new EventEmitter<any>();
constructor(private _app: AppService, private _bus: EmitBusUtils) {
}
keyboard;
inputKeyboard;
container;
isDown = false; // 是否按下拖拽图标
position = {
start_x: 0,
start_y: 0,
move_x: 0,
move_y: 0,
box_x: 0,
box_y: 0,
};
ngAfterViewInit() {
if (this._isShow) {
this.inputKeyboard = document.querySelector('#inputKeyboard');
this.container = document.querySelector('.box');
this.keyboard = new aKeyboard.numberKeyboard({
el: '#main',
style: {
position: 'absolute',
top: this.position.box_x + 35 + 'px',
left: this.position.box_y,
right: '0',
bottom: '0',
},
});
this.keyboard.inputOn('#inputKeyboard', 'value');
this.keyboard.onclick('Enter', (e) => {
this.change.emit(false);
this._bus.emit('getInputValue', {
value: document.querySelector('#inputKeyboard')['value']
});
});
}
}
// 关闭事件
onClose() {
this._isShow = false;
this.change.emit(this._isShow);
}
// 触摸开始事件
onTouchStart(e) {
this.position['start_x'] = e.changedTouches[0].clientX;
this.position['start_y'] = e.changedTouches[0].clientY;
this.position['box_x'] = this.container.offsetLeft;
this.position['box_y'] = this.container.offsetTop;
this.isDown = true;
}
// 触摸移动事件
onTouchMove(e) {
if (!this.isDown) {
return;
}
this.position['move_x'] =
e.changedTouches[0].clientX - this.position['start_x'];
this.position['move_y'] =
e.changedTouches[0].clientY - this.position['start_y'];
this.container.style.left =
this.position['box_x'] + this.position['move_x'] + 'px';
this.container.style.top =
this.position['box_y'] + this.position['move_y'] + 'px';
}
// 手指离开屏幕
onTouchEnd(e) {
this.isDown = false;
}
}
4、挂载到根目录下
挂载到根目录下通过app服务中的isShow为true还是false去控制。
<virtual-keyboard [isShow]="app.isShow" *ngIf="app.isShow" (change)="handleChange($event)" ></virtual-keyboard>
5、在需要使用虚拟键盘的地方去subscribe订阅事件
每次点击文本框的时候,进行取反操作,this.appService.isShow = !this.appService.isShow;
也就是虚拟键盘的弹出与关闭,this._bus.subscribe('订阅的事件名(一定要与emit那边相同的事件名相同)
', (res) => { // 这里的res也就是那边emit传过来的值 })
async handleOpen(query) {
// tslint:disable-next-line: curly
if (query && query.mark) return;
this.appService.isShow = !this.appService.isShow;
await this._bus.subscribe('getInputValue', (res) => {
if (res) {
console.log(res);
query.count = Number(res.value);
}
});
}