Angular 7 | HostListener 模糊条件停止所有其他事件回调
Angular 7 | HostListener blur conditional stop all other event callbacks
我有一个要求,我们希望用户解决输入字段错误,并且在完成之前不允许用户在屏幕上执行任何其他操作。
同样,我在输入字段和 onBlur 上实现了 HostListener,如果验证场景失败,我会将焦点设置回输入字段。
而且,我正在执行 e.preventDefault() 和 e.stopPropagtion() 以停止在页面上执行所有其他事件回调(在设置 focus
后,blur
将是第一个被执行的事件。
但是在任何外部事件上,blur
确实会执行,但它并不限制其他事件的执行。那些也正在执行,没有它们我将无法实现所需的功能。
import { Directive, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appNumberFormat]'
})
export class NumberFormatDirective {
constructor() { }
@HostListener('blur', ['$event']) blur(evt) {
evt.preventDefault();
console.log('field focus...');
evt.target.focus();
return false;
// if (evt.target.value.trim() === '') {
// this.formControl.control.setValue(this.defaultValue);
// }
}
}
我在 stackBlitz 中复制了相同的场景。请看一下。
您可以将额外的输入 属性 添加到 enable/disable 焦点功能。如果你有一个 Reactive 表单,你可以将它挂接到表单是 VALID 状态。
export class NumberFormatDirective {
@Input() enableTargetFocus: boolean;
@HostListener('blur', ['$event']) blur(evt) {
if (this.enableTargetFocus === false) {
return;
}
...
}
}
HTML:
<input type="text" appNumberFormat [enableTargetFocus]="form.invalid" />
解决此问题的一种方法是通过组件上的变量(指示表单是否有效)和阻止所有点击的简单 css 元素。
不过你必须要小心,确保你专注于正确的元素,而且这种情况不会永远持续下去,因为我不得不说从用户体验的角度来看这似乎相当烦人。
在您的 html 模板上:
<div *ngIf="formIsInvalid" class="click-blocker"></div>
在你的 css 上:
.click-blocker {
position: absolute;
background-color: rgba(1, 1, 1, 0.1); // adjust the opacity as you wish
bottom: 0;
left: 0;
right: 0;
top: 0;
height: 100vh;
width: 100vw;
z-index: 1;
}
我在你的 stackblitz 中实现了它:https://stackblitz.com/edit/angular-za1lgx
我不太确定你想要实现什么,但一个技巧可能是根据你在 onBlur() 中被调用的不同情况限制你的 focus() 执行,因为你每次都会被调用由于您绑定了事件,因此要求模糊。
为了防止执行,您可以仅在需要时触发它,但这并不是很理想:假设您的 e.target
或 e.relatedTarget
不是您想要的或类似的东西或当 document.activeElement
已经是输入井时,您不需要将其重新聚焦在 blur() 上,但又一次......不理想
希望对您有所帮助!
我认为这不是实现所需功能的理想方式。我认为其他 buttons/anchor-links 应该在表单中有错误时保持禁用状态。您可以将按钮和锚链接的禁用 属性 绑定到一个函数,该函数将在检查验证后 return true/false。
然而,浏览器按特定顺序执行事件,如果您需要停止点击事件的进行,您应该 return false。我已经使用一个布尔变量实现了相同的功能,该变量将根据验证检查设置为 true/false,我在这里创建了您的代码的分支 - https://stackblitz.com/edit/angular-uay8rm
preventOtherActions: boolean = true;
txtBoxValue: string = "";
submitForm($event) {
if(this.preventOtherActions) {
return false;
}
console.log('submitForm');
}
onBlur(e) {
console.log('field focus...');
setTimeout(() => {
e.target.focus();
}, 10);
this.preventOtherActions = this.txtBoxValue.length < 6;
return;
}
Blur 不会阻止另一个事件,因为它是 运行 独立的。将指针事件设置为 none 将阻止点击。
onBlur(e) {
e.preventDefault();
e.stopPropagation();
document.body.style.pointerEvents = 'none';
console.log('field focus...');
setTimeout(() => {
e.target.focus();
}, 10);
setTimeout(() => {
document.body.style.pointerEvents = 'initial';
}, 300);
return;
}
您还可以在输入聚焦时添加叠加层,而不是使用 pointer-events: none
。在这两种情况下,您都需要处理 Tab 键。
为了防止tab + enter在input元素上添加:
(keydown)="onKeyDown($event)"
在组件 ts 中:
onKeyDown(event) {
if (event.code === 'Tab') {
event.preventDefault();
event.stopPropagation();
}
}
您可以做的一种方法是使用此解决方案禁用页面上的所有事件(并自定义不允许输入键的按键)。
Stop propagation for all events
disableAllUserEvents = () => {
const events = ["click", "contextmenu", "dblclick", "mousedown", "mouseenter", "mouseleave", "mousemove",
"mouseover", "mouseout", "mouseup", "blur", "change", "focus", "focusin",
"focusout", "input", "invalid", "reset", "search", "select", "submit", "drag", "dragend", "dragenter",
"dragleave", "dragover", "dragstart", "drop", "copy", "cut", "paste", "mousewheel", "wheel", "touchcancel",
"touchend", "touchmove", "touchstart"];
const handler = event => {
event.stopPropagation();
event.preventDefault();
return false;
};
for (let i = 0, l = events.length; i < l; i++) {
document.addEventListener(events[i], handler, true);
}
return () => {
for (let i = 0, l = events.length; i < l; i++) {
document.removeEventListener(events[i], handler, true);
}
};
};
并在一切正常时重新启用该事件。这有点蛮力,但确实有效。
我有一个要求,我们希望用户解决输入字段错误,并且在完成之前不允许用户在屏幕上执行任何其他操作。
同样,我在输入字段和 onBlur 上实现了 HostListener,如果验证场景失败,我会将焦点设置回输入字段。
而且,我正在执行 e.preventDefault() 和 e.stopPropagtion() 以停止在页面上执行所有其他事件回调(在设置 focus
后,blur
将是第一个被执行的事件。
但是在任何外部事件上,blur
确实会执行,但它并不限制其他事件的执行。那些也正在执行,没有它们我将无法实现所需的功能。
import { Directive, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appNumberFormat]'
})
export class NumberFormatDirective {
constructor() { }
@HostListener('blur', ['$event']) blur(evt) {
evt.preventDefault();
console.log('field focus...');
evt.target.focus();
return false;
// if (evt.target.value.trim() === '') {
// this.formControl.control.setValue(this.defaultValue);
// }
}
}
我在 stackBlitz 中复制了相同的场景。请看一下。
您可以将额外的输入 属性 添加到 enable/disable 焦点功能。如果你有一个 Reactive 表单,你可以将它挂接到表单是 VALID 状态。
export class NumberFormatDirective {
@Input() enableTargetFocus: boolean;
@HostListener('blur', ['$event']) blur(evt) {
if (this.enableTargetFocus === false) {
return;
}
...
}
}
HTML:
<input type="text" appNumberFormat [enableTargetFocus]="form.invalid" />
解决此问题的一种方法是通过组件上的变量(指示表单是否有效)和阻止所有点击的简单 css 元素。
不过你必须要小心,确保你专注于正确的元素,而且这种情况不会永远持续下去,因为我不得不说从用户体验的角度来看这似乎相当烦人。
在您的 html 模板上:
<div *ngIf="formIsInvalid" class="click-blocker"></div>
在你的 css 上:
.click-blocker {
position: absolute;
background-color: rgba(1, 1, 1, 0.1); // adjust the opacity as you wish
bottom: 0;
left: 0;
right: 0;
top: 0;
height: 100vh;
width: 100vw;
z-index: 1;
}
我在你的 stackblitz 中实现了它:https://stackblitz.com/edit/angular-za1lgx
我不太确定你想要实现什么,但一个技巧可能是根据你在 onBlur() 中被调用的不同情况限制你的 focus() 执行,因为你每次都会被调用由于您绑定了事件,因此要求模糊。
为了防止执行,您可以仅在需要时触发它,但这并不是很理想:假设您的 e.target
或 e.relatedTarget
不是您想要的或类似的东西或当 document.activeElement
已经是输入井时,您不需要将其重新聚焦在 blur() 上,但又一次......不理想
希望对您有所帮助!
我认为这不是实现所需功能的理想方式。我认为其他 buttons/anchor-links 应该在表单中有错误时保持禁用状态。您可以将按钮和锚链接的禁用 属性 绑定到一个函数,该函数将在检查验证后 return true/false。
然而,浏览器按特定顺序执行事件,如果您需要停止点击事件的进行,您应该 return false。我已经使用一个布尔变量实现了相同的功能,该变量将根据验证检查设置为 true/false,我在这里创建了您的代码的分支 - https://stackblitz.com/edit/angular-uay8rm
preventOtherActions: boolean = true;
txtBoxValue: string = "";
submitForm($event) {
if(this.preventOtherActions) {
return false;
}
console.log('submitForm');
}
onBlur(e) {
console.log('field focus...');
setTimeout(() => {
e.target.focus();
}, 10);
this.preventOtherActions = this.txtBoxValue.length < 6;
return;
}
Blur 不会阻止另一个事件,因为它是 运行 独立的。将指针事件设置为 none 将阻止点击。
onBlur(e) {
e.preventDefault();
e.stopPropagation();
document.body.style.pointerEvents = 'none';
console.log('field focus...');
setTimeout(() => {
e.target.focus();
}, 10);
setTimeout(() => {
document.body.style.pointerEvents = 'initial';
}, 300);
return;
}
您还可以在输入聚焦时添加叠加层,而不是使用 pointer-events: none
。在这两种情况下,您都需要处理 Tab 键。
为了防止tab + enter在input元素上添加:
(keydown)="onKeyDown($event)"
在组件 ts 中:
onKeyDown(event) {
if (event.code === 'Tab') {
event.preventDefault();
event.stopPropagation();
}
}
您可以做的一种方法是使用此解决方案禁用页面上的所有事件(并自定义不允许输入键的按键)。
Stop propagation for all events
disableAllUserEvents = () => {
const events = ["click", "contextmenu", "dblclick", "mousedown", "mouseenter", "mouseleave", "mousemove",
"mouseover", "mouseout", "mouseup", "blur", "change", "focus", "focusin",
"focusout", "input", "invalid", "reset", "search", "select", "submit", "drag", "dragend", "dragenter",
"dragleave", "dragover", "dragstart", "drop", "copy", "cut", "paste", "mousewheel", "wheel", "touchcancel",
"touchend", "touchmove", "touchstart"];
const handler = event => {
event.stopPropagation();
event.preventDefault();
return false;
};
for (let i = 0, l = events.length; i < l; i++) {
document.addEventListener(events[i], handler, true);
}
return () => {
for (let i = 0, l = events.length; i < l; i++) {
document.removeEventListener(events[i], handler, true);
}
};
};
并在一切正常时重新启用该事件。这有点蛮力,但确实有效。