angular11源碼探索三[深刻指令demo篇]

\angular-master\packages\common\src\directivescss

模擬ngClass 方法

@Directive({
  selector: '[appNkClass]'
})
export class NkClassDirective implements DoCheck {

  constructor(private renderer: Renderer2,
              private hostElement: ElementRef
  ) {
  }

  @Input('appNkClass') cssMap: Record<string, boolean>;

  ngDoCheck(): void {
    for (let [key, value] of Object.entries(this.cssMap)) {
      if (value === true) {
        this.renderer.addClass(this.hostElement.nativeElement, key);
      } else {
        this.renderer.removeClass(this.hostElement.nativeElement, key);
      }
    }
  }
}
<h1 [ngClass]="{active:flag}">xxxxxxxxxxx</h1>
<h1 [appNkClass]="{active:flag}">xxxxxxxxxxx</h1>

click屬性裏面編寫方法

<button (click.long)="clickAdd()">Click2</button>

@Directive({
  selector: '[click.long]'
})
export class clickLongDirective implements OnDestroy {
  /*設置一個方法,兩s後執行*/
  private clickTimer: any;
  @Output('click.long')
  longClickEvent = new EventEmitter();

  @HostListener('mousedown')
  onMouse() {
    clearTimeout(this.clickTimer);
    this.clickTimer = window.setTimeout(() => {
      this.longClickEvent.emit();
    }, 2000);
  }

  ngOnDestroy() {
    clearTimeout(this.clickTimer);
  }

  constructor() {
  }
}

雙向事件綁定

<input type="text"  [(nkModel)]="names">

 @Directive({
  selector: '[nkModel]'
})
export class NkModelDirective {
  @Input('nkModel')
  dyName: string = '';
  @Output('nkModelChange')
  emitUpdates = new EventEmitter();

  @HostBinding('value')
  get value() {
    return this.dyName;
  }
          //change 事件是失去焦點觸發
  @HostListener('input', ['$event'])
  update(event: Event) {
    this.dyName = (event.target as HTMLInputElement).value;
    this.emitUpdates.emit(this.dyName);
  }

  constructor() {
  }

}

斷網的時候竟用按鈕

<button (click)="clickAdd()" appNkDisabled>CLIck</button>

@Directive({
  selector: 'button[appNkDisabled]'
})
export class NkDisabledDirective {
  /*需求沒網的時候禁用按鈕*/
  private _offline: boolean = false;

  @HostBinding('disabled')
  get isDisabled() {
    return  this._offline
  }

  /*斷網*/
  @HostListener('window:offline')
  disableButton() {
    this._offline=true;
  }

  /*在線*/
  @HostListener('window:online')
  enableButton() {
    this._offline=false;
  }
  constructor() { }
}

寫一個原生的textrea方法

@Directive({
  selector: 'textarea'
})
export class NkDisabledDirective {
  private readonly _textArea: HTMLTextAreaElement;
  @HostBinding('style')
  defaultStyle = {overflow: 'hidden', height: 'auto', resize: 'none'}

  // 輸入數字的時候,高度隨着行數的增長而增長
  @HostListener('input')
  autoExpand() {
    // 主要是隨着滾動長度的增長height纔會增長
    console.log(this._textArea.scrollHeight);
    //繼承父級的高度
    this._textArea.style.height = 'inherit'
    const computed = window.getComputedStyle(this._textArea);
    const height = parseInt(computed.getPropertyValue('border-top-width'), 10) + this._textArea.scrollHeight
      + parseInt(computed.getPropertyValue('padding-bottom'), 10)
      + parseInt(computed.getPropertyValue('border-bottom-width'), 10)
    this._textArea.style.height = height + 'px';
  }

  constructor(private hostElement: ElementRef) {
    this._textArea = hostElement.nativeElement
  }
}

思考一個問題,window用的是全局的html

爲了使您的應用程序能夠與服務器端渲染一塊兒使用,我建議您不只使用窗口直通令牌,並且還以SSR友好的方式建立此令牌,而無需徹底引用window。Angular具備DOCUMENT用於訪問的內置令牌documentgit

看了寫大佬寫的github

