Angular 2 how to keep event from triggering digest loop/detection cycle?

后端 未结 2 724
谎友^
谎友^ 2020-12-01 05:45

We are implementing drag and drop functionality with Angular 2.

I\'m using the dragover event just to run the preventDefault() function. S

相关标签:
2条回答
  • 2020-12-01 06:11

    You can detach the change detector to prevent change detection to be invoked for a component

    constructor(private cdRef:ChangeDetectorRef) {}
    foo() {
      this.cdRef.detach();
      ...
      this.cdRef.attach();
    }
    
    0 讨论(0)
  • 2020-12-01 06:13

    1) One interesting solution might be overriding EventManager

    custom-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 {
    

    Usage:

    <h1 (click.out-zone)="test()">Click outside ng zone</h1>
    
    <div (dragover.out-zone)="onDragOver($event)">
    

    Plunker Example

    So with solution above you can use one of these options to prevent default behavior and run event outside angular zone:

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

    2) One more solution provided by Rob Wormald is using blacklist for 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(...)
    })
    

    Plunker with blacklist

    Update:

    5.0.0-beta.7 (2017-09-13)

    fix(platform-browser): run BLACK_LISTED_EVENTS outside of ngZone

    Follow

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

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

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

    Update 2

    Angular cli includes template for disabling parts of macroTask/DomEvents patch.

    Just open

    polyfills.ts

    You can find there the following code

    /**
     * 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

    See also:

    • https://angular.io/guide/browser-support#polyfills-for-non-cli-users
    0 讨论(0)
提交回复
热议问题