https://netbasal.com/inspiration-for-custom-decorators-in-angular-95aeb87f072c

 

以下代码使用了 class decorator, method decorator, property decorator

 

import { environment } from 'src/environments/environment';
import { Component, OnChanges, Input } from '@angular/core';
import { AppModule } from 'src/app/app.module';
import { Observable } from 'rxjs';

export function NgLog(): ClassDecorator {
  return (constructor: any) => {
    console.log('cons:', constructor);
    if (!environment.production) {
      // You can add/remove events for your needs
      const LIFECYCLE_HOOKS = [
        'ngOnInit',
        'ngOnChanges',
        'ngOnDestroy'
      ];
      const component = constructor.name;

      LIFECYCLE_HOOKS.forEach(hook => {
        console.log('123:',hook);
        const original = constructor.prototype[hook];

        constructor.prototype[hook] = function (...args) {
          console.log(`%c ${component} - ${hook}`, `color: #4CAF50; font-weight: bold`, ...args, constructor.prototype);
          original && original.apply(this, args);
        }
      });
    }

  }
} 

export function PageTrack(pageName: string): ClassDecorator {

  return function (constructor: any) {
    // const analyticsService = AppModule.injector.get(AnalyticsService);

    const ngOnInit = constructor.prototype.ngOnInit;

    constructor.prototype.ngOnInit = function (...args) {
      // analyticsService.visit(pageName);
      console.log(`%c visit: ${pageName}`, `color: #4CAF50; font-weight: bold`);
      ngOnInit && ngOnInit.apply(this, args);
    }

    const ngOnDestroy = constructor.prototype.ngOnDestroy;

    constructor.prototype.ngOnDestroy = function (...args) {
      // analyticsService.leave(pageName);
      console.log(`%c leave: ${pageName}`, `color: green; font-weight: bold`);
      ngOnDestroy && ngOnDestroy.apply(this, args);
    }

  }
}


export function log$(target: any, propertyKey: string) {
  let propertyValue;

  function getter() {
    return propertyValue;
  }

  function setter(value: any) {
    if (value instanceof Observable) {
      propertyValue = value.forEach(res => {
        const isArrayOfObjects = Array.isArray(res) && typeof res[0] === 'object';
        const logType = isArrayOfObjects ? 'table' : 'log';
        console.groupCollapsed(propertyKey);
        console[logType](res);
        console.groupEnd();
      });
    } else {
      console.error('none observable', value);
      propertyValue = value;
    }
  }

  Object.defineProperty(target, propertyKey, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true
  });
}


export function throttle(milliseconds: number = 500): MethodDecorator {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value;
    descriptor.value = () => {
      console.error('throttle...........');
    }
    return descriptor;
  };
}


@Component({
  selector: 'fly-aa',
  template: `
    <div >zzzzz {{zz}}</div>
  `
})
@NgLog()
@PageTrack("fly")
export class FlyComponent implements OnChanges {
  @Input() @log$ zz: string;
   name = 'Angular';
   
  mm() {
    console.log('m');
  }

  @throttle()
  ngOnChanges(ss) {
    console.log('cha:', ss);
  }
  ngOnDestory(ss) {
    console.log('destroy:', ss);
  }
}

  

【转】angular 自定义 component decorator

 

 

 

-------------------------------------------------------------

 

This post assumes that you at least have some working knowledge of Angular and Decorators.

If you have no prior knowledge on the subject, you can read the following articles:

  • Make your Code Cleaner with Decorators
  • Automagically Unsubscribe in Angular
  • Decorators & metadata reflection in TypeScript: From Novice to Expert

ngOnChanges lifecycle hooks.

import { environment } from "../environments/environment";
export function NgLog() : ClassDecorator {
  return function ( constructor : any ) {
    if( !environment.production ) {
      // You can add/remove events for your needs
      const LIFECYCLE_HOOKS = [
        'ngOnInit',
        'ngOnChanges',
        'ngOnDestroy'
      ];
      const component = constructor.name;

      LIFECYCLE_HOOKS.forEach(hook => {
        const original = constructor.prototype[hook];

        constructor.prototype[hook] = function ( ...args ) {
          console.log(`%c ${component} - ${hook}`, `color: #4CAF50; font-weight: bold`, ...args);
          original && original.apply(this, args);
        }
      });
    }

  }
}

  

