Вопрос по angular, event-handling, mouseevent – Это ожидаемо, иначе привязка к функции все равно не будет иметь смысла.

13

ализуем функциональность перетаскивания с Angular 2.

Я используюdragover событие просто запуститьpreventDefault() функция. Таким образомdrop событие работает как объяснено вэтот вопрос.

dragover метод обрабатываетсяonDragOver функция в компоненте.

<div draggable="true"
    (dragover)="onDragOver($event)">
...

В компоненте эта функция предотвращает поведение по умолчанию, позволяя перетаскивать перетаскиваемый элемент на эту цель.

onDragOver(event) {
    event.preventDefault();
}

Это работает как ожидалось. Драговер событие запускается каждые несколько сотен миллисекунд.

Но каждый раз, когдаonDragOver функция вызывается, Angular 2 запускает цикл дайджеста. Это замедляет работу приложения. Я хотел бы запустить эту функцию без запуска цикла дайджеста.

Обходное решение, которое мы используем для этого, подписывается на событие элемента и запускает его вне контекста Angular 2 следующим образом:

constructor( ele: ElementRef, private ngZone: NgZone ) {
    this.ngZone.runOutsideAngular( () => {
        Observable.fromEvent(ele.nativeElement, "dragover")
            .subscribe( (event: Event) => {
                event.preventDefault();
            }
        );
    });
}

Это отлично работает. Но есть ли способ достичь этого без прямого доступа к nativeElement?

@echonax Я обновил вопрос: событие dragover запускается каждые несколько сотен миллисекунд, как мы и ожидаем. Но каждый раз, когдаonDragOver при этом вызывается функция, Angular 2 запускает цикл дайджеста. Это замедляет работу приложения. Я хотел бы запустить эту функцию без запуска цикла дайджеста. nipuna777
Что вы подразумеваете под «циклом дайджеста, который срабатывает каждый раз, когда что-то перетаскивается через цель»? Вы имеете в виду, что каждый элемент, который уже находится внутри div, запускает это событие? eko

Ваш Ответ

2   ответа
9

Вы можете отключить детектор изменений, чтобы предотвратить обнаружение изменений для компонента

constructor(private cdRef:ChangeDetectorRef) {}
foo() {
  this.cdRef.detach();
  ...
  this.cdRef.attach();
}
Возможно, отделить его в конструкторе. Я не думаю, что есть общее правило, которое работает для всех случаев. Günter Zöchbauer
Это ожидаемо, иначе привязка к функции все равно не будет иметь смысла. Günter Zöchbauer
Хотя это работает для отключения обнаружения изменений внутри функции (для определенного набора строк), когда событие запускается впервые и функция выполняется, цикл дайджеста все еще выполняется. Есть ли способ предотвратить это? nipuna777
Что если у функции есть оператор возврата? alex351
19

1) Одним интересным решением может быть переопределениеEventManager

таможенно-EVENT-manager.ts

import { Injectable, Inject, NgZone  } from '@angular/core';
import { EVENT_MANAGER_PLUGINS, EventManager } from '@angular/platform-browser';

@Injectable()
export class CustomEventManager extends EventManager {
  constructor(@Inject(EVENT_MANAGER_PLUGINS) plugins: any[], private zone: NgZone) {
    super(plugins, zone); 
  }

  addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {
    if(eventName.endsWith('out-zone')) {
      eventName = eventName.split('.')[0];
      return this.zone.runOutsideAngular(() => 
          super.addEventListener(element, eventName, handler));
    } 

    return super.addEventListener(element, eventName, handler);
  }
}

app.module.ts

  ...
  providers: [
    { provide: EventManager, useClass: CustomEventManager }
  ]
})
export class AppModule {

Использование:

<h1 (click.out-zone)="test()">Click outside ng zone</h1>

<div (dragover.out-zone)="onDragOver($event)">

Пример плунжера

Таким образом, с решением выше вы можете использовать один из этих параметров, чтобы предотвратить поведение по умолчанию и запустить событие за пределами угловой зоны:

(dragover.out-zone)="$event.preventDefault()"
(dragover.out-zone)="false"
(dragover.out-zone)="!!0"

2) Еще одно решение, предоставленное Робом Вормальдом, этоиспользуя черный список для Zonejs

