有没有办法防止 DOM 在 Angular 2 RC5 中删除?
Is there a way to prevent DOM removal in Angular 2 RC5?
我在使用 Angular 2 和 触摸设备 时遇到问题。特别是,当组件通过 NgFor 渲染并在屏幕上(触摸)拖动时。如果在触摸拖动期间发生 NgFor 的重新渲染(由于外部事件更新绑定到 NgFor 的数据,这在我的应用程序中很常见),就会出现问题。 touchmove
事件停止触发,并且需要您抬起手指然后再次放下,这是一种糟糕的移动体验。如果您使用鼠标,则不会出现此问题。
本质上,在我的应用程序中,我监听组件上的 touchstart
事件,通过条件 *ngIf="isDragging"
(不在 NgFor 中)显示另一个 'DragComponent',它是根据 touchmove
事件位置数据在屏幕上移动。
我知道为什么会这样。这是由于 Touch Spec 的浏览器实现。我通常通过将 DOM 元素保留在内存中直到 touchend
或 touchcancel
事件触发来在 vanilla js 中围绕这个问题进行编码。但是,Angular 现在控制着 DOM!他们正在删除仍在使用中的元素!
查看这个 plunker http://plnkr.co/edit/QR6WDzv6NxOmn6LXTngG?p=preview 以更深入地了解我要描述的内容。 (注意 需要触摸屏,或在 Chrome DevTools 中使用触摸模拟)
我还在 Angular 存储库中创建了一个问题 #9864,但没有得到任何回应。我知道他们正忙于为决赛做准备,但我认为这应该在决赛前解决,因为很多用户将在触摸设备上使用 Angular。
我将不胜感激 tips/workarounds/hacks。随时用解决方案更新 plunker。
找到解决方法:
TouchEvents 在 DOM 移除后确实继续触发 但是 它们仅针对 node/element 原始 touchstart
发生的并且不要冒泡(与 MouseEvents 不同,后者令人困惑!)。
因此,我们不能执行一个简单的 @HostListener('touchmove', ['$event'])
并期望它与 DOM 删除一起工作(因为事件侦听器附加到外部组件元素)。我们必须在 touchstart 事件发生时 动态地 添加事件监听器到 目标元素 。然后对 touchend
或 touchcancel
(或 ngOnDestroy()
)执行清理。
索恩:
@HostListener('touchstart', ['$event'])
@HostListener('mousedown', ['$event'])
dragStart(event) {
if (event.touches) { // avoid touch event loss issue
this.removePreviousTouchListeners(); // avoid mem leaks
this.touchmoveListenFunc = this.renderer.listen(event.target, 'touchmove', (e) => { this.onDragMove(e); });
this.touchendListenFunc = this.renderer.listen(event.target, 'touchend', (e) => { this.removePreviousTouchListeners(); this.onDragEnd(e); });
this.touchcancelListenFunc = this.renderer.listen(event.target, 'touchcancel', (e) => { this.removePreviousTouchListeners(); this.onDragEnd(e); });
}
...
}
removePreviousTouchListeners() {
if (this.touchmoveListenFunc !== null)
this.touchmoveListenFunc(); // remove previous listener
if (this.touchendListenFunc !== null)
this.touchendListenFunc(); // remove previous listener
if (this.touchcancelListenFunc !== null)
this.touchcancelListenFunc(); // remove previous listener
this.touchmoveListenFunc = null;
this.touchendListenFunc = null;
this.touchcancelListenFunc = null;
}
@HostListener('mousemove', ['$event'])
// @HostListener('touchmove', ['$event']) // don't declare this, as it is added dynamically
onDragMove(event) {
... // do stuff with event
}
@HostListener('mouseup', ['$event'])
// @HostListener('touchend', ['$event']) // don't use these as they are added dynamically
// @HostListener('touchcancel', ['$event']) // don't use these as they are added dynamically
onDragEnd(event) {
... // do stuff
}
ngOnDestroy() {
this.removePreviousTouchListeners();
不要忘记在构造函数中注入 Renderer
(从 @angular/core
导入
我在使用 Angular 2 和 触摸设备 时遇到问题。特别是,当组件通过 NgFor 渲染并在屏幕上(触摸)拖动时。如果在触摸拖动期间发生 NgFor 的重新渲染(由于外部事件更新绑定到 NgFor 的数据,这在我的应用程序中很常见),就会出现问题。 touchmove
事件停止触发,并且需要您抬起手指然后再次放下,这是一种糟糕的移动体验。如果您使用鼠标,则不会出现此问题。
本质上,在我的应用程序中,我监听组件上的 touchstart
事件,通过条件 *ngIf="isDragging"
(不在 NgFor 中)显示另一个 'DragComponent',它是根据 touchmove
事件位置数据在屏幕上移动。
我知道为什么会这样。这是由于 Touch Spec 的浏览器实现。我通常通过将 DOM 元素保留在内存中直到 touchend
或 touchcancel
事件触发来在 vanilla js 中围绕这个问题进行编码。但是,Angular 现在控制着 DOM!他们正在删除仍在使用中的元素!
查看这个 plunker http://plnkr.co/edit/QR6WDzv6NxOmn6LXTngG?p=preview 以更深入地了解我要描述的内容。 (注意 需要触摸屏,或在 Chrome DevTools 中使用触摸模拟)
我还在 Angular 存储库中创建了一个问题 #9864,但没有得到任何回应。我知道他们正忙于为决赛做准备,但我认为这应该在决赛前解决,因为很多用户将在触摸设备上使用 Angular。
我将不胜感激 tips/workarounds/hacks。随时用解决方案更新 plunker。
找到解决方法:
TouchEvents 在 DOM 移除后确实继续触发 但是 它们仅针对 node/element 原始 touchstart
发生的并且不要冒泡(与 MouseEvents 不同,后者令人困惑!)。
因此,我们不能执行一个简单的 @HostListener('touchmove', ['$event'])
并期望它与 DOM 删除一起工作(因为事件侦听器附加到外部组件元素)。我们必须在 touchstart 事件发生时 动态地 添加事件监听器到 目标元素 。然后对 touchend
或 touchcancel
(或 ngOnDestroy()
)执行清理。
索恩:
@HostListener('touchstart', ['$event'])
@HostListener('mousedown', ['$event'])
dragStart(event) {
if (event.touches) { // avoid touch event loss issue
this.removePreviousTouchListeners(); // avoid mem leaks
this.touchmoveListenFunc = this.renderer.listen(event.target, 'touchmove', (e) => { this.onDragMove(e); });
this.touchendListenFunc = this.renderer.listen(event.target, 'touchend', (e) => { this.removePreviousTouchListeners(); this.onDragEnd(e); });
this.touchcancelListenFunc = this.renderer.listen(event.target, 'touchcancel', (e) => { this.removePreviousTouchListeners(); this.onDragEnd(e); });
}
...
}
removePreviousTouchListeners() {
if (this.touchmoveListenFunc !== null)
this.touchmoveListenFunc(); // remove previous listener
if (this.touchendListenFunc !== null)
this.touchendListenFunc(); // remove previous listener
if (this.touchcancelListenFunc !== null)
this.touchcancelListenFunc(); // remove previous listener
this.touchmoveListenFunc = null;
this.touchendListenFunc = null;
this.touchcancelListenFunc = null;
}
@HostListener('mousemove', ['$event'])
// @HostListener('touchmove', ['$event']) // don't declare this, as it is added dynamically
onDragMove(event) {
... // do stuff with event
}
@HostListener('mouseup', ['$event'])
// @HostListener('touchend', ['$event']) // don't use these as they are added dynamically
// @HostListener('touchcancel', ['$event']) // don't use these as they are added dynamically
onDragEnd(event) {
... // do stuff
}
ngOnDestroy() {
this.removePreviousTouchListeners();
不要忘记在构造函数中注入 Renderer
(从 @angular/core