Angular拥有强大的模板引擎,可让咱们轻松操纵元素的DOM结构。css
本指南介绍Angular如何用结构指令操纵DOM,以及如何编写本身的结构指令来完成相同的操做。html
结构指令负责HTML布局。 它们一般经过添加,移除或操纵元素来塑造或重塑DOM的结构。git
与其余指令同样,您将结构指令应用于宿主元素。 而后该指令会执行它应该对该宿主元素及其后代所作的任何操做。github
结构指令很容易识别。 在此示例中,星号(*)在指令属性名称前面。web
<div *ngIf="hero != null" >{{hero.name}}</div>
没有方括号。 没有圆括号。 只要*ngIf设置为一个字符串。express
您将在本指南中学习到星号(*)是一种便利的符号,字符串是一种微型语法,而不是一般的模板表达式。 Angular将这个符号解析成一个围绕宿主元素及其后代的标记<template>。 每一个结构指令都与该模板有所不一样。api
三种常见的内置结构指令 - NgIf,NgFor和NgSwitch ... - 在模板语法指南中进行了描述,并在整个Angular文档中的示例中进行了介绍。 如下是模板中的示例:浏览器
<div *ngIf="hero != null" >{{hero.name}}</div> <ul> <li *ngFor="let hero of heroes">{{hero.name}}</li> </ul> <div [ngSwitch]="hero?.emotion"> <happy-hero *ngSwitchCase="'happy'" [hero]="hero"></happy-hero> <sad-hero *ngSwitchCase="'sad'" [hero]="hero"></sad-hero> <confused-hero *ngSwitchCase="'confused'" [hero]="hero"></confused-hero> <unknown-hero *ngSwitchDefault [hero]="hero"></unknown-hero> </div>
本指南不会重复如何使用它们。 但它确实解释了它们是如何工做的以及如何编写本身的结构指示。app
指令拼写
在本指南中,您将看到UpperCamelCase和lowerCamelCase拼写的指令。 你已经看到了NgIf和ngIf。 有一个缘由。 NgIf指向指令类; ngIf引用指令的属性(attribute)名称。
指令类拼写使用UpperCamelCase(NgIf)。 指令的属性名称拼写使用lowerCamelCase(ngIf)。 该指南在谈论其属性以及指令的功能时引用了指令类。 指南在描述如何将指令应用于HTML模板中的元素时引用了属性(attribute)名称。
还有其余两种Angular指令,在其余地方被普遍描述:(1)组件和(2)属性指令。
组件以本地HTML元素的方式管理HTML区域。 从技术上讲,这是一个模板指令。
属性指令改变元素,组件或其余指令的外观或行为。 例如,内置的NgStyle指令能够同时更改多个元素样式。
您能够将许多属性指令应用于一个宿主元素。 您只能将一个结构指令应用于宿主元素。
NgIf是最简单的结构指令,也是最容易理解的。 它须要一个布尔表达式并使DOM的整个块出现或消失。
<p *ngIf="true"> Expression is true and ngIf is true. This paragraph is in the DOM. </p> <p *ngIf="false"> Expression is false and ngIf is false. This paragraph is not in the DOM. </p>
ngIf指令不会隐藏CSS元素。 它从DOM中物理添加和删除它们。 使用浏览器开发人员工具确认事实,以检查DOM。
顶部段落在DOM中。 底部,废弃的段落不是; 取而代之的是关于“模板绑定”的评论(稍后更多)。
当条件为false时,NgIf从DOM中删除它的宿主元素,将它从DOM事件(它所依附的)中分离出来,将组件从Angular变化检测中分离出来并销毁它。 组件和DOM节点能够被垃圾收集并释放内存。
指令能够经过将其显示样式设置为无隐藏不须要的段落。
<p [style.display]="'block'"> Expression sets display to "block". This paragraph is visible. </p> <p [style.display]="'none'"> Expression sets display to "none". This paragraph is hidden but still in the DOM. </p>
虽然不可见,但元素仍保留在DOM中。
对于一个简单的段落来讲,隐藏和删除之间的区别并不重要。 当宿主元素链接到资源密集型组件时,这很重要。 即便隐藏,这种组件的行为也会继续。 该组件保持链接到其DOM元素。 它一直在倾听事件。 Angular不断检查可能会影响数据绑定的更改。 不管组件在作什么,它都会继续这样作。
虽然看不见,但组件及其全部后代组件都会占用资源。 性能和记忆负担可能很大,响应性可能会下降,用户什么也看不到。
从积极的方面来讲,再次显示元素很快。 该组件的之前的状态被保存并准备显示。 该组件不会从新初始化 - 这种操做可能很昂贵。 因此隐藏和展现有时候是正确的。
可是若是没有一个使人信服的理由让他们保持身临其境,你应该首先去除用户看不到的DOM元素,并用像NgIf这样的结构指令来恢复未使用的资源。
这些相同的考虑适用于每一个结构指令,不管是内置仍是定制。 在应用结构指令以前,您可能想暂停一下,以考虑添加和删除元素以及建立和销毁组件的后果。
固然,你注意到了指令名称的星号(*)前缀,并想知道为何它是必要的以及它作了什么。
这里是*ngIf英雄存在,则显示hero的名字。
<div *ngIf="hero != null" >{{hero.name}}</div>
星号是“语法糖”,由于它有点复杂。 在内部,Angular分两个阶段。 首先,它将*ngIf =“...”转换为模板属性template =“ngIf ...”,就像这样。
<div template="ngIf hero != null">{{hero.name}}</div>
而后它将模板属性转换为一个模板元素,并包裹在宿主元素上,就像这样。
<template [ngIf]="hero != null"> <div>{{hero.name}}</div> </template>
这些形式都没有实际呈现。 只有最终产品在DOM中结束。
Angular在实际渲染过程当中消耗了<template>内容,并用诊断注释替换了<template>。
NgFor和NgSwitch ...指令遵循相同的模式。
Angular以相似的方式将*ngFor转换为从星号(*)语法经过模板属性到模板元素。
这里有一个全功能的NgFor应用程序,它有三种写法:
<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd"> ({{i}}) {{hero.name}} </div> <div template="ngFor let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd"> ({{i}}) {{hero.name}} </div> <template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"> <div [class.odd]="odd">({{i}}) {{hero.name}}</div> </template>
这显然比ngIf更复杂,正因如此。 NgFor指令具备比本指南中显示的NgIf更多的功能,包括必需的和可选的。 至少NgFor须要一个循环变量(let hero)和一个列表(heroes)。
您能够在分配给ngFor的字符串中启用这些功能,这是您在Angular的microsyntax中编写的。
ngFor字符串以外的全部内容仍在宿主元素(<div>)中且移动到<template>时保持不变。 在这个例子中,[ngClass] =“odd”保留在<div>上。
Angular microsyntax容许您以紧凑友好的字符串配置指令。 microsyntax解析器将该字符串转换为<template>上的属性:
当你编写本身的结构指令时,可使用这些微观语法机制。 研究NgIf和NgFor的源代码是了解更多信息的好方法。
模板输入变量是一个变量,其值能够在模板的单个实例中引用。 在这个例子中有几个这样的变量:hero,i和odd。 全部前面都有关键字let。
模板输入变量与模板引用变量不一样,语义和语法都不一样。
您使用let关键字(let hero)声明模板输入变量。 变量的做用域限于重复模板的单个实例。 您能够在其余结构指令的定义中再次使用相同的变量名称。
您经过在#(#var)前缀加上变量名称来声明一个模板引用变量。 引用变量是指其附加的元素,组件或指令。 它能够在整个模板中的任何地方访问。
模板输入和引用变量名称都有其本身的名称空间。 let hero中的hero变量永远不会和#hero中的hero同样。
有一天你会想重复一段HTML,但只有当特定条件成立时才会重复。 您将尝试将*ngFor和*ngIf放在同一宿主元素上。 Angular不会容许。 您仅能够将一个结构指令应用于宿主元素。
缘由是简单。 结构指令能够用宿主元素及其后代完成复杂的事情。 当两个指令声明相同的宿主元素时,哪个优先? NgIf或NgFor应该先走哪个? NgIf可否取消NgFor的效果? 若是是这样(而且看起来应该如此),Angular应该如何归纳取消其余结构指令的能力?
这些问题没有简单的答案。 禁止多项结构性指令使得它们没有实际意义。 这个用例有一个简单的解决方案:将*ngIf放在包裹*ngFor元素的容器元素上。 一个或两个元素能够是一个temple,因此你没必要引入额外的HTML级别。
Angular NgSwitch其实是一组协做指令:NgSwitch,NgSwitchCase和NgSwitchDefault。
这是一个例子。
<div [ngSwitch]="hero?.emotion"> <happy-hero *ngSwitchCase="'happy'" [hero]="hero"></happy-hero> <sad-hero *ngSwitchCase="'sad'" [hero]="hero"></sad-hero> <confused-hero *ngSwitchCase="'confused'" [hero]="hero"></confused-hero> <unknown-hero *ngSwitchDefault [hero]="hero"></unknown-hero> </div>
您可能会遇到旧代码中的NgSwitchWhen指令。 这是NgSwitchCase的弃用名称。
分配给NgSwitch(hero.emotion)的阀值肯定显示哪些阀(若是有)。
NgSwitch自己不是一个结构性指令。 它是一个属性指令,用于控制其余两个switch指令的行为。 这就是为何你写[ngSwitch],从不写成*ngSwitch。
NgSwitchCase和NgSwitchDefault是结构指令。使用星号(*)前缀表示法将它们附加到元素。当NgSwitchCase的值与switch的值匹配时,会显示它的宿主元素。当没有同级NgSwitchCase匹配switch的值时,NgSwitchDefault显示它的宿主元素。
您应用指令的元素是其宿主元素.<happy-hero>是*ngSwitchCase的宿主元素.<unknown-hero>是*ngSwitchDefault的宿主元素。
与其余结构指令同样,NgSwitchCase和NgSwitchDefault能够被解析为模板属性表单。
<div [ngSwitch]="hero?.emotion"> <happy-hero template="ngSwitchCase 'happy'" [hero]="hero"></happy-hero> <sad-hero template="ngSwitchCase 'sad'" [hero]="hero"></sad-hero> <confused-hero template="ngSwitchCase 'confused'" [hero]="hero"></confused-hero> <unknown-hero template="ngSwitchDefault" [hero]="hero"></unknown-hero> </div>
反过来,它能够被解析到<template>元素窗体中。
<div [ngSwitch]="hero?.emotion"> <template [ngSwitchCase]="'happy'"> <happy-hero [hero]="hero"></happy-hero> </template> <template [ngSwitchCase]="'sad'"> <sad-hero [hero]="hero"></sad-hero> </template> <template [ngSwitchCase]="'confused'"> <confused-hero [hero]="hero"></confused-hero> </template > <template ngSwitchDefault> <unknown-hero [hero]="hero"></unknown-hero> </template> </div>
星号(*)语法比其它脱糖形式更清晰。
虽然不多有理由在模板属性或元素形式中应用结构指令,但了解Angular建立<template>并了解它的工做原理仍然很重要。
当你编写本身的结构指令时,你会参考<template>。
HTML 5 <template>是用于呈现HTML的方案。 它从不直接显示。 事实上,在呈现视图以前,Angular用注释替换<template>及其内容。
若是没有结构指令,而只是将一些元素包装在<template>中,那些元素就会消失。好比短语”Hip! Hip! Hooray!”中间的“Hip”。
<p>Hip!</p> <template> <p>Hip!</p> </template> <p>Hooray!</p>
Angular会擦掉中间的“Hip!”,让欢呼声不那么热烈。
结构指令使<template>起做用,就像您在编写本身的结构指令时看到的同样。
根元素一般能且应该成为结构指令的宿主,列表元素(<li>)是NgFor迭代的典型宿主元素。
<li *ngFor="let hero of heroes">{{hero.name}}</li>
若是没有宿主元素,一般能够将内容包装在本机HTML容器元素(如<div>)中,而后将该指令附加到该容器。
<div *ngIf="hero != null" >{{hero.name}}</div>
引入另外一个容器元素(一般是<span>或<div>)将元素组纳入单个根元素一般是无害的。 一般...但不老是。
分组元素可能会破坏模板外观,由于CSS样式既不指望也不适应新布局。例如,假设您有如下段落布局。
<p> I turned the corner <span *ngIf="hero != null"> and saw {{hero.name}}. I waved </span> and continued on my way. </p>
您也有一个刚好适用于段落<p>内的<span>的CSS样式规则。
p span { color: red; font-size: 70%; }
构建的段落呈现奇怪。
打算在其余地方使用的p span样式无心中应用于此处。
另外一个问题:一些HTML元素要求全部直系孩子属于特定类型。 例如,<select>元素须要<option>子元素。 您不能将选项封装在条件<div>或<span>中。
当你尝试这个时,
<div> Pick your favorite hero (<label><input type="checkbox" checked (change)="showSad = !showSad">show sad</label>) </div> <select [(ngModel)]="hero"> <span *ngFor="let h of heroes"> <span *ngIf="showSad || h.emotion !== 'sad'"> <option [ngValue]="h">{{h.name}} ({{h.emotion}})</option> </span> </span> </select>
下拉列表是空的。
浏览器不会在<span>中显示<option>。
Angular <template>是一个分组元素,不会干扰样式或布局,由于Angular不会将其放入DOM中。
如下是条件段落,此次使用<template>。
<p> I turned the corner <template [ngIf]="hero != null"> and saw {{hero.name}}. I waved </template> and continued on my way. [template] </p>
它正确渲染。 注意使用NgIf的脱糖形式。
如今有条件地用<template>排除一个选项<option>。
<div> Pick your favorite hero 2 (<label><input type="checkbox" checked (change)="showSad = !showSad">show sad</label>) </div> <select [(ngModel)]="hero"> <template ngFor let-h [ngForOf]="heroes"> <template [ngIf]="showSad || h.emotion !== 'sad'"> <option [ngValue]="h">{{h.name}} ({{h.emotion}})</option> </template> </template> </select>
下拉菜单正常工做。
<template>是Angular解析器识别的语法元素。 它不是指令,组件,类或接口。 它更像是Dart if块中的花括号:
if (someCondition) { statement1; statement2; statement3; }
若是没有这些大括号,Dart只会在您打算有条件地将其所有做为一个块执行时执行第一条语句。 <template>知足Angular模板中的相似需求。
在本节中,您将编写一个与NgIf相反的UnlessDirective结构指令。 NgIf在条件为true时显示模板内容。 UnlessDirective在条件为false时显示内容。
<p *myUnless="condition">Show this sentence unless the condition is true.</p>
建立指令与建立组件相似。 如下是您能够开始的方式:
lib/src/unless_directive.dart (skeleton)
import 'package:angular/angular.dart'; @Directive(selector: '[myUnless]') class UnlessDirective { }
该指令的选择器一般是指令的方括号中的属性名称,[myUnless]。 括号定义了一个CSS属性选择器。
指令属性名称应使用lowerCamelCase拼写,并之前缀开头。 不要使用ng。 该前缀属于Angular。 选择适合您或您公司的简短内容。 在这个例子中,前缀是my。
指令类名称以Directive结尾。 Angular本身的指令不会。
像这样一个简单的结构指令从Angular生成的<template>中建立一个嵌入式视图,并将该视图插入与指令的原始<p>宿主元素相邻的视图容器中。
您将经过TemplateRef获取<template>内容并经过ViewContainerRef访问视图容器。
你在指令构造函数中注入这两个类做为类的私有变量。
TemplateRef _templateRef; ViewContainerRef _viewContainer; UnlessDirective(this._templateRef, this._viewContainer);
指令消费者指望将真/假条件绑定到[myUnless]。 这意味着该指令须要使用@Input注解的myUnless属性
@Input() set myUnless(bool condition) { if (!condition && !_hasView) { _viewContainer.createEmbeddedView(_templateRef); _hasView = true; } else if (condition && _hasView) { _viewContainer.clear(); _hasView = false; } }
只要条件的值发生变化,Angular就会设置myUnless属性。 因为myUnless属性确实有效,它须要一个setter。
没有人读取myUnless属性,所以它不须要getter。
完成的指令代码以下所示:
lib/src/unless_directive.dart (excerpt)
import 'package:angular/angular.dart'; @Directive(selector: '[myUnless]') class UnlessDirective { bool _hasView = false; TemplateRef _templateRef; ViewContainerRef _viewContainer; UnlessDirective(this._templateRef, this._viewContainer); @Input() set myUnless(bool condition) { if (!condition && !_hasView) { _viewContainer.createEmbeddedView(_templateRef); _hasView = true; } else if (condition && _hasView) { _viewContainer.clear(); _hasView = false; } } }
将此指令添加到AppComponent的directives列表中。
而后建立一些HTML来尝试使用它。
<p *myUnless="condition" class="unless a"> (A) This paragraph is displayed because the condition is false. </p> <p *myUnless="!condition" class="unless b"> (B) Although the condition is true, this paragraph is displayed because myUnless is set to false. </p>
当条件为假时,出现顶部(A)段落而且底部(B)段落消失。 条件为真时,顶部(A)段被删除,底部(B)段出现。
这是lib文件夹下的源代码。
lib/app_component.dart
import 'package:angular/angular.dart'; import 'package:angular_forms/angular_forms.dart'; import 'package:angular_components/angular_components.dart'; import 'src/hero.dart'; import 'src/unless_directive.dart'; import 'src/hero_switch_components.dart'; @Component( selector: 'my-app', templateUrl: 'app_component.html', styleUrls: const ['app_component.css'], directives: const [ CORE_DIRECTIVES, formDirectives, heroSwitchComponents, materialDirectives, UnlessDirective ], providers: const [materialProviders], ) class AppComponent { final List<Hero> heroes = mockHeroes; Hero hero; bool condition = false; final List<String> logs = []; bool showSad = true; String status = 'ready'; AppComponent() { hero = heroes[0]; } num trackById(num index, Hero hero) => hero.id; }
lib/app_component.html
<h1>Structural Directives</h1> <p>Conditional display of hero</p> <blockquote> <div *ngIf="hero != null" >{{hero.name}}</div> </blockquote> <p>List of heroes</p> <ul> <li *ngFor="let hero of heroes">{{hero.name}}</li> </ul> <hr> <h2 id="ngIf">NgIf</h2> <p *ngIf="true"> Expression is true and ngIf is true. This paragraph is in the DOM. </p> <p *ngIf="false"> Expression is false and ngIf is false. This paragraph is not in the DOM. </p> <p [style.display]="'block'"> Expression sets display to "block". This paragraph is visible. </p> <p [style.display]="'none'"> Expression sets display to "none". This paragraph is hidden but still in the DOM. </p> <h4>NgIf with template</h4> <p><template> element</p> <template [ngIf]="hero != null"> <div>{{hero.name}}</div> </template> <p>template attribute</p> <div template="ngIf hero != null">{{hero.name}}</div> <hr> <a id="ng-container"></a> <h2 id="template"><template></h2> <h4>*ngIf with a <template></h4> <button (click)="hero = hero != null ? null : heroes[0]">Toggle hero</button> <p> I turned the corner <template [ngIf]="hero != null"> and saw {{hero.name}}. I waved </template> and continued on my way. [template] </p> <!-- No ng-container yet: <p> I turned the corner <ng-container *ngIf="hero != null"> and saw {{hero.name}}. I waved </ng-container> and continued on my way. </p> --> <p> I turned the corner <span *ngIf="hero != null"> and saw {{hero.name}}. I waved </span> and continued on my way. </p> <p><i><select> with <span></i></p> <div> Pick your favorite hero (<label><input type="checkbox" checked (change)="showSad = !showSad">show sad</label>) </div> <select [(ngModel)]="hero"> <span *ngFor="let h of heroes"> <span *ngIf="showSad || h.emotion !== 'sad'"> <option [ngValue]="h">{{h.name}} ({{h.emotion}})</option> </span> </span> </select> <p><i><select> with <template></i></p> <div> Pick your favorite hero 2 (<label><input type="checkbox" checked (change)="showSad = !showSad">show sad</label>) </div> <select [(ngModel)]="hero"> <template ngFor let-h [ngForOf]="heroes"> <template [ngIf]="showSad || h.emotion !== 'sad'"> <option [ngValue]="h">{{h.name}} ({{h.emotion}})</option> </template> </template> </select> <!-- <p><i><select> with <ng-container></i></p> <div> Pick your favorite hero (<label><input type="checkbox" checked (change)="showSad = !showSad">show sad</label>) </div> <select [(ngModel)]="hero"> <ng-container *ngFor="let h of heroes"> <ng-container *ngIf="showSad || h.emotion !== 'sad'"> <option [ngValue]="h">{{h.name}} ({{h.emotion}})</option> </ng-container> </ng-container> </select> --> <br><br> <hr> <h2 id="ngFor">NgFor</h2> <div class="box"> <p class="code"><div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd"></p> <div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd"> ({{i}}) {{hero.name}} </div> <p class="code"><div template="ngFor let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd"></p> <div template="ngFor let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd"> ({{i}}) {{hero.name}} </div> <p class="code"><template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"></p> <template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"> <div [class.odd]="odd">({{i}}) {{hero.name}}</div> </template> </div> <hr> <h2 id="ngSwitch">NgSwitch</h2> <div>Pick your favorite hero</div> <material-radio-group [(ngModel)]="hero"> <material-radio *ngFor="let h of heroes" [value]="h"> {{h.name}} </material-radio> <material-radio>None of the above</material-radio> </material-radio-group> <h4>NgSwitch</h4> <div [ngSwitch]="hero?.emotion"> <happy-hero *ngSwitchCase="'happy'" [hero]="hero"></happy-hero> <sad-hero *ngSwitchCase="'sad'" [hero]="hero"></sad-hero> <confused-hero *ngSwitchCase="'confused'" [hero]="hero"></confused-hero> <unknown-hero *ngSwitchDefault [hero]="hero"></unknown-hero> </div> <h4>NgSwitch with <i>template</i> attribute</h4> <div [ngSwitch]="hero?.emotion"> <happy-hero template="ngSwitchCase 'happy'" [hero]="hero"></happy-hero> <sad-hero template="ngSwitchCase 'sad'" [hero]="hero"></sad-hero> <confused-hero template="ngSwitchCase 'confused'" [hero]="hero"></confused-hero> <unknown-hero template="ngSwitchDefault" [hero]="hero"></unknown-hero> </div> <h4>NgSwitch with <template></h4> <div [ngSwitch]="hero?.emotion"> <template [ngSwitchCase]="'happy'"> <happy-hero [hero]="hero"></happy-hero> </template> <template [ngSwitchCase]="'sad'"> <sad-hero [hero]="hero"></sad-hero> </template> <template [ngSwitchCase]="'confused'"> <confused-hero [hero]="hero"></confused-hero> </template > <template ngSwitchDefault> <unknown-hero [hero]="hero"></unknown-hero> </template> </div> <hr> <h2><template></h2> <p>Hip!</p> <template> <p>Hip!</p> </template> <p>Hooray!</p> <hr> <h2 id="myUnless">UnlessDirective</h2> <p> The condition is currently <span [ngClass]="{ a: !condition, b: condition, unless: true }">{{condition}}</span>. <button (click)="condition = !condition" [ngClass] = "{ a: condition, b: !condition }" > Toggle condition to {{condition ? 'false' : 'true'}} </button> </p> <p *myUnless="condition" class="unless a"> (A) This paragraph is displayed because the condition is false. </p> <p *myUnless="!condition" class="unless b"> (B) Although the condition is true, this paragraph is displayed because myUnless is set to false. </p> <h4>UnlessDirective with template</h4> <p *myUnless="condition">Show this sentence unless the condition is true.</p> <p template="myUnless condition" class="code unless"> (A) <p template="myUnless condition" class="code unless"> </p> <template [myUnless]="condition"> <p class="code unless"> (A) <template [myUnless]="condition"> </p> </template>
lib/app_component.css
button { min-width: 100px; font-size: 100%; } .box { border: 1px solid gray; max-width: 600px; padding: 4px; } .choices { font-style: italic; } code, .code { background-color: #eee; color: black; font-family: Courier, sans-serif; font-size: 85%; } div.code { width: 400px; } .heroic { font-size: 150%; font-weight: bold; } hr { margin: 40px 0 } .odd { background-color: palegoldenrod; } td, th { text-align: left; vertical-align: top; } p span { color: red; font-size: 70%; } .unless { border: 2px solid; padding: 6px; } p.unless { width: 500px; } button.a, span.a, .unless.a { color: red; border-color: gold; background-color: yellow; font-size: 100%; } button.b, span.b, .unless.b { color: black; border-color: green; background-color: lightgreen; font-size: 100%; }
lib/src/hero.dart
class Hero { final int id; String name; /*@nullable*/ String emotion; Hero(this.id, this.name, [this.emotion]); @override String toString() => '$name'; } final List<Hero> mockHeroes = <Hero>[ new Hero(1, 'Mr. Nice', 'happy'), new Hero(2, 'Narco', 'sad'), new Hero(3, 'Windstorm', 'confused'), new Hero(4, 'Magneta') ];
lib/src/hero_switch_components.dart
import 'package:angular/angular.dart'; import 'hero.dart'; @Component( selector: 'happy-hero', template: 'Wow. You like {{hero.name}}. What a happy hero ... just like you.', ) class HappyHeroComponent { @Input() Hero hero; } @Component( selector: 'sad-hero', template: 'You like {{hero.name}}? Such a sad hero. Are you sad too?', ) class SadHeroComponent { @Input() Hero hero; } @Component( selector: 'confused-hero', template: 'Are you as confused as {{hero.name}}?', ) class ConfusedHeroComponent { @Input() Hero hero; } @Component( selector: 'unknown-hero', template: '{{message}}', ) class UnknownHeroComponent { @Input() Hero hero; String get message => hero != null && hero.name.isNotEmpty ? '${hero.name} is strange and mysterious.' : 'Are you feeling indecisive?'; } const List heroSwitchComponents = const [ HappyHeroComponent, SadHeroComponent, ConfusedHeroComponent, UnknownHeroComponent ];
lib/src/unless_directive.dart
import 'package:angular/angular.dart'; /// Add the template content to the DOM unless the condition is true. /// /// If the expression assigned to `myUnless` evaluates to a truthy value /// then the templated elements are removed removed from the DOM, /// the templated elements are (re)inserted into the DOM. /// /// <div *ngUnless="errorCount" class="success"> /// Congrats! Everything is great! /// </div> /// /// ### Syntax /// /// - `<div *myUnless="condition">...</div>` /// - `<div template="myUnless condition">...</div>` /// - `<template [myUnless]="condition"><div>...</div></template>` @Directive(selector: '[myUnless]') class UnlessDirective { bool _hasView = false; TemplateRef _templateRef; ViewContainerRef _viewContainer; UnlessDirective(this._templateRef, this._viewContainer); @Input() set myUnless(bool condition) { if (!condition && !_hasView) { _viewContainer.createEmbeddedView(_templateRef); _hasView = true; } else if (condition && _hasView) { _viewContainer.clear(); _hasView = false; } } }
你学到了