blacklist.ts

/// <reference types='zone.js/dist/zone.js' />

const BLACKLISTED_ZONE_EVENTS: string[] = [
  'addEventListener:mouseenter',
  'addEventListener:mouseleave',
  'addEventListener:mousemove',
  'addEventListener:mouseout',
  'addEventListener:mouseover',
  'addEventListener:mousewheel',
  'addEventListener:scroll',
  'requestAnimationFrame',
];

export const blacklistZone = Zone.current.fork({
  name: 'blacklist',
  onScheduleTask: (delegate: ZoneDelegate, current: Zone, target: Zone,
                   task: Task): Task => {

    // Blacklist scroll, mouse, and request animation frame events.
    if (task.type === 'eventTask' &&
        BLACKLISTED_ZONE_EVENTS.some(
            (name) => task.source.indexOf(name) > -1)) {
      task.cancelScheduleRequest();

      // Schedule task in root zone, note Zone.root != target,
      // "target" Zone is Angular. Scheduling a task within Zone.root will
      // prevent the infinite digest cycle from appearing.
      return Zone.root.scheduleTask(task);
    } else {
      return delegate.scheduleTask(target, task);
    }
  }
});

main.ts

import {blacklistZone} from './blacklist'

blacklistZone.run(() => {
  platformBrowser().bootstrapModuleFactory(...)
})

Плункер с черным списком

Обновить:

5.0.0-бета.7 (2017-09-13)

исправить (платформа-браузер): запустить BLACK_LISTED_EVENTS вне ngZone

следить

https://github.com/angular/angular/issues/19989

https://github.com/angular/angular/commit/e82812b9a9992bc275bba54575f9d780753c1f8f

https://github.com/angular/angular/pull/21681

Обновление 2

Угловой cli включает в себя шаблон для отключения частей патча macroTask / DomEvents.

Просто открой

polyfills.ts

Вы можете найти там следующий код

/**
 * By default, zone.js will patch all possible macroTask and DomEvents
 * user can disable parts of macroTask/DomEvents patch by setting following flags
 */

 // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
 // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
 // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
 /*
 * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
 * with the following flag, it will bypass `zone.js` patch for IE/Edge
 */
// (window as any).__Zone_enable_cross_context_check = true;

https://github.com/angular/devkit/blob/8651a94380eccef0e77b509ee9d2fff4030fbfc2/packages/schematics/angular/application/files/SourceDir/polyfills.ts#L55-L68

Смотрите также:

https://angular.io/guide/browser-support#polyfills-for-non-cli-users
Не могли бы вы предоставить поршень для примера 2? Что-то, что я ищу, однако, неясно, откуда импортируется или указывается «Zone»? Ashg
# 2 Действительно помог мне. Большое спасибо! instantaphex
@Ashg Добавлен плункер. Как вы можете, угловые не будут запускать обнаружение изменений при перемещении мыши. Только когда вы нажимаете кнопку Обновить yurzui
@Ashg Я обновил ответ. Вы также можете включить типы зон в tsconfig вместо директивы Triple-Slash, напримерgithub.com/angular/angular/blob/master/packages/core/..., Или, если вы не хотите иметь типы, просто используйтеdeclare let Zone: any а такжеany для таких типов, какZoneDelegate, Task так далее yurzui
Спасибо yurzui за это, в конце концов я понял эту часть, однако, когда я реализовал это решение, я все же обнаружил, что выражения оцениваются по движениям мыши, onScheduleTask вызывается после того, как выражение компонента было оценено, что для меня побеждает цель, если я что-то упустил. Поэтому я подумал, что работающий Плункер будет полезен. В любом случае, спасибо. Ashg

Похожие вопросы