【Angular】關(guān)于模板輸入變量(let-變量)的理解

前言

我這個(gè)人令野,寫文章或者說(shuō)心得,不喜歡直接抄官網(wǎng)上面的東西忆矛,實(shí)在是沒(méi)啥意思。我還是喜歡用我的大白話來(lái)寫文章请垛。今天這個(gè)關(guān)于模板輸入變量的這個(gè)我今天啃官網(wǎng)啃了許久催训,總算是初步的了解了是啥東西。

let-變量到底是什么

我想研究這玩意的起因在于之前我使用ng-zorro的時(shí)候宗收,用到了它的分頁(yè)組件Pagination官網(wǎng)鏈接)漫拭。這其中有一個(gè)自定義上一頁(yè)下一頁(yè)模板的功能。代碼的話如下:

@Component({
  selector: 'nz-demo-pagination-item-render',
  template: `
    <nz-pagination [nzPageIndex]="1" [nzTotal]="500" [nzItemRender]="renderItemTemplate"></nz-pagination>
    <ng-template #renderItemTemplate let-type let-page="page">
      <ng-container [ngSwitch]="type">
        <a *ngSwitchCase="'page'">{{ page }}</a>
        <a *ngSwitchCase="'prev'">Previous</a>
        <a *ngSwitchCase="'next'">Next</a>
        <a *ngSwitchCase="'prev_5'"><<</a>
        <a *ngSwitchCase="'next_5'">>></a>
      </ng-container>
    </ng-template>
  `
})
export class NzDemoPaginationItemRenderComponent {}

看完這個(gè)之后我就很是疑惑混稽,這個(gè)let是啥采驻,為啥let-跟上一個(gè)變量之后就有值了呢?然后我就開始在官網(wǎng)中找這個(gè)let是什么東西匈勋。最終礼旅,我在主要概念-指令-結(jié)構(gòu)性指令微語(yǔ)法一節(jié)找到了關(guān)于let的說(shuō)明。官網(wǎng)描述:微語(yǔ)法洽洁。

在下面還有一段簡(jiǎn)短的說(shuō)明:

模板輸入變量(Template input variable)

模板輸入變量是這樣一種變量痘系,你可以在單個(gè)實(shí)例的模板中引用它的值。 這個(gè)例子中有好幾個(gè)模板輸入變量:hero饿自、iodd汰翠。 它們都是用 let 作為前導(dǎo)關(guān)鍵字临谱。

......

你使用 let 關(guān)鍵字(如 let hero)在模板中聲明一個(gè)模板輸入變量。 這個(gè)變量的范圍被限制在所重復(fù)模板的單一實(shí)例上奴璃。 事實(shí)上,你可以在其它結(jié)構(gòu)型指令中使用同樣的變量名城豁。

......

官網(wǎng)還是一如既往的不說(shuō)人話苟穆,短短幾句話就給你介紹完了,也不告訴你這玩意怎么用唱星,也不告訴你let聲明的變量的值到底是從哪里來(lái)的雳旅。我越看越氣,得间聊,官網(wǎng)你不說(shuō)攒盈,我自己找。

先說(shuō)一個(gè)粗略的結(jié)論:let聲明的變量是這個(gè)template模板的上下文對(duì)象中的變量哎榴。不然為啥叫模板輸入變量呢型豁。在*ngFor內(nèi)幕這小節(jié)中,我們了解到了其內(nèi)幕尚蝌,結(jié)構(gòu)性指令其實(shí)就是將宿主元素包裹在一個(gè)<ng-template></ng-template>中迎变,然后在這個(gè)模板上將*ngFor中的表達(dá)式解析成一個(gè)個(gè)的let模板輸入變量和這個(gè)指令需要傳入的值,官方示例代碼如下:

//解析前的模板
<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>
  • 在此提示一下飘言,所謂的宿主元素就是指令所在的那個(gè)元素衣形,像上面的例子中,div就是*ngFor指令的宿主元素姿鸿。
  • trackBy這個(gè)類似于vue和react中的key谆吴。可以給模板一個(gè)標(biāo)識(shí)苛预,使其重新渲染的時(shí)候性能開銷降低一些句狼。

然后我們?cè)倏垂倬W(wǎng)說(shuō)的:

  • let-ilet-odd 變量是通過(guò) let i=indexlet odd=odd 來(lái)定義的。 Angular 把它們?cè)O(shè)置為上下文對(duì)象中的 indexodd 屬性的當(dāng)前值碟渺。
  • 這里并沒(méi)有指定 let-hero 的上下文屬性鲜锚。它的來(lái)源是隱式的。 Angular 將 let-hero 設(shè)置為此上下文中 $implicit 屬性的值苫拍, 它是由 NgFor 用當(dāng)前迭代中的英雄初始化的芜繁。

