站長資訊網
        最全最豐富的資訊網站

        聊聊Angular中的模板輸入變量(let-變量)

        聊聊Angular中的模板輸入變量(let-變量)

        我這個人,寫文章或者說心得,不喜歡直接抄官網上面的東西,實在是沒啥意思。我還是喜歡用我的大白話來寫文章。今天這個關于模板輸入變量的這個我今天啃官網啃了許久,總算是初步的了解了是啥東西。【相關教程推薦:《angular教程》】

        那么模板輸入變量到底是什么

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

        @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 {}

        看完這個之后我就很是疑惑,這個let是啥,為啥let-跟上一個變量之后就有值了呢?然后我就開始在官網中找這個let是什么東西。最終,我在主要概念-指令-結構性指令微語法一節找到了關于let的說明。官網描述:微語法。

        在下面還有一段簡短的說明:

        模板輸入變量(Template input variable)

        模板輸入變量是這樣一種變量,你可以在單個實例的模板中引用它的值。 這個例子中有好幾個模板輸入變量:heroiodd。 它們都是用 let 作為前導關鍵字。

        ……

        你使用 let 關鍵字(如 let hero)在模板中聲明一個模板輸入變量。 這個變量的范圍被限制在所重復模板的單一實例上。 事實上,你可以在其它結構型指令中使用同樣的變量名。

        ……

        官網還是一如既往的不說人話,短短幾句話就給你介紹完了,也不告訴你這玩意怎么用,也不告訴你let聲明的變量的值到底是從哪里來的。我越看越氣,得,官網你不說,我自己找。

        先說一個粗略的結論:let聲明的變量是這個template模板的上下文對象中的變量。不然為啥叫模板輸入變量呢。在*ngFor內幕這小節中,我們了解到了其內幕,結構性指令其實就是將宿主元素包裹在一個<ng-template></ng-template>中,然后在這個模板上將*ngFor中的表達式解析成一個個的let模板輸入變量和這個指令需要傳入的值。由于模板中代碼并不會直接被渲染成視圖,所以一定需要某種方法來使模板變成視圖。我們的結構性指令就是干這個事的,就是將我們的模板變成視圖。

        *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>
        • 在此提示一下,所謂的宿主元素就是指令所在的那個元素,像上面的例子中,div就是*ngFor指令的宿主元素。
        • trackBy這個類似于vue和react中的key。可以給模板一個標識,使其重新渲染的時候性能開銷降低一些。

        然后我們再看官網說的:

        • let-ilet-odd 變量是通過 let i=indexlet odd=odd 來定義的。 Angular 把它們設置為上下文對象中的 indexodd 屬性的當前值。
        • 這里并沒有指定 let-hero 的上下文屬性。它的來源是隱式的。 Angular 將 let-hero 設置為此上下文中 $implicit 屬性的值, 它是由 NgFor 用當前迭代中的英雄初始化的。

        看完這段描述,我們可以得知:angular為這個模板設置了上下文對象。但是我們看不到這個過程,因為這是在ngFor的源碼內部實現的。并且這個上下文對象具備indexodd屬性,并且包含一個$implicit(implicit:含蓄的;不直接言明的)的屬性。那么我們推斷這個上下文對象至少有以下幾個屬性:

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

        那么我們聲明let變量的本質其實就是聲明一個變量獲取上下文對象中的同名屬性的值let-hero不進行任何賦值的話,hero默認等于$implicit的值。無論是有多少個let-alet-blet-c還是let-me。聲明的這個變量的值都等于$implicit的值。而我們進行賦值過的,比如let-i = "index"i的值就等于這個上下文對象中的index屬性對應的值。

        上下文對象是如何設置的

        當我們知道這個上下文對象是什么了,就該想這個上下文對象是怎么設置的了

        結構性指令這一節當中,我們跟著官方示例做了一遍這個自定義結構性指令(如果還沒有做的話,建議先跟著做一遍)。在UnlessDirective這個指令中,其構造器constructor聲明了兩個可注入的變量,分別是TemplateRefViewContainerRef。官網給的解釋我覺得太過晦澀難懂,我這里給出一下我自己的理解:TemplateRef代表的是宿主元素被包裹之后形成的模板的引用。而ViewContainerRef代表的是一個視圖容器的引用。那么問題來了,這個視圖容器在哪兒呢?我們在constructor構造器中打印一下ViewContainerRef。打印結果如圖:

        聊聊Angular中的模板輸入變量(let-變量)

        然后我們點進去這個comment元素。發現其就是一個注釋元素。如圖所示:

        聊聊Angular中的模板輸入變量(let-變量)

        其實我也不是很確定這個視圖容器到底是不是這個注釋元素。但是毋庸置疑的是,視圖容器和宿主元素是兄弟關系,緊挨著宿主元素。我們可以使用ViewContainerRef中的createEmbeddedView() 方法(Embedded:嵌入式,內嵌式),將templateRef模板引用傳入進去,創建出來一個真實的視圖。由于這個視圖是被插入到視圖容器ViewContainerRef中了,所以又叫內嵌視圖。那么這又和我們的上下文對象有什么關系呢?其實createEmbeddedView這個方法不止一個參數,其第二個參數就是給我們的模板設置上下文對象的。API的詳情介紹請看createEmbeddedView這個API的詳情。

        聊聊Angular中的模板輸入變量(let-變量)

        就這樣。我們就可以將上下文對象塞入模板中了,這樣的話我們也可以直接使用let聲明變量的方法來使用這個上下文對象了。

        自定義一個簡單的類*ngFor指令——appRepeat

        那么我們知道是如何設置的了,那么我們就來驗證一下是否是對的。接下來,我們仿照ngfor的功能,自己寫一個簡單的渲染指令。

        首先我們定義一個指令: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, {         //當前條目的默認值         $implicit: item,         //可迭代對象的總數         count: arr.length,         //當前條目的索引值         index: index,         //如果當前條目在可迭代對象中的索引號為偶數則為 true。         even: index % 2 === 0,         //如果當前條目在可迭代對象中的索引號為奇數則為 true。         odd: index % 2 === 1,       });     });   } }

        然后我們將其導入NgModule中,這個過程就省略不寫了。然后我們在組件中使用一下這個指令:

        @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}} -- 索引值是否是偶數:{{even.toString()}}       </li>     </ul>     <h4>真正的ngFor</h4>     <ul>       <li *ngFor="let h of heroesList;let i = index;let even = even">         索引:{{i}} -- {{h}} -- 索引值是否是偶數:{{even.toString()}}       </li>     </ul>   `, }) export class StructuralLikeNgForDemoComponent {   public heroesList: string[] = ['鐘離', '煙緋', '諾艾爾', '迪奧娜']; }

        在這里需要注意的是指令中的appRepeatOf不是亂寫的。在微語法的解析過程中let h of heroesList中的of首先首字母會變成大寫的,變成Of。然后在給它加上這個指令的前綴,也就是appRepeat。組合起來就是appRepeatOf了。由它來接收一個可迭代的對象。

        最后顯示的效果圖:

        聊聊Angular中的模板輸入變量(let-變量)

        運行結果的話和*ngFor沒有區別。但是功能肯定是欠缺的,如果有能力的小伙伴可以去閱讀*ngFor的源碼:*ngFor的源碼。

        總結

        通過這篇文章,我們知道了let-變量這個模板輸入變量是通過模板的上下文對象中定義并獲取值的。然后想要設置上下文對象的話需要通過createEmbeddedView方法的第二個參數來設置。

        結語

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

        參考資料:

        這篇文章靈感來自:Angular 實現一個"repeat" 指令

        轉載地址:https://juejin.cn/post/6956466729891561503

        贊(0)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 久久99精品久久久久婷婷| 亚洲国产另类久久久精品黑人 | 亚洲精品专区| 亚洲av午夜国产精品无码中文字| 日本精品久久久久影院日本| 精品国产福利久久久| 一本一道精品欧美中文字幕| 国产精品二区观看| 久久亚洲国产午夜精品理论片| 国内精品久久久久久久97牛牛| 亚洲AV第一页国产精品| 在线观看91精品国产网站| 久久996热精品xxxx| 麻豆成人久久精品二区三区免费| 国产区精品福利在线观看精品| 久久国产精品成人免费| 999国内精品永久免费视频| 久久精品国产亚洲AV无码娇色| 亚洲AV成人精品一区二区三区| 亚洲国产午夜中文字幕精品黄网站| 久久精品国产亚洲av瑜伽| 国产亚洲精品a在线观看| 2023国产精品自拍| 久热这里只精品99re8久| 国产成人精品日本亚洲11| 丰满人妻熟妇乱又伦精品劲| 国产成人精品久久免费动漫| 精品国产乱码久久久久久1区2区| 四虎成人精品无码| 久久永久免费人妻精品下载| 久久精品国产亚洲av日韩| 激情亚洲一区国产精品| 国产人妖乱国产精品人妖| 国产精品高清一区二区三区| 国产女人精品视频国产灰线| 国产精品jizz视频| 99久久成人国产精品免费 | 国产精品无码午夜福利| 精品久久久久久久无码| 潮喷大喷水系列无码久久精品 | 老司机亚洲精品影院|