本篇文章帶大家來了解Angular中的範本輸入變數(let-變數)。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。
我這個人,寫文章或說心得,不喜歡直接抄官網上面的東西,實在沒啥意思。我還是喜歡用我的大白話來寫文章。今天這個關於模板輸入變數的這個我今天啃官網啃了許久,總算是初步的了解了是啥東西。
我想研究這玩意的起因在於之前我使用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
的說明。官網描述:微語法。 【相關推薦:《angular教學》】
在下面還有一段簡短的說明:
範本輸入變數(Template input variable)
模板輸入變數是這樣一種變量,你可以在單一實例的模板中引用它的值。這個範例中有好幾個模板輸入變數:
hero
、i
和odd
。它們都是用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-i
和let-odd
變數是透過let i=index
和let odd=odd
來定義的。 Angular 把它們設定為上下文物件中的index
和odd
屬性的目前值。- 這裡並沒有指定
let-hero
的上下文屬性。它的來源是隱式的。 Angular 將let-hero
設定為此上下文中$implicit
屬性的值, 它是由NgFor
用目前迭代中的英雄初始化的。
看完這段描述,我們可以得知:angular為這個範本設定了上下文物件。但是我們看不到這個過程,因為這是在ngFor
的源碼內部實現的。而這個上下文物件具備index
和odd
屬性,並且包含一個$implicit
(implicit:含蓄的;不直接言明的)的屬性。那我們推論這個上下文物件至少有以下幾個屬性:
{ $implicit:null, index:null, odd:null, }
那么我们声明let
变量的本质其实就是声明一个变量获取上下文对象中的同名属性的值。let-hero
不进行任何赋值的话,hero
默认等于$implicit
的值。无论是有多少个let-a
,let-b
,let-c
还是let-me
。声明的这个变量的值都等于$implicit
的值。而我们进行赋值过的,比如let-i = "index"
,i
的值就等于这个上下文对象中的index
属性对应的值。
当我们知道这个上下文对象是什么了,就该想这个上下文对象是怎么设置的了。
在结构性指令这一节当中,我们跟着官方示例做了一遍这个自定义结构性指令(如果还没有做的话,建议先跟着做一遍)。在UnlessDirective
这个指令中,其构造器constructor
声明了两个可注入的变量,分别是TemplateRef
和ViewContainerRef
。官网给的解释我觉得太过晦涩难懂,我这里给出一下我自己的理解:TemplateRef
代表的是宿主元素被包裹之后形成的模板的引用。而ViewContainerRef
代表的是一个视图容器的引用。那么问题来了,这个视图容器在哪儿呢?我们在constructor
构造器中打印一下ViewContainerRef
。打印结果如图:
然后我们点进去这个comment
元素。发现其就是一个注释元素。如图所示:
其实我也不是很确定这个视图容器到底是不是这个注释元素。但是毋庸置疑的是,视图容器和宿主元素是兄弟关系,紧挨着宿主元素。我们可以使用ViewContainerRef
中的createEmbeddedView()
方法(Embedded:嵌入式,内嵌式),将templateRef
模板引用传入进去,创建出来一个真实的视图。由于这个视图是被插入到视图容器ViewContainerRef
中了,所以又叫内嵌视图。那么这又和我们的上下文对象有什么关系呢?其实createEmbeddedView
这个方法不止一个参数,其第二个参数就是给我们的模板设置上下文对象的。API的详情介绍请看createEmbeddedView这个API的详情。
就这样。我们就可以将上下文对象塞入模板中了,这样的话我们也可以直接使用let声明变量的方法来使用这个上下文对象了。
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
了。由它来接收一个可迭代的对象。
最后显示的效果图:
运行结果的话和*ngFor
没有区别。但是功能肯定是欠缺的,如果有能力的小伙伴可以去阅读*ngFor
的源码:*ngFor的源码。
透過這篇文章,我們知道了let-變數
這個範本輸入變數是透過範本的上下文物件中定義並取得值的。然後想要設定上下文物件的話需要透過createEmbeddedView
方法的第二個參數來設定。
但是我總覺得了解的還不夠透徹,我總覺得設定範本的上下文物件可能不只是createEmbeddedView
這一種方法,但是我並沒有找到其他的方法。如果各位小夥伴有知道其他的方法可以留言告訴我。
參考資料:
這篇文章靈感來自:Angular 實作一個"repeat" 指令
更多程式相關知識,請訪問:編程視頻! !
以上是詳解Angular中的模板輸入變數(let-變數)的詳細內容。更多資訊請關注PHP中文網其他相關文章!