我们在Angular中处理事件

从前,我写了一篇关于在Angular中使用EventManager的文章在其中,我谈到了如何保存事件订阅的常规语法,同时避免不必要的触发来检查频繁和敏感事件的更改。


但是,我描述的方法麻烦且难以理解。现在是时候重写装饰器上的过滤了。



简短重播


对于那些还没有阅读并且不想阅读上一篇文章的人,问题摘要:


  1. Angular允许以声明方式订阅模板((eventName))和装饰器(@HostListener(‘eventName’))中的事件。
  2. 使用更改验证策略,OnPush如果发生我们以这种方式订阅的事件Angular将运行验证。
  3. 活动如scrollmousemovedrag引发非常频繁。实际上,您只需要响应其中的一些(例如,当用户将容器滚动到末尾时,加载新元素)。
  4. Angular EventManager使用提供EventManagerPlugin处理事件处理
  5. Angular , .

. . .



Angular (keydown.ctrl.enter ).


, EventManager. . Angular , EVENT_MANAGER_PLUGINS . , , . addEventListener , , , , , . .


preventDefault stopPropagation. , , , :


@Injectable()
export class StopEventPlugin {
  supports(event: string): boolean {
    return event.split('.').includes('stop');
  }

  addEventListener(
    element: HTMLElement, 
    event: string, 
    handler: Function
  ): Function {
    const wrapped = (event: Event) => {
      event.stopPropagation();
      handler(event);
    };

    return this.manager.addEventListener(
      element,
      event
        .split('.')
        .filter(v => v !== 'stop')
        .join('.'),
      wrapped,
    );
  }
}

. , :


  1. Angular, .
  2. .
  3. .

, NgZone :


@Injectable()
export class SilentEventPlugin {
  supports(event: string): boolean {
    return event.split('.').includes('silent');
  }

  addEventListener(
    element: HTMLElement,
    event: string,
    handler: Function
  ): Function {
    return this.manager.getZone().runOutsideAngular(() =>
      this.manager.addEventListener(
        element,
        event
          .split('.')
          .filter(v => v !== 'silent')
          .join('.'),
        handler,
      ),
    );
  }
}

, .



, -. /, this.


, , . — - , , . $event @HostListener. :


export function shouldCall<T>(
  predicate: Predicate<T>
): MethodDecorator {
  return (_target, _key, desc: PropertyDescriptor) => {
    const {value} = desc;

    desc.value = function(this: T, ...args: any[]) {
      if (predicate.apply(this, args)) {
        value.apply(this, args);
      }
    };
  };
}

. — - Angular, .


Angular 10 Ivy , markDirty(this). , - NgZone. . , . , NgZone , :


@Injectable()
export class ZoneEventPlugin {
  supports(event: string): boolean {
    return event.split('.').includes('init');
  }

  addEventListener(
    _element: HTMLElement,
    _event: string, 
    handler: Function
  ): Function {
    const zone = this.manager.getZone();
    const subscription = zone.onStable.subscribe(() => {
      subscription.unsubscribe();
      handler(zone);
    });

    return () => {};
  }
}

.init , ( , ). @HostListener(‘prop.init’, [‘$event’]) :


export function shouldCall<T>(
  predicate: Predicate<T>
): MethodDecorator {
  return (_, key, desc: PropertyDescriptor) => {
    const {value} = desc;

    desc.value = function() {
      const zone = arguments[0] as NgZone;

      Object.defineProperty(this, key, {
        value(this: T, ...args: any[]) {
          if (predicate.apply(this, args)) {
            zone.run(() => {
              value.apply(this, args);
            });
          }
        },
      });
    };
  };
}

, . , . Ivy.




, , :
https://stackblitz.com/edit/angular-event-filter-decorator


, , , . , , . async Observable :


<p *ngFor="let item of service.items$ | async">{{item}}</p>

, :


export function scrolledToBottom(
   {scrollTop, scrollHeight, clientHeight}: HTMLElement
): boolean {
  return scrollTop >= scrollHeight - clientHeight - 20;
}

@Component({
  selector: 'awesome-component',
  templateUrl: './awesome-component.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AwesomeComponent {
  constructor(@Inject(Service) readonly service: Service) {}

  @HostListener('scroll.silent', ['$event.currentTarget'])
  @HostListener('init.onScroll', ['$event'])
  @shouldCall(scrolledToBottom)
  onScroll() {
    this.service.loadMore();
  }
}

. :
https://stackblitz.com/edit/angular-event-filters-scroll


, . CustomEvent, . .


(1 gzip) open-source- @tinkoff/ng-event-filters. Angular 10 2.0.0, markDirty(this), Angular 4.



npm-


-, open source, ? Angular Open-source Library Starter, . CI, , , CHANGELOG, .

All Articles