看完這段描述,我們可以得知:angular為這個(gè)模板設(shè)置了上下文對(duì)象绒极。但是我們看不到這個(gè)過(guò)程骏令,因?yàn)檫@是在ngFor的源碼內(nèi)部實(shí)現(xiàn)的。并且這個(gè)上下文對(duì)象具備indexodd屬性垄提,并且包含一個(gè)$implicit(implicit:含蓄的榔袋;不直接言明的)的屬性周拐。那么我們推斷這個(gè)上下文對(duì)象至少有以下幾個(gè)屬性:

{
    $implicit:null,
    index:null,
    odd:null,
}

那么我們聲明let變量的本質(zhì)其實(shí)就是聲明一個(gè)變量獲取上下文對(duì)象中的同名屬性的值let-hero不進(jìn)行任何賦值的話凰兑,hero默認(rèn)等于$implicit的值妥粟。無(wú)論是有多少個(gè)let-alet-b吏够,let-c還是let-me勾给。聲明的這個(gè)變量的值都等于$implicit的值。而我們進(jìn)行賦值過(guò)的锅知,比如let-i = "index"播急,i的值就等于這個(gè)上下文對(duì)象中的index屬性對(duì)應(yīng)的值。

上下文對(duì)象是如何設(shè)置的

當(dāng)我們知道這個(gè)上下文對(duì)象是什么了售睹,就該想這個(gè)上下文對(duì)象是怎么設(shè)置的了桩警。

結(jié)構(gòu)性指令這一節(jié)當(dāng)中,我們跟著官方示例做了一遍這個(gè)自定義結(jié)構(gòu)性指令(如果還沒(méi)有做的話昌妹,建議先跟著做一遍)捶枢。在UnlessDirective這個(gè)指令中,其構(gòu)造器constructor聲明了兩個(gè)可注入的變量飞崖,分別是TemplateRefViewContainerRef柱蟀。官網(wǎng)給的解釋我覺得太過(guò)晦澀難懂,我這里給出一下我自己的理解:TemplateRef代表的是宿主元素被包裹之后形成的模板的引用蚜厉。而ViewContainerRef代表的是一個(gè)視圖容器的引用长已。那么問(wèn)題來(lái)了,這個(gè)視圖容器在哪兒呢昼牛?我們?cè)?code>constructor構(gòu)造器中打印一下ViewContainerRef术瓮。打印結(jié)果如圖:

ViewContainerRef的打印結(jié)果

然后我們點(diǎn)進(jìn)去這個(gè)comment元素。發(fā)現(xiàn)其就是一個(gè)注釋元素贰健。如圖所示:

注釋元素

其實(shí)我也不是很確定這個(gè)視圖容器到底是不是這個(gè)注釋元素胞四。但是毋庸置疑的是,視圖容器和宿主元素是兄弟關(guān)系伶椿,緊挨著宿主元素辜伟。我們可以使用ViewContainerRef中的createEmbeddedView()方法(Embedded:嵌入式,內(nèi)嵌式)脊另,將templateRef模板引用傳入進(jìn)去导狡,創(chuàng)建出來(lái)一個(gè)真實(shí)的視圖。由于這個(gè)視圖是被插入到視圖容器ViewContainerRef中了偎痛,所以又叫內(nèi)嵌視圖旱捧。那么這又和我們的上下文對(duì)象有什么關(guān)系呢?其實(shí)createEmbeddedView這個(gè)方法不止一個(gè)參數(shù),其第二個(gè)參數(shù)就是給我們的模板設(shè)置上下文對(duì)象的枚赡。API的詳情介紹請(qǐng)看createEmbeddedView這個(gè)API的詳情氓癌。

createEmbeddedView方法的第二個(gè)參數(shù)

就這樣。我們就可以將上下文對(duì)象塞入模板中了贫橙,這樣的話我們也可以直接使用let聲明變量的方法來(lái)使用這個(gè)上下文對(duì)象了贪婉。

自定義一個(gè)簡(jiǎn)單的類*ngFor指令——appRepeat

那么我們知道是如何設(shè)置的了,那么我們就來(lái)驗(yàn)證一下是否是對(duì)的卢肃。接下來(lái)谓松,我們仿照ngfor的功能,自己寫一個(gè)簡(jiǎn)單的渲染指令践剂。

首先我們定義一個(gè)指令:RepeatDirective。代碼如下:

