Angular指令內容小結

前言

在通往angualr的進階之路上,指令的相關內容是必須熟悉的,這篇文章會經過幾個例子來總結一下本身對於指令相關內容的學習。css

指令分類

  • 組件:用於構建UI組件,繼承於 Directive 類
  • 屬性指令: 用於改變組件的外觀或行爲
  • 結構指令: 用於動態添加或刪除DOM元素來改變DOM佈局

1) 全部的組件都是指令,這裏不做說明
2) 屬性型指令會監聽和修改其餘HTML元素或組件的行爲,元素屬性或者DOM屬性,angular有一些內置的屬性指令,好比說:ngClassngStyle
3) 結構指令是負責重塑DOM結構的,經過添加、移除和操縱它們所附加到的宿主元素來實現的。一樣angular也有一些咱們常常用到的內置結構指令,如*ngIf*ngForhtml

下面經過一個小需求來看看如何實現自定義屬性指令以及自定義結構指令。需求是這樣的,實現相似toolTip組件的效果,mouseenter的時候顯示提示信息,mouseleave的時候提示消失。typescript

Angular4 實現自定義屬性指令

1. 準備工做app

首先經過命令行ng g d common/anotherTip來建立anotherTip.directive指令文件,友情提示:經過ng generate生成的文件,默認路徑是src/app,生成文件以下:框架

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

    @Directive({
    selector: '[appAnotherTip]'
    })
    export class AnotherTipDirective implements OnInit {

    constructor() { }

    ngOnInit(): void { }
    }

2. 名詞介紹angular4

背景介紹:佈局

Angular 的口號是 "一套框架,多種平臺。同時適用手機與桌面 (One framework.Mobile & desktop.)",即 Angular 是支持開發跨平臺的應用,好比:Web 應用、移動 Web 應用、原生移動應用和原生桌面應用等。
爲了可以支持跨平臺,Angular 經過抽象層封裝了不一樣平臺的差別,統一了 API 接口。如定義了抽象類 Renderer 、抽象類 RootRenderer 等。此外還定義瞭如下引用類型:ElementRef、TemplateRef、ViewRef 、ComponentRef 和 ViewContainerRef 等學習

因此經過ElementRef就能封裝不一樣平臺下視圖層的native元素,而後經過Render去統一操做這些元素,避免了應用層與渲染層之間的強耦合。測試

首先導入須要的依賴:Input,ElementRef,Renderer2,HostListenerthis

  • Input:輸入屬性,用來獲取須要提示的信息
  • ElementRef: 獲取native元素
  • Render2: 在 Angular 4.x+ 版本,咱們使用 Renderer2 替代 Renderer (Angular V2)。
  • HostListener:屬性裝飾器,用來爲宿主元素添加事件監聽(宿主元素就是使用指令的當前元素)
  1. 具體實現以下:
export class AnotherTipDirective implements OnInit {
    @Input('appAnotherTip') anotherTip: string;

    private visible: boolean = false; // 控制tip是否顯示
    componentHtml: HTMLElement;  // 生成的模板

    constructor(private elementRef: ElementRef, private render2: Renderer2) { }

    ngOnInit(): void {
        this.componentHtml = this.render2.createElement('div');
        this.componentHtml.innerHTML = this.makeHtml();

        const  parentElement = this.elementRef.nativeElement; //父元素
        this.render2.appendChild(parentElement.parentElement, this.componentHtml); // 父元素內添加對應內容
    }

    /**
    * @returns 模板
    */
    makeHtml(): string {
        return `<div class="hoverTipWrapper" style="display: ${this.visible ? 'block': 'none'}">
        <span>${this.anotherTip}</span>
        </div>`
    }


    @HostListener('mouseenter') onmouseenter() {
        this.visible = true;
        this.componentHtml.innerHTML = this.makeHtml();
    }
    
    @HostListener('mouseleave') onmouseleave() {
        this.componentHtml.innerHTML = '';
    }
}

結合註釋以及名詞介紹,上面實現的tip指令的過程應該很清晰了,寫好一個指令以後,就能直接在組件中使用了:

