рдХреЛрдгреАрдп: рд╕рджрд╕реНрдпрддрд╛ рд╕рдорд╛рдкреНрдд рдХрд░рдиреЗ рдХрд╛ рджреВрд╕рд░рд╛ рддрд░реАрдХрд╛

рдШрдЯрдХ рдХреЛрдб рдореЗрдВ рд╕рджрд╕реНрдпрддрд╛ рдЗрд╕ рдХрд╛рд░реНрдп рдХреЛ AsyncPipe рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рд╕реЗ рдмрдЪрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдпрд╣ рд╣рдореЗрд╢рд╛ рд╕рдВрднрд╡ рдирд╣реАрдВ рд╣реИред рд╕рджрд╕реНрдпрддрд╛ рд╕рдорд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рдЕрд▓рдЧ-рдЕрд▓рдЧ рддрд░реАрдХреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рд╡реЗ рд╕рднреА рджреЛ рд╕реЗ рдиреАрдЪреЗ рдЖрддреЗ рд╣реИрдВ - рдореИрдиреБрдЕрд▓ рдЕрдирд╕рдмрд╕реНрдХреНрд░рд╛рдЗрдм рдпрд╛ рдЯреЗрдХ рдпреВрдЯрд┐рд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ред


рд╕рдордп рдХреЗ рд╕рд╛рде, рдореИрдВрдиреЗ рддреЗрдЬреА рд╕реЗ рд╕рджрд╕реНрдпрддрд╛ рд╕рдорд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдкрдиреЗ рдбреЗрдХреЛрд░реЗрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░ рджрд┐рдпрд╛ред рдЖрдЗрдП рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ рдХрд┐ рдпрд╣ рдХреИрд╕реЗ рд╡реНрдпрд╡рд╕реНрдерд┐рдд рдФрд░ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рд╢рд╛рдпрдж рдЖрдкрдХреЛ рдпрд╣ рддрд░реАрдХрд╛ рдкрд╕рдВрдж рдЖрдПрдЧрд╛ред


рдореВрд▓ рд╡рд┐рдЪрд╛рд░ рдпрд╣ рд╣реИ рдХрд┐ рдХрд┐рд╕реА рднреА рд╕рджрд╕реНрдпрддрд╛ рдХреЛ рдПрдХ рд╡рд┐рдзрд┐ рд╕реЗ рд╡рд╛рдкрд╕ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдЙрдиред рд╕рднреА рд╕рджрд╕реНрдпрддрд╛рдПрдБ рдПрдХ рдЕрд▓рдЧ рд╕рдЬреА рд╣реБрдИ рд╡рд┐рдзрд┐ рдореЗрдВ рд╣реЛрддреА рд╣реИрдВ рдФрд░ рдЗрд╕рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рд╣реЛрддреЗ рд╣реИрдВред


(...args: any[]) => Subscription;

рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЖрдкрдХреЛ рдПрдХ рд╡рд┐рдзрд┐ рдкрд░ рдПрдХ рдбреЗрдХреЛрд░реЗрдЯрд░ рд▓рдЯрдХрд╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ рдЬреЛ рд╡рд┐рдзрд┐ рдФрд░ рдЙрд╕рдХреЗ рдкрд░рд┐рдгрд╛рдо рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░ рд╕рдХрддрд╛ рд╣реИред


рддреАрди рдСрдкрд░реЗрд╢рди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреАред


  1. NgOnInit рдкрд░ рдХреЙрд▓ рдХрд░рддреЗ рд╕рдордп, рдХрд┐рд╕реА рдкреНрд░рдХрд╛рд░ рдХреА рд╕рджрд╕реНрдпрддрд╛ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рдмрдирд╛рдИ рдЬрд╛рдиреА рдЪрд╛рд╣рд┐рдПред
  2. рдХрд┐рд╕реА рд╕рджрд╕реНрдпрддрд╛ рдХреЛ рд▓реМрдЯрд╛рдиреЗ рд╡рд╛рд▓реА рд╕рдЬрд╛рдП рдЧрдП рддрд░реАрдХреЗ рдХреЛ рдХреЙрд▓ рдХрд░рддреЗ рд╕рдордп, рдЗрд╕ рд╕рджрд╕реНрдпрддрд╛ рдХреЛ рднрдВрдбрд╛рд░ рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред
  3. рдЬрдм ngOnDestroy рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рд░рд┐рдкреЙрдЬрд┐рдЯрд░реА рд╕реЗ рд╕рднреА рд╕рджрд╕реНрдпрддрд╛рдПрдВ рдкреВрд░реА рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдП (рд╕рджрд╕реНрдпрддрд╛ рд╕рдорд╛рдкреНрдд)ред

рдореБрдЭреЗ рдпрд╛рдж рджрд┐рд▓рд╛рдПрдВ рдХрд┐ рдХреНрд▓рд╛рд╕ рд╡рд┐рдзрд┐ рдбреЗрдХреЛрд░реЗрдЯрд░ рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЖрдзрд┐рдХрд╛рд░рд┐рдХ рдкреНрд░рд▓реЗрдЦрди рдпрд╣рд╛рдБ рд╣реИ


рдпрд╣рд╛рдБ рдбреЗрдХреЛрд░реЗрдЯрд░ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рд╣реИ:


<T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void; 