export const WINDOW=new InjectionToken<Window>(
'return reference to window',
{providedIn:'platform',factory:()=>window}
)

有個, 本地Web API的高質量輕量級包裝器web

能夠看看源碼學習下api

而後把咱們寫好的WINDOW 注入到頁面裏面,代替咱們寫的window數組

conststructor(@Inject(WINDOW) private window)
替換以前的改爲 this.window

有時間好好研究依賴注入,服務那塊服務器

*NgIf

<h1 *nkIf="flag">Hello World</h1>

@Directive({
  selector: '[nkIf]'
})
export class NkDisabledDirective {
  @Input('nkIf')
  set IsHidden(shouldHide: boolean) {
    if (shouldHide) {
      this.viewContainer.clear()
    } else {
        // 插入新視圖
      this.viewContainer.createEmbeddedView(this.template)
    }
  }

  constructor(
    private template: TemplateRef<any>,
    private viewContainer: ViewContainerRef
  ) {
  }

}

ngTemplateOutlet

將一個或者多個組件插入視圖中app

@Directive({
  selector: '[nkComponent]'
})
export class NkDisabledDirective {
 /*Type 實例類*/
  @Input('nkComponent')
  set componentToRender(component: Type<any>) {
    // 拿到當前組件dom
    // console.log(this.viewContainer.element);
    // 清空當前的組件
    this.viewContainer.clear()
    // 經過注入器取出 用於獲取給定組件類型的工廠函數
    const resolver = this.viewContainer.injector.get(ComponentFactoryResolver);
    // 經過實例化組件
    this.viewContainer.createComponent(resolver.resolveComponentFactory(component))
  }

  constructor(
    private viewContainer: ViewContainerRef
  ) {
  }

}
<ng-container *ngTemplateOutlet="bool"></ng-container>
// 模擬
<div [nkComponent]="bool"></div>

// 外部引入的組件
  bool = CountDownComponent;

ngFor探索

@Directive({
  selector: '[nkForMap]'
})
export class NkDisabledDirective implements DoCheck {
  @Input('nkForMapIn') map;
  @Input('nkForMapFilter') filter;
  constructor(private viewContainer: ViewContainerRef,
              private template: TemplateRef<any>) {
  }

  ngDoCheck(): void {
    // 能夠拿到值
    console.log(this.filter);
    // 進行for-in操做
    Object.keys(this.map).forEach(key => {
      this.viewContainer.createEmbeddedView(this.template,{$implicit:{key:key,value:this.map[key]}})
    })
  }
}
<ul>
  <li *nkForMap="let entry in user;filter:'s'">{{entry.key}}{{entry.value}}</li>
</ul>

  user = {
    name: 'xxx',
    email: 'bbbb',
    age: 123
  }

獲取剪貼欄的內容

<input type="text" (keydown.paste)="clickDown($event)"  [(ngModel)]="str">

  str: string;
 clickDown(content) {
    //獲取剪貼欄的內容
    this.str=content
  }
@Directive({
  selector: '[keydown.paste]'
})
export class NkModelDirective {
  @Output('keydown.paste')
  pasteEvent = new EventEmitter<string>();

  @HostListener('keydown', ['$event'])
  async detectPaste(event) {
    // 按下ctrl
    // console.log($event.ctrlKey);
    //     按下p
    // console.log($event.code== 'KeyP');
    // 按下ctrl+i
    // 解析系統剪貼板的文本內容返回一個Promise 。
    // navigator.clipboard.readText()
    if (event.ctrlKey && event.code === 'KeyI') {
      const content = await navigator.clipboard.readText();
      this.pasteEvent.emit(content);
    }
  }
  constructor() {
  }
}

ngComponentOutlet

<ng-container *ngComponentOutlet="two"></ng-container>

export class OneComponent implements OnInit, AfterViewInit {
  // 組件
  public two=TwoComponent
}
<div *ngFor="let comp of comps">
  <ng-container *ngComponentOutlet="comp"></ng-container>
</div>
把三個組件放在 comps 數組中
  comps: any[] = [
    OneComponent,
    TwoComponent,
    ThreeComponent
  ];
相關文章
相關標籤/搜索