<div  style="position:relative;margin-top:80px;">
  <span appAnotherTip="測試another" >測試another tip指令</span>
</div>

效果以下(css在文章末尾貼出):

Angular4 實現自定義結構指令

這部分暫且先叫作自定義結構指令,爲何暫且,是由於下面的這種實現方式我還不肯定能不能稱之爲結構指令(捂臉。。。),等到弄清楚再來更改,若是有大兄弟知道的,還請留言說一下。

1. 準備工做

首先仍是用命令行ng g d common/directive/tip生成一個tip.directive的文件,而後再ng g c common/component/hoverTip的組件。

2. 名詞介紹:

  • ViewContainerRef:用於表示一個視圖容器,可添加一個或多個視圖。

節選一段來自官網的介紹:

The location of the View Container within the containing View is specified by the Anchor element. Each View Container can have only one Anchor Element and each Anchor Element can only have a single View Container.

Root elements of Views attached to this container become siblings of the Anchor Element in the Rendered View.

包含視圖的視圖容器的位置由錨點元素指定。每一個視圖容器只能有一個錨元素,每一個錨元素只能有一個視圖容器。鏈接到這個容器的視圖的根元素成爲呈現視圖中的錨元素的兄弟元素。

<div  style="position:relative;">
  <span  appTip="測試" >測試tip指令。</span>
</div>

結合上面指令的使用來看,span元素就是視圖容器,appTip="測試"就是錨點元素,由appTip這個指令產生的視圖的根元素會與span成爲兄弟元素。

  • ComponentRef:提供了對組件實例以及與此組件實例相關的其餘對象的訪問

  • ComponentFactory:提供一個create方法,建立一個component
  • ComponentFactoryResolver:提供一個resolveComponentFactory方法,該方法接收一個組建類做爲參數,生成一個基於該組建類的組件工廠

  1. 具體實現:
export class TipDirective {

  public hoverTip: ComponentRef<HovertipComponent>;
  public factory: ComponentFactory<HovertipComponent>;

  constructor(private viewContainer: ViewContainerRef, private resolve: ComponentFactoryResolver) {
    // 獲取組件工廠
    //ViewContainerRef 用於表示一個視圖容器
    // 服務對象ComponentFactoryResolver提供resolveComponentFactory方法,接收一個組件類做爲參數,返回ComponentFactory
    this.factory = this.resolve.resolveComponentFactory(HovertipComponent);
  }

  @Input('appTip') tipText: string;

  @HostListener('mouseenter') onmouseenter() {
    // 建立以前先刪除視圖
    this.viewContainer.clear();
    console.log(this.viewContainer); //span
    // 建立組件
    this.hoverTip = this.viewContainer.createComponent(this.factory);
    this.hoverTip.instance.tipText = this.tipText;
  }

  @HostListener('mouseleave') onmouseleave() {
    if(this.hoverTip) {
      this.hoverTip.destroy();
    }
  }
}

關於使用方法,上文介紹名詞的時候已經說明了。執行結果以下:

貼一下上述tip使用的css:

.hoverTipWrapper{
    position: absolute;
    height: 30px;
    line-height: 30px;
    // top: 0;
    // left: 0;
    bottom: calc(100% + 5px);
    left: 10px;
    background-color: #000000;
    padding: 0 5px;
    border-radius: 3px;

    &::after{
        content: '';
        position: absolute;
        height: 0;
        width: 0;
        border: 4px solid transparent;
        border-top-color: #000000;
        left: 10px;
        top: 100%;
    }

    span {
        color: #fff;
        font-size: 12px;
    }
}

總結

關於指令這一塊,仍是要在多寫多練的基礎上再去深刻了解原理,剛開始接觸很容易被一些類,服務搞得暈乎乎的,有寫的不對的地方歡迎指出;寫這篇文章的過程中又發現了一些不懂不會的點。作個計劃表,後面會依次解決下列知識點:

  1. ViewContainerRef.createComponent()與ComponentFactory.create()的區別
  2. constructor和組件的生命週期關係
  3. Angular檢測相關問題
相關文章
相關標籤/搜索