рдбреЗрдХреЛрд░реЗрдЯрд░ рдХреЛ рдХреНрд▓рд╛рд╕ рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░, рдкреНрд░реЙрдкрд░реНрдЯреА рдХрд╛ рдирд╛рдо рдФрд░ рдЗрдирдкреБрдЯ рдХреЗ рд░реВрдк рдореЗрдВ рдбрд┐рд╕реНрдХреНрд░рд┐рдкреНрдЯрд░ рдорд┐рд▓рддрд╛ рд╣реИред рдореИрдВ рдбрд┐рд╕реНрдХреНрд░рд┐рдкреНрдЯрд░ рдХреА рдЙрдкреЗрдХреНрд╖рд╛ рдХрд░реВрдВрдЧрд╛, рдпрд╣ рдЗрд╕ рдХрд╛рд░реНрдп рдХреЗ рд▓рд┐рдП рдХреЛрдИ рднреВрдорд┐рдХрд╛ рдирд╣реАрдВ рдирд┐рднрд╛рддрд╛ рд╣реИ, рд╢рд╛рдпрдж рд╣реА рдХреЛрдИ рд╡рд┐рдзрд┐ рдбрд┐рд╕реНрдХреНрд░рд┐рдкреНрдЯрд░ рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░реЗрдЧрд╛ред


, , - , , .


,


export function UntilOnDestroy<ClassType extends DirectiveWithSubscription>(): MethodDecorator {
  return function UntilOnDestroyDecorator(target: ClassType, propertyKey: string): TypedPropertyDescriptor<SubscriptionMethod> {
    wrapHooks(target);
    return {
      value: createMethodWrapper(target, target[propertyKey]),
    };
  } as MethodDecorator;
}

, , , тАФ - , , createMethodWrapper.


, .1 . 3 (Subscription). add, unsubscribe .


Subscription


-, .


const subSymbol = Symbol('until-on-destroy');

interface ClassWithSubscription {
  [subSymbol]?: Subscription;
}

, .


createMethodWrapper


2.


function createMethodWrapper(target: ClassWithSubscription, originalMethod: SubscriptionMethod): SubscriptionMethod {
  return function(...args: any[]) {
    const sub: Subscription = originalMethod.apply(this, args);
    target[subSymbol].add(sub);
    return sub;
  };
}

createMethodWrapper , , () . subSymbol, , .


wrapHooks


1 3.


function wrapHooks(target: ClassWithSubscription) {
  if (!target.hasOwnProperty(subSymbol)) {
    target[subSymbol] = null;
    wrapOneHook(target, 'OnInit', t => t[subSymbol] = new Subscription());
    wrapOneHook(target, 'OnDestroy', t => t[subSymbol].unsubscribe());
  }
}

, , subSymbol.


. , , .


. , Angular 9 , . ViewEngine Ivy


const cmpKey = '╔╡cmp';

function wrapOneHook(target: any, hookName: string, wrappingFn: (target: ClassWithSubscription) => void): void {
  return target.constructor[cmpKey]
    ? wrapOneHook__Ivy(target, hookName, wrappingFn)
    : wrapOneHook__ViewEngine(target, hookName, wrappingFn);
}

'╔╡cmp' , Ivy . Ivy .


ViewEngine


function wrapOneHook__ViewEngine(target: any, hookName: string, wrappingFn: (target: ClassWithSubscription) => void): void {
  const veHookName = 'ng' + hookName;
  if (!target[veHookName]) {
    throw new Error(`You have to implements ${veHookName} in component ${target.constructor.name}`);
  }
  const originalHook: () => void = target[veHookName];
  target[veHookName] = function (): void {
    wrappingFn(target);
    originalHook.call(this);
  };
}

, ( ), .


wrappingFn.


Ivy , Ivy. , .


ngOnInit ngOnDestroy.


function wrapOneHook__Ivy(target: any, hookName: string, wrappingFn: (target: ClassWithSubscription) => void): void {
  const ivyHookName = hookName.slice(0, 1).toLowerCase() + hookName.slice(1);
  const componentDef: any = target.constructor[cmpKey];

  const originalHook: () => void = componentDef[ivyHookName];
  componentDef[ivyHookName] = function (): void {
    wrappingFn(target);

    if (originalHook) {
      originalHook.call(this);
    }
  };
}

, , componentDef.


, , OnInit ngOnInit, onInit.


, .




ng new check


ng g c child


app.component.ts


@Component({
  selector: 'app-root',
  template: `
    <button #b (click)="b.toggle = !b.toggle">toggle</button>
    <app-child *ngIf="b.toggle"></app-child>
  `,
})
export class AppComponent {}

child.component.ts


@Component({
  selector: 'app-child',
  template: `<p>child: {{id}}</p>`,
})
export class ChildComponent implements OnInit {
  id: string;

  ngOnInit() {
    this.id = Math.random().toString().slice(-3);
    this.sub1();
    this.sub2();
  }

  @UntilOnDestroy()
  sub1(): Subscription {
    console.log(this.id, 'sub1 subscribe');
    return NEVER.pipe(
      finalize(() => console.log(this.id, 'sub1 unsubscribe'))
    )
      .subscribe();
  }

  sub2(): Subscription {
    console.log(this.id, 'sub2 subscribe');
    return NEVER.pipe(
      finalize(() => console.log(this.id, 'sub2 unsubscribe'))
    )
      .subscribe();
  }
}

toggle app-child :



тАж :



sub1 , sub2 .


.


GitHub рдкрд░ рдХреЛрдгреАрдп 9 рдХреЗ рд▓рд┐рдП рд╕реНрдЯреИрдХрдмреНрд▓рд┐рдЯреНрдЬрд╝ рд▓рд┐рдВрдХ


рдбреЗрдХреЛрд░реЗрдЯрд░ рдПрдХ рдкреИрдХреЗрдЬ рдХреЗ рд░реВрдк рдореЗрдВ NPM рдореЗрдВ рд▓рд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ ngx-until-on-destroy
рдкрд░ рдбреЗрдХреЛрд░реЗрдЯрд░ рд╕реВрддреНрд░реЛрдВ Github


All Articles