نحن نضخ العمل مع الأحداث في Angular

ذات مرة ، كتبت مقالة حول العمل مع EventManager في Angular . في ذلك ، تحدثت عن كيفية حفظ البنية المعتادة لاشتراكات الأحداث ، مع تجنب المحفزات غير الضرورية للتحقق من التغييرات على الأحداث المتكررة والحساسة.


ومع ذلك ، فإن الطريقة التي وصفتها مرهقة ويصعب فهمها. حان الوقت لإعادة كتابة الترشيح على الديكور.



إعادة موجزة


بالنسبة لأولئك الذين لم يقرؤوا ولا يريدون قراءة المقالة السابقة ، ملخص المشكلة:


  1. يسمح Angular بالاشتراك التعريفي في الأحداث في القالب ( (eventName)) ومن خلال الديكور ( @HostListener(‘eventName’)).
  2. باستخدام إستراتيجية التحقق من التغيير ، OnPushستقوم Angular بإجراء فحص في حالة وقوع حدث اشتركنا فيه بهذه الطريقة.
  3. أحداث مثل scroll، mousemove، dragأثار في كثير من الأحيان. في الممارسة العملية ، ما عليك سوى الرد على بعضها (على سبيل المثال ، عندما قام المستخدم بتمرير الحاوية حتى النهاية - قم بتحميل عناصر جديدة).
  4. يعالج الزاوي معالجة الأحداث EventManagerباستخدام EventManagerPlugins المقدمة .
  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