@Directive({
  selector: '[appRepeat]',
})
export class RepeatDirective {
  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef,
  ) { }

  @Input() set appRepeatOf(heroesList: string[]) {
    heroesList.forEach((item, index, arr) => {
      this.viewContainer.createEmbeddedView(this.templateRef, {
        //當(dāng)前條目的默認(rèn)值
        $implicit: item,
        //可迭代對(duì)象的總數(shù)
        count: arr.length,
        //當(dāng)前條目的索引值
        index: index,
        //如果當(dāng)前條目在可迭代對(duì)象中的索引號(hào)為偶數(shù)則為 true娜膘。
        even: index % 2 === 0,
        //如果當(dāng)前條目在可迭代對(duì)象中的索引號(hào)為奇數(shù)則為 true逊脯。
        odd: index % 2 === 1,
      });
    });
  }
}

然后我們將其導(dǎo)入NgModule中,這個(gè)過(guò)程就省略不寫了竣贪。然后我們?cè)诮M件中使用一下這個(gè)指令:

@Component({
  selector: 'app-structural-likeNgFor-demo',
  template: `
    <h2>原神1.5版本卡池角色</h2>
    <h4>自定義ngFor(appRepeat)</h4>
    <ul>
      <li *appRepeat="let h of heroesList;let i = index;let even = even">
        索引:{{i}} -- {{h}} -- 索引值是否是偶數(shù):{{even.toString()}}
      </li>
    </ul>
    <h4>真正的ngFor</h4>
    <ul>
      <li *ngFor="let h of heroesList;let i = index;let even = even">
        索引:{{i}} -- {{h}} -- 索引值是否是偶數(shù):{{even.toString()}}
      </li>
    </ul>
  `,
})
export class StructuralLikeNgForDemoComponent {
  public heroesList: string[] = ['鐘離', '煙緋', '諾艾爾', '迪奧娜'];
}

在這里需要注意的是指令中的appRepeatOf不是亂寫的军洼。在微語(yǔ)法的解析過(guò)程中let h of heroesList中的of首先首字母會(huì)變成大寫的,變成Of演怎。然后在給它加上這個(gè)指令的前綴匕争,也就是appRepeat。組合起來(lái)就是appRepeatOf了爷耀。由它來(lái)接收一個(gè)可迭代的對(duì)象甘桑。

最后顯示的效果圖:

顯示結(jié)果

運(yùn)行結(jié)果的話和*ngFor沒(méi)有區(qū)別。但是功能肯定是欠缺的歹叮,如果有能力的小伙伴可以去閱讀*ngFor的源碼:*ngFor的源碼跑杭。

總結(jié)

通過(guò)這篇文章,我們知道了let-變量這個(gè)模板輸入變量是通過(guò)模板的上下文對(duì)象中定義并獲取值的咆耿。然后想要設(shè)置上下文對(duì)象的話需要通過(guò)createEmbeddedView方法的第二個(gè)參數(shù)來(lái)設(shè)置德谅。

結(jié)語(yǔ)

但是我總覺得了解的還不夠透徹,我總覺得設(shè)置模板的上下文對(duì)象可能不只是createEmbeddedView這一種方法萨螺,但是我并沒(méi)有找到其他的方法窄做。如果各位小伙伴有知道其他的方法可以留言告訴我。

參考資料:

這篇文章靈感來(lái)自:Angular 實(shí)現(xiàn)一個(gè)"repeat" 指令

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末慰技,一起剝皮案震驚了整個(gè)濱河市椭盏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吻商,老刑警劉巖庸汗,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異手报,居然都是意外死亡蚯舱,警方通過(guò)查閱死者的電腦和手機(jī)改化,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)枉昏,“玉大人陈肛,你說(shuō)我怎么就攤上這事⌒至眩” “怎么了句旱?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)晰奖。 經(jīng)常有香客問(wèn)我谈撒,道長(zhǎng),這世上最難降的妖魔是什么匾南? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任啃匿,我火速辦了婚禮,結(jié)果婚禮上蛆楞,老公的妹妹穿的比我還像新娘溯乒。我一直安慰自己,他們只是感情好豹爹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布裆悄。 她就那樣靜靜地躺著,像睡著了一般臂聋。 火紅的嫁衣襯著肌膚如雪光稼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天孩等,我揣著相機(jī)與錄音钟哥,去河邊找鬼。 笑死瞎访,一個(gè)胖子當(dāng)著我的面吹牛腻贰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扒秸,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼播演,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了伴奥?” 一聲冷哼從身側(cè)響起写烤,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拾徙,沒(méi)想到半個(gè)月后洲炊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年暂衡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了询微。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狂巢,死狀恐怖撑毛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唧领,我是刑警寧澤藻雌,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站斩个,受9級(jí)特大地震影響胯杭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜受啥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一做个、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腔呜,春花似錦、人聲如沸再悼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)冲九。三九已至谤草,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間莺奸,已是汗流浹背丑孩。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留灭贷,地道東北人温学。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像甚疟,于是被迫代替她去往敵國(guó)和親仗岖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容