Angular8深入了解Directive指令

2,277 阅读3分钟

angular指令,目的在于影响Dom布局,或者修改Dom属性。

Directive分类

Component

an extension of @Directive()

Demo
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'idata',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.scss']
})
export class UserComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}
  • @Component定义在class上
  • templateUrl、template定义视图模板
@Component源码
export declare interface Component extends Directive {

    changeDetection?: ChangeDetectionStrategy;

    viewProviders?: Provider[];

    moduleId?: string;

    templateUrl?: string;

    template?: string;

    styleUrls?: string[];

    styles?: string[];

    animations?: any[];

    encapsulation?: ViewEncapsulation;

    interpolation?: [string, string];

    entryComponents?: Array<Type<any> | any[]>;

    preserveWhitespaces?: boolean;
}

从中我们得出如下:

  • Component是一种特殊的指令
  • Component 上述自有属性,都是可选
  • 那么Directive的源码又是什么呢?
@Directive源码
export declare interface Directive {
   
   selector?: string;
  
   inputs?: string[];
   
   outputs?: string[];
   
   providers?: Provider[];
   
   exportAs?: string;
   
   queries?: {
       [key: string]: any;
   };
   
   host?: {
       [key: string]: string;
   };
   
   jit?: true;
}

属性指令

Attribute directives are used as attributes of elements

内置指令
  • NgStyle
  • NgClass
自定义指令
  • step 1:
# 要求不能ng开头
ng generate directive highlight
  • code如下:
# src/app/highlight.directive.ts 
import { Directive } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor() { }
}
  • step 2: 添加指令处理逻辑
import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
    constructor(el: ElementRef) {
        # 修改元素背景
       el.nativeElement.style.backgroundColor = 'yellow';
    }
}
  • step3: 模块中声明指令的存在
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HighlightDirective } from './highlight.directive';

@NgModule({
  declarations: [
    AppComponent,
    HighlightDirective # 视图中声明指令的引用
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
  ],
  providers: [],
  bootstrap: [AppComponent],
  exports: []
})
export class AppModule { }
  • step 4: 应用指令
<p appHighlight>Highlight me!</p>

结构指令

Structural directives are responsible for HTML layout

  • 结构指令,影响当前元素以及后代元素
  • 结构指令,大多以*开头
内置结构指令
  • ngIf
# false,不渲染元素,而非渲染后隐藏
<div *ngIf="hero" class="name">{{hero.name}}</div>

  • ngIf false为何不隐藏元素,而是删除元素? 这里应该是框架设计者针对利弊的取舍吧!如果元素仅仅隐藏,那么元素还占据原来的位置,那么对应的鼠标事件,有可能还是存在的,那么就会影响现有组件的功能,视图渲染数据。 具体的可以参考这边文章针对visible,opacity,hiden之间区别,写的挺好的!
  • angular 编译ngIf为以下代码:
    <ng-template [ngIf]="hero">
    <div class="name">{{hero.name}}</div>
    </ng-template>
    
  • ngFor
<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">
  ({{i}}) {{hero.name}}
</div>
  • angular编译为以下代码 :
<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById">
  <div [class.odd]="odd">({{i}}) {{hero.name}}</div>
</ng-template>
  • ngSwitch
<div [ngSwitch]="hero?.emotion">
  <app-happy-hero    *ngSwitchCase="'happy'"    [hero]="hero"></app-happy-hero>
  <app-sad-hero      *ngSwitchCase="'sad'"      [hero]="hero"></app-sad-hero>
  <app-confused-hero *ngSwitchCase="'confused'" [hero]="hero"></app-confused-hero>
  <app-unknown-hero  *ngSwitchDefault           [hero]="hero"></app-unknown-hero>
</div>
自定义结构指令
  • step 1:
      ng generate directive appUnless
    
    • code 如下:
     import { Directive } from '@angular/core';
    
     @Directive({
     selector: '[appUnless]'
     })
     export class AppUnlessDirective {
    
     constructor() { }
    
     }
    
  • step 2: 定义元素逻辑
import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core';

@Directive({
  selector: '[appUnless]'
})
export class AppUnlessDirective {

  private hasView = false;

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

  @Input() set appUnless(condition: boolean) {
    if (!condition && !this.hasView) {
      this.viewContainer.createEmbeddedView(this.templateRef);
      this.hasView = true;
    } else if (condition && this.hasView) {
      this.viewContainer.clear();
      this.hasView = false;
    }
  }

}

  • step 3: 声明指令
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HighlightDirective } from './highlight.directive';
import { AppUnlessDirective } from './app-unless.directive';

@NgModule({
  declarations: [
    AppComponent,
    HighlightDirective,
    AppUnlessDirective # 声明结构指令
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
  ],
  providers: [],
  bootstrap: [AppComponent],
  exports: []
})
export class AppModule { }

  • step 4: 应用指令
<p *appUnless="condition" class="unless a">
  (A) This paragraph is displayed because the condition is false.
</p>

#ts
public condition = false;
constructor(private domSanitizer: DomSanitizer){
    interval(2000).subscribe(() => {
        this.condition = !this.condition;
    });
}

更多推荐

Rxjs 操作符分类后的那些事

Angular Render2你了解吗?

Angular8 日常开发填坑指南

参考

Angular