저는 기사를 쓰거나 내 경험을 이야기하는 사람입니다. 공식 홈페이지에서 직접 복사하는 것은 정말 의미가 없습니다. 나는 아직도 모국어로 기사를 쓰는 것을 좋아합니다. 오늘 공식 홈페이지에서 템플릿 입력 변수에 대한 내용을 오랜 시간 읽어보고 드디어 그것이 무엇인지 예비적으로 이해하게 되었습니다. [관련 튜토리얼 추천 : "angular tutorial"]
제가 이걸 공부하고 싶은 이유는 예전에 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)
模板输入变量是这样一种变量,你可以在单个实例的模板中引用它的值。 这个例子中有好几个模板输入变量:
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:null, index:null, odd:null, }🎜이 글을 읽고 나서 저는 매우 혼란스러웠습니다. 이것이 let이 무엇인지, 왜
let-
에 변수가 뒤따르고 값이 있는 걸까요? 그러다가 공식 홈페이지에서 이 let
가 무엇인지 찾기 시작했습니다. 드디어 🎜주요 개념 - 명령어 - 구조적 명령어🎜의 🎜Microgrammar🎜섹션에서 let
에 대한 설명을 찾았습니다. 공식 웹사이트 설명: 마이크로 구문 🎜. 🎜🎜아래에 간단한 설명도 있습니다. 🎜🎜템플릿 입력 변수 🎜🎜템플릿 입력 변수는 단일 인스턴스에서 해당 값을 참조할 수 있는 변수입니다. em>템플릿입니다. 이 예에는🎜공식 홈페이지는 여전히 여전히 비언어적입니다. 단 몇 문장으로 사용법을 알려주지도 않고, 알려주지도 않습니다.hero
,i
및odd
등 여러 가지 템플릿 입력 변수가 있습니다. 그들은 모두let
를 주요 키워드로 사용합니다. 🎜🎜......🎜🎜let
키워드(예:let Hero
)를 사용하여 템플릿에서 템플릿 입력 변수를 선언합니다. . 이 변수의 범위는 반복되는 템플릿의 단일 인스턴스로 제한됩니다. 실제로 다른 구조 지시문에서도 동일한 변수 이름을 사용할 수 있습니다. 🎜🎜...🎜
let
으로 선언된 변수의 값은 어디에서 왔습니까? 더 많이 읽을수록 더 화가 났어요. 공식 홈페이지에 대해 알려주지 않으면 제가 직접 찾아보겠습니다. 🎜🎜대략적인 결론부터 시작해 보겠습니다. 🎜let으로 선언된 변수는 이 템플릿 템플릿🎜의 컨텍스트 개체에 있는 변수입니다. 그렇지 않으면 왜 🎜템플릿 입력 변수🎜라고 불리는 걸까요? *🎜ngFor Insider🎜 섹션에서 구조적 지시어가 실제로 <ng-template></ng-template>
에 호스트 요소를 래핑하는 내부 이야기를 배웠습니다. 템플릿에서 *ngFor
의 표현식은 각 let
템플릿 입력 변수와 이 지시어에 전달되어야 하는 값으로 구문 분석됩니다. 템플릿의 코드는 뷰로 직접 렌더링되지 않으므로 템플릿을 뷰로 전환할 수 있는 방법이 있어야 합니다. 우리의 구조적 지시문이 바로 그 일을 하며 템플릿을 뷰로 전환합니다. 🎜🎜*ngFor
의 공식 샘플 코드는 다음과 같습니다. 🎜@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, }); }); } }
*ngFor
지시문의 호스트 요소입니다. trackBy
이는 vue 및 React의 키와 유사합니다. 템플릿을 다시 렌더링할 때 성능 오버헤드를 줄이기 위해 템플릿에 식별자를 제공할 수 있습니다. 🎜이 설명을 읽고 나면 다음을 알 수 있습니다. 🎜angular는 이 템플릿에 대한 컨텍스트 개체🎜를 설정합니다. 하지만 이 프로세스는
let-i
및let-odd
변수는let i=index
및letodd=odd
로 정의됩니다. Angular는 이를 Context 객체의index
및odd
속성의 현재 값으로 설정합니다.let-hero
의 컨텍스트 속성은 여기에 지정되지 않습니다. 그 기원은 암시적입니다. Angular는let-hero
를 이 컨텍스트의$implicit
속성 값으로 설정합니다. 이 속성은 현재 반복에서 Hero를 사용하여NgFor
에 의해 초기화됩니다. .
ngFor
의 소스 코드 내부에 구현되어 있기 때문에 볼 수 없습니다. 그리고 이 🎜Context 개체🎜에는 index
및 odd
속성이 있고 $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-variable
let-变量
这个模板输入变量是通过模板的上下文对象中定义并获取值的。然后想要设置上下文对象的话需要通过createEmbeddedView
方法的第二个参数来设置。
但是我总觉得了解的还不够透彻,我总觉得设置模板的上下文对象可能不只是createEmbeddedView
템플릿 입력 변수
컨텍스트 개체이 기사는 다음에서 영감을 받았습니다: 🎜Angular는 "반복" 지침을 구현합니다.🎜🎜🎜재인쇄 주소: https://juejin.cn/post/6956466729891561503🎜🎜🎜더 많은 프로그래밍 관련 지식을 보려면 다음을 방문하세요. : 🎜프로그래밍 입문🎜! ! 🎜를 통해 값을 가져오는 것을 알 수 있습니다. 그런 다음 컨텍스트 개체를 설정하려면
하지만 템플릿을 설정하는createEmbeddedView
메서드의 두 번째 매개변수를 통해 설정해야 합니다.context
객체가 단지가 아닐 수도 있다는 생각이 항상 듭니다. createEmbeddedView
한 가지 방법이지만 다른 방법은 찾지 못했습니다. 다른 방법을 아시는 분은 메시지를 남겨 알려주세요.
위 내용은 Angular의 템플릿 입력 변수(let-variables)에 대해 이야기해 보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!