We just log the hook and calling the original method. Let’s use the decorator.

@Component({
  selector: 'posts-page',
  template: `
    <posts [posts]="posts$ | async"></posts>
  `
})
@NgLog()
export class PostsPageComponent {
  constructor( private store : Store<any> ) {
    this.posts$ = store.select('posts');
  }
}

    
@Component({
  selector: 'posts',
  template: `
    <p *ngFor="let post of posts">{{post.title}}</p>
  `
})
@NgLog()
export class PostsComponent implements OnInit {
  @Input() posts = [];
}

  

【转】angular 自定义 component decorator

 

 

Throttle Method Decorator

This decorator will be helpful when working for example with scroll events.

import t from 'lodash.throttle';

export function throttle( milliseconds : number = 500 ) : MethodDecorator {
  return function ( target : any, propertyKey : string, descriptor : PropertyDescriptor ) {
    const original = descriptor.value;
    descriptor.value = t(original, milliseconds);
    return descriptor;
  };
}

  

lodash and replacing the original method with our “throttle” version. Let’s use the decorator.

@Component({
  selector: 'app-posts-page',
  template: `
     <posts [posts]="posts$ | async"></posts>
  `
})
export class PostsPageComponent {
  constructor( private store : Store<any> ) {
    this.posts$ = store.select('posts');
  }

  @HostListener('document:scroll')
  @throttle()
  scroll() {
    console.log('scroll');
  }

}

  

【转】angular 自定义 component decorator

 

 

Track Page Class Decorator

This decorator will be helpful when you need to report page visits to your analytics provider.

import { AnalyticsService, AppModule } from "./app.module";
export function PageTrack( pageName : string ): ClassDecorator {

  return function ( constructor : any ) {
    const analyticsService = AppModule.injector.get(AnalyticsService);

    const ngOnInit = constructor.prototype.ngOnInit;

    constructor.prototype.ngOnInit = function ( ...args ) {
      analyticsService.visit(pageName);
      ngOnInit && ngOnInit.apply(this, args);
    }

    const ngOnDestroy = constructor.prototype.ngOnDestroy;

    constructor.prototype.ngOnDestroy = function ( ...args ) {
      analyticsService.leave(pageName);
      ngOnDestroy && ngOnDestroy.apply(this, args);
    }

  }
}

  

Analytics service then invoke the original method.

@Component({
  ...
})
@PageTrack('blog')
export class PostsPageComponent {

  constructor( private store : Store<any> ) {
    this.posts$ = store.select('posts').pluck('data');
  }

}

【转】angular 自定义 component decorator

 

 

  

Log Observable Property Decorator

This decorator will be helpful for debugging purposes. You can also achieve the same thing with a custom operator.

export function log$( target : any, propertyKey : string ) {
  let propertyValue;

  function getter() {
    return propertyValue;
  }

  function setter( value : any ) {
    if( value instanceof Observable ) {
      propertyValue = value.do(res => {
        const isArrayOfObjects = Array.isArray(res) && typeof res[0] === 'object';
        const logType = isArrayOfObjects ? 'table' : 'log';
        console.groupCollapsed(propertyKey);
        console[logType](res)
        console.groupEnd();
      });
    } else {
      propertyValue = value;
    }
  }

  Object.defineProperty(target, propertyKey, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true
  });
}

  

console.log .

Let’s use the decorator.

@Component({
  selector: 'posts-page',
  template: `
      <posts [posts]="posts$ | async"></posts>
  `
})
export class PostsPageComponent implements OnInit {

  @log$ posts$ : Observable<Post[]>;
    
  constructor( private store : Store<any> ) {
    this.posts$ = store.select('posts');
  }

}

  

【转】angular 自定义 component decorator

 

 

Conclusion:

You can leverage decorators in your apps and create powerful things with them. Decorators are not only for frameworks or libraries, so be creative and start using them.

 to read more about Angular, JS and Vue.

相关文章: