【Angular】關(guān)于ViewChild和ContentChild的區(qū)別

一般來說,我們編寫組件或者抽取組件的時候,總是會考慮到組件的復(fù)用性以及擴展性。當(dāng)我們想讓組件變成嵌套關(guān)系的時候,我們就需要考慮這個子組件是寫死在父組件下面?還是抽取出來作為一個外部組件然后插入到父組件中?其實這個主要是看個人選擇了。在angular中我們可以使用這兩種方式封裝組件。而ViewChildcontentChild這兩個裝飾器官網(wǎng)上面的定義是這樣的:

ViewChild:屬性裝飾器,用于配置一個視圖查詢。 變更檢測器會在視圖的 DOM 中查找能匹配上該選擇器的第一個元素或指令。 如果視圖的 DOM 發(fā)生了變化,出現(xiàn)了匹配該選擇器的新的子節(jié)點,該屬性就會被更新。

ContentChild:用于配置內(nèi)容查詢的參數(shù)裝飾器。用于從內(nèi)容 DOM 獲取與此選擇器匹配的第一個元素或指令。如果內(nèi)容 DOM 發(fā)生了更改,并且有一個新的子項與選擇器匹配,則該屬性將被更新。

其實,這兩個裝飾器其實就是為了獲取子組件的引用而已,相當(dāng)于vue和react中的ref。只不過angular這個可以通過ViewChildren或者ContentChildren來獲取多個引用。這個所謂的引用或者angular的視圖查詢或者內(nèi)容查詢獲取到的值其實是這個子組件生成出來的實例。我們可以像使用vue或者react似的使用這兩個查詢來直接修改子組件的屬性或者調(diào)用子組件的方法。

接下來我會舉一個例子來說明這兩個(或者說四個)裝飾器的用法。

  • 現(xiàn)在我們有一個業(yè)務(wù)場景:假設(shè)有一個新聞列表:

    <ul>
      <li>
          <span>1</span>
          <span>【原神】鐘離將在1.5版本復(fù)刻,大家準(zhǔn)備好原石了嗎?</span>
          <span>2021/4/16</span>
      </li>
    </ul>
    

    li中有三個span,分別表示新聞id,新聞標(biāo)題,新聞時間。我們現(xiàn)在的需求是,將這個組件抽離出來。然后將li的部分封裝成一個子組件。如下:

  • 首先,我們封裝這個子組件<news-item>

    import {Component, Input} from '@angular/core';
    import {New} from './models';
    
    @Component({
      selector:'news-item',
      template:`
        <li [style]="{display:'flex',justifyContent:'center',alignItems:'center'}">
          <p>id:{{Item.id}}</p>
          <p>{{Item.title}}</p>
          <p>{{Item.time}}</p>
        </li>
      `,
      ]
    })
    export class NewsItem {
      @Input() Item:New;
      constructor() {
      }
    }
    

    然后定義一下New

    export interface New {
      id:number;
      title:string,
      time:string;
    }
    
  • 然后,我們將ul整體封裝成一個父組件。然后使用兩種不同的方式來封裝。

    • 第一種方法:將子組件封裝進父組件中;

    • 第二種方法:子組件獨立在外,使用的時候才將其放入父組件的標(biāo)簽之內(nèi);

  • 先用第一種方法:

    NewsListByViewChild.ts代碼如下:

    import {AfterViewInit, Component, Input, QueryList, ViewChild, ViewChildren} from '@angular/core';
    import {New} from './models';
    import {NewsItem} from './NewsItem';
    
    @Component({
      selector:'news-list-view',
      template:`
        <ul>
          <news-item *ngFor="let n of NewsList" [Item]="n"></news-item>
        </ul>
      `
    })
    export class NewsListByViewChild implements AfterViewInit{
      public NewsList:New[] = [
        {
          id:1,
          title:'view-標(biāo)題1',
          time:'2021/4/17'
        },
        {
          id:2,
          title:'view-標(biāo)題2',
          time:'2021/4/19'
        },
        {
          id:3,
          title:'view-標(biāo)題3',
          time:'2021/5/6'
        }
      ]
      //使用viewchild獲取單個的符合條件的組件或者指令
      @ViewChild(NewsItem) newItem:NewsItem;
      //使用viewChildren獲取多個的符合條件的組件或者指令,QueryList是一個類似于數(shù)組的可迭代對象,我們可以使用get([索引值])的方法來取得每一個元素
      @ViewChildren(NewsItem) allNewsItem:QueryList<NewsItem>
      constructor() { }
      //ViewChild設(shè)置的查詢,只會在AfterViewInit之后的生命周期訪問到。
      ngAfterViewInit() {
          console.log('newItem:',this.newItem);
          console.log('allNewsItem:',this.allNewsItem);
          //使用get()來獲取單獨的元素
          console.log('allNewsItem01:',this.allNewsItem.get(0));
          console.log('allNewsItem02:',this.allNewsItem.get(1));
          console.log('allNewsItem03:',this.allNewsItem.get(2));
          //然后我們可以直接調(diào)用子組件的屬性和方法。
          setTimeout(()=>{
              this.newItem.Item.id = 99;
              this.allNewsItem.get(1).title = '【原神】新角色優(yōu)菈閃亮登場!'
          })
      }
    }
    
    

    將其導(dǎo)入使用:

    app.component.ts中調(diào)用

    @Component({
      selector: 'app-root',
      template: `
      <news-list-view></news-list-view>
      `,
    })
    export class AppComponent {}
    
  • 第二種方法:

    NewsListByContentChild.ts代碼如下:

    import {AfterContentInit, Component, ContentChild, ContentChildren, QueryList} from '@angular/core';
    import {NewsItem} from './NewsItem';
    
    @Component({
      selector:'news-list-content',
      template:`
        <ul>
          <ng-content></ng-content>
        </ul>
      `
    })
    export class NewsListByContentChild implements AfterContentInit{
      @ContentChild(NewsItem) newsItem:NewsItem;
      @ContentChildren(NewsItem) allNewsItem:QueryList<NewsItem>
      constructor() {}
      //ContentChild設(shè)置的查詢,只會在調(diào)用AfterContentInit之后才能訪問到
      ngAfterContentInit() {
        console.log("NewsItem:",this.newsItem);
        console.log("allNewsItem:",this.allNewsItem);
      }
    }
    
    

    然后將其導(dǎo)入app.component.ts中使用

    //導(dǎo)入語句省略
    @Component({
      selector: 'app-root',
      template: `
      <news-list-content>
          <news-item *ngFor="let n of NewsList" [Item]="n"></news-item>
        </news-list-content>
      `,
    })
    export class AppComponent {
        public NewsList:New[] = [
        {
          id:4,
          title:'content-標(biāo)題1',
          time:'2021/3/21'
        },
        {
          id:5,
          title:'content-標(biāo)題2',
          time:'2021/4/23'
        },
        {
          id:6,
          title:'content-標(biāo)題3',
          time:'2021/5/30'
        }
      ]
    }
    
總結(jié):

ViewChild和ContentChild一開始了解不了就是因為一開始沒有找到在vue或者react中對應(yīng)什么api。其實就是相當(dāng)于ref。只不過是寫法有不同而已。當(dāng)我們熟悉了之后,使用這兩個(或者四個)其實就得心應(yīng)手了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、安裝最新版本的 nodejs 注意:請先在終端/控制臺窗口中運行命令 node -v 和 npm -v, 來驗...
    liuguangsen閱讀 2,279評論 0 1
  • Angular介紹 Angular安裝、創(chuàng)建項目、目錄結(jié)構(gòu)、組件、服務(wù) 創(chuàng)建組件、綁定數(shù)據(jù)、綁定屬性、數(shù)據(jù)循環(huán)、條...
    地瓜的筆記閱讀 676評論 0 2
  • Angular7入門總結(jié)篇 6 2019.01.08 19:34:05 字?jǐn)?shù) 4854閱讀 46093 發(fā)表于 h...
    痞_4fc8閱讀 1,593評論 0 5
  • 有時不得不面對一些需要在組件中直接操作DOM的情況,如我們的組件中存在大量的CheckBox,我們想獲取到被選中的...
    阿踏閱讀 1,255評論 0 1
  • 一.課程簡介 (注意:這里的AngularJS指的是2.0以下的版本) AngularJS的優(yōu)點: 模板功能強大豐...
    壹點微塵閱讀 1,005評論 0 0

友情鏈接更多精彩內(nèi)容