前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AngularDart 4.0 高级-结构指令 顶

AngularDart 4.0 高级-结构指令 顶

作者头像
南郭先生
发布2018-08-14 16:02:53
16.1K0
发布2018-08-14 16:02:53
举报
文章被收录于专栏:Google Dart

Angular拥有强大的模板引擎,可以让我们轻松操纵元素的DOM结构。

本指南介绍Angular如何用结构指令操纵DOM,以及如何编写自己的结构指令来完成相同的操作。

尝试一下实例(查看源代码)。

什么是结构指令?

结构指令负责HTML布局。 它们通常通过添加,移除或操纵元素来塑造或重塑DOM的结构。

与其他指令一样,您将结构指令应用于宿主元素。 然后该指令会执行它应该对该宿主元素及其后代所做的任何操作。

结构指令很容易识别。 在此示例中,星号(*)在指令属性名称前面。

代码语言:javascript
复制
<div *ngIf="hero != null" >{{hero.name}}</div>

没有方括号。 没有圆括号。 只要*ngIf设置为一个字符串。

您将在本指南中学习到星号(*)是一种便利的符号,字符串是一种微型语法,而不是通常的模板表达式。 Angular将这个符号解析成一个围绕宿主元素及其后代的标记<template>。 每个结构指令都与该模板有所不同。

三种常见的内置结构指令 - NgIfNgForNgSwitch ... - 在模板语法指南中进行了描述,并在整个Angular文档中的示例中进行了介绍。 以下是模板中的示例:

代码语言:javascript
复制
<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>

本指南不会重复如何使用它们。 但它确实解释了它们是如何工作的以及如何编写自己的结构指示。

指令拼写 在本指南中,您将看到UpperCamelCase和lowerCamelCase拼写的指令。 你已经看到了NgIf和ngIf。 有一个原因。 NgIf指向指令类; ngIf引用指令的属性(attribute)名称。 指令类拼写使用UpperCamelCase(NgIf)。 指令的属性名称拼写使用lowerCamelCase(ngIf)。 该指南在谈论其属性以及指令的功能时引用了指令类。 指南在描述如何将指令应用于HTML模板中的元素时引用了属性(attribute)名称。

还有其他两种Angular指令,在其他地方被广泛描述:(1)组件和(2)属性指令。 组件以本地HTML元素的方式管理HTML区域。 从技术上讲,这是一个模板指令。 属性指令改变元素,组件或其他指令的外观或行为。 例如,内置的NgStyle指令可以同时更改多个元素样式。 您可以将许多属性指令应用于一个宿主元素。 您只能将一个结构指令应用于宿主元素。

NgIf案例研究

NgIf是最简单的结构指令,也是最容易理解的。 它需要一个布尔表达式并使DOM的整个块出现或消失。

代码语言:javascript
复制
<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节点可以被垃圾收集并释放内存。

为什么要移除而不是隐藏?

指令可以通过将其显示样式设置为无隐藏不需要的段落。

代码语言:javascript
复制
<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的名字。

代码语言:javascript
复制
<div *ngIf="hero != null" >{{hero.name}}</div>

星号是“语法糖”,因为它有点复杂。 在内部,Angular分两个阶段。 首先,它将*ngIf =“...”转换为模板属性template =“ngIf ...”,就像这样。

代码语言:javascript
复制
<div template="ngIf hero != null">{{hero.name}}</div>

然后它将模板属性转换为一个模板元素,并包裹在宿主元素上,就像这样。

代码语言:javascript
复制
<template [ngIf]="hero != null">
  <div>{{hero.name}}</div>
</template>
  • *ngIf指令移至<template>元素,并在其中成为属性绑定[ngIf]。
  • 其余的<div>,包括它的class属性,移动到<template>元素中。

这些形式都没有实际呈现。 只有最终产品在DOM中结束。

Angular在实际渲染过程中消耗了<template>内容,并用诊断注释替换了<template>。

NgForNgSwitch ...指令遵循相同的模式。

*ngFor内部详解

Angular以类似的方式将*ngFor转换为从星号(*)语法通过模板属性到模板元素。

这里有一个全功能的NgFor应用程序,它有三种写法:

代码语言:javascript
复制
<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>上的属性:

  • let关键字声明了模板中引用的模板输入变量。这个例子中的输入变量是hero,i和odd。解析器将 let hero, let i, let odd 转换成变量名let-hero,let-i和let-odd。
  • 微观语法分析器提取of和trackby,将首字母大写(of -> Of, trackBy -> TrackBy),并在它们前面加上指令的属性名称(ngFor),产生名称ngForOf和ngForTrackBy。这些是两个NgFor输入属性的名称。 这就是指令如何得知列表是heroes,并且track-by功能是trackById。
  • 当NgFor指令遍历列表时,它会设置并重置其自己的上下文对象的属性。 这些属性包括index和odd以及一个名为$implicit的特殊属性。
  • let-i和let-odd变量被定义为let i = index并let odd = odd。 Angular将它们设置为上下文的index和odd 属性的当前值。
  • 没有指定let-hero的上下文属性。 它的原意是隐含的。Angular设置let-hero为上下文的$implicit属性的值,NgFor已经用当前迭代的hero初始化了它的值。
  • API指南描述了额外的NgFor指令属性和上下文属性。

当你编写自己的结构指令时,可以使用这些微观语法机制。 研究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级别。

NgSwitch指令内部详解

Angular NgSwitch实际上是一组协作指令:NgSwitch,NgSwitchCase和NgSwitchDefault。

这是一个例子。

代码语言:javascript
复制
<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可以被解析为模板属性表单。

代码语言:javascript
复制
<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>元素窗体中。

代码语言:javascript
复制
<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>。

template元素

HTML 5 <template>是用于呈现HTML的方案。 它从不直接显示。 事实上,在呈现视图之前,Angular用注释替换<template>及其内容。

如果没有结构指令,而只是将一些元素包装在<template>中,那些元素就会消失。比如短语”Hip! Hip! Hooray!”中间的“Hip”。

代码语言:javascript
复制
<p>Hip!</p>
<template>
  <p>Hip!</p>
</template>
<p>Hooray!</p>

Angular会擦掉中间的“Hip!”,让欢呼声不那么热烈。

结构指令使<template>起作用,就像您在编写自己的结构指令时看到的一样。

兄弟元素组

根元素通常能且应该成为结构指令的宿主,列表元素(<li>)是NgFor迭代的典型宿主元素。

代码语言:javascript
复制
<li *ngFor="let hero of heroes">{{hero.name}}</li>

如果没有宿主元素,通常可以将内容包装在本机HTML容器元素(如<div>)中,然后将该指令附加到该容器。

代码语言:javascript
复制
<div *ngIf="hero != null" >{{hero.name}}</div>

引入另一个容器元素(通常是<span>或<div>)将元素组归入单个根元素通常是无害的。 通常...但不总是。

分组元素可能会破坏模板外观,因为CSS样式既不期望也不适应新布局。例如,假设您有以下段落布局。

代码语言:javascript
复制
<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样式规则。

代码语言:javascript
复制
p span { color: red; font-size: 70%; }

构建的段落呈现奇怪。

打算在其他地方使用的p span样式无意中应用于此处。

另一个问题:一些HTML元素要求所有直系孩子属于特定类型。 例如,<select>元素需要<option>子元素。 您不能将选项封装在条件<div>或<span>中。

当你尝试这个时,

代码语言:javascript
复制
<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>。

代码语言:javascript
复制
<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>。

代码语言:javascript
复制
<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块中的花括号:

代码语言:javascript
复制
if (someCondition) {
  statement1;
  statement2;
  statement3;
}

如果没有这些大括号,Dart只会在您打算有条件地将其全部作为一个块执行时执行第一条语句。 <template>满足Angular模板中的类似需求。

编写一个结构指令

在本节中,您将编写一个与NgIf相反的UnlessDirective结构指令。 NgIf在条件为true时显示模板内容。 UnlessDirective在条件为false时显示内容。

代码语言:javascript
复制
<p *myUnless="condition">Show this sentence unless the condition is true.</p>

创建指令与创建组件类似。 以下是您可以开始的方式:

lib/src/unless_directive.dart (skeleton)

代码语言:javascript
复制
import 'package:angular/angular.dart';

@Directive(selector: '[myUnless]')
class UnlessDirective {
}

该指令的选择器通常是指令的方括号中的属性名称,[myUnless]。 括号定义了一个CSS属性选择器

指令属性名称应使用lowerCamelCase拼写,并以前缀开头。 不要使用ng。 该前缀属于Angular。 选择适合您或您公司的简短内容。 在这个例子中,前缀是my。

指令类名称以Directive结尾。 Angular自己的指令不会。

TemplateRef和ViewContainerRef

像这样一个简单的结构指令从Angular生成的<template>中创建一个嵌入式视图,并将该视图插入与指令的原始<p>宿主元素相邻的视图容器中。

您将通过TemplateRef获取<template>内容并通过ViewContainerRef访问视图容器。

你在指令构造函数中注入这两个类作为类的私有变量。

代码语言:javascript
复制
TemplateRef _templateRef;
ViewContainerRef _viewContainer;

UnlessDirective(this._templateRef, this._viewContainer);

myUnless属性

指令消费者期望将真/假条件绑定到[myUnless]。 这意味着该指令需要使用@Input注解的myUnless属性

模板语法指南中阅读@Input

代码语言:javascript
复制
@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)

代码语言:javascript
复制
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来尝试使用它。

代码语言:javascript
复制
<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

代码语言:javascript
复制
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

代码语言:javascript
复制
<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>&lt;template&gt; 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">&lt;template&gt;</h2>
<h4>*ngIf with a &lt;template&gt;</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>&lt;select&gt; with &lt;span&gt;</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>&lt;select&gt; with &lt;template&gt;</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>&lt;select&gt; with &lt;ng-container&gt;</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">&lt;div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd"&gt;</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">&lt;div template="ngFor let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd"&gt;</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">&lt;template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById"&gt;</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 &lt;template&gt;</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>&lt;template&gt;</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) &lt;p template="myUnless condition" class="code unless"&gt;
</p>
<template [myUnless]="condition">
  <p class="code unless">
    (A) &lt;template [myUnless]="condition"&gt;
  </p>
</template>

lib/app_component.css

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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;
    }
  }
}

你学到了

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是结构指令?
  • NgIf案例研究
    • 为什么要移除而不是隐藏?
    • 星号(*)前缀
    • *ngFor内部详解
      • 微语法
        • 模板输入变量
          • 每个宿主元素一个结构指令
          • NgSwitch指令内部详解
          • 首选星号(*)语法
          • template元素
          • 兄弟元素组
            • 模板来解决
            • 编写一个结构指令
              • TemplateRef和ViewContainerRef
                • myUnless属性
                • 概要
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档