本章节我们将为大家介绍如何将数据显示到用户界面上,可以使用以下三种方式:
通过插值表达式显示组件的属性
通过 NgFor 显示数组型属性
通过 NgIf 实现按条件显示
通过插值表达式显示组件的属性
要显示组件的属性,插值是最简单的方式,格式为:{{属性名}}。
以下代码基于 AngularJS2 TypeScript 环境配置 来创建,你可以再该章节上下载源码,并修改以下提到的几个文件。
app/app.component.ts 文件:
import{Component}from'@angular/core';@Component({selector: 'my-app', template: ` <h1>{{title}}</h1> <h2>我喜欢的网站: {{mySite}}</h2> `})exportclassAppComponent{title='站点列表'; mySite='菜鸟教程';}
Angular 会自动从组件中提取 title 和 mySite 属性的值,并显示在浏览器中,显示信息如下:
注意:模板是包在反引号 (`) 中的一个多行字符串,而不是单引号 (')。
使用 ngFor 显示数组属性
我们也可以循环输出多个站点,修改以下文件:
app/app.component.ts 文件:
import{Component}from'@angular/core';@Component({selector: 'my-app', template: ` <h1>{{title}}</h1> <h2>我喜欢的网站: {{mySite}}</h2> <p>网站列表:</p> <ul> <li *ngFor="let site of sites"> {{site}} </li> </ul> `})exportclassAppComponent{title='站点列表'; sites=['菜鸟教程', 'Google', 'Taobao', 'Facebook']; mySite=this.sites[0];}
代码中我们在模板使用 Angular 的 ngFor 指令来显示 sites 列表中的每一个条目,不要忘记 *ngFor 中的前导星号 (*) 。。
修改后,浏览器显示如下所示:
实例中 ngFor 循环了一个数组, 事实上 ngFor 可以迭代任何可迭代的对象。
接下来我们在 app 目录下创建 site.ts 的文件,代码如下:
app/site.ts 文件:
exportclassSite{constructor(publicid: number, publicname: string){}}
以上代码中定义了一个带有构造函数和两个属性: id 和 name 的类。
接着我们循环输出 Site 类的 name 属性:
app/app.component.ts 文件:
import{Component}from'@angular/core';import{Site}from'./site'; @Component({selector: 'my-app', template: ` <h1>{{title}}</h1> <h2>我喜欢的网站: {{mySite.name}}</h2> <p>网站列表:</p> <ul> <li *ngFor="let site of sites"> {{site.name}} </li> </ul> `})exportclassAppComponent{title='站点列表'; sites=[newSite(1, '菜鸟教程'), newSite(2, 'Google'), newSite(3, 'Taobao'), newSite(4, 'Facebook')]; mySite=this.sites[0];}
修改后,浏览器显示如下所示:
通过 NgIf 进行条件显示
我们可以使用 NgIf 来设置输出指定条件的数据。
以下实例中我们判断如果网站数 3 个以上,输出提示信息:修改以下 app.component.ts 文件,代码如下:
app/app.component.ts 文件:
import{Component}from'@angular/core';import{Site}from'./site'; @Component({selector: 'my-app', template: ` <h1>{{title}}</h1> <h2>我喜欢的网站: {{mySite.name}}</h2> <p>网站列表:</p> <ul> <li *ngFor="let site of sites"> {{site.name}} </li> </ul> <p *ngIf="sites.length > 3">你有很多个喜欢的网站!</p> `})exportclassAppComponent{title='站点列表'; sites=[newSite(1, '菜鸟教程'), newSite(2, 'Google'), newSite(3, 'Taobao'), newSite(4, 'Facebook')]; mySite=this.sites[0];}
修改后,浏览器显示如下所示,底部多了个提示信息:
用户点击链接、按下按钮或者输入文字时,这些用户的交互行为都会触发 DOM 事件。
本章中,我们将学习如何使用 Angular 事件绑定语法来绑定这些事件。
以下Gif图演示了该实例的操作:
源代码可以再文章末尾下载。
绑定到用户输入事件
我们可以使用 Angular 事件绑定机制来响应任何 DOM 事件 。
以下实例将绑定了点击事件:
<button (click)="onClickMe()">点我!</button>
等号左边的 (click) 表示把该按钮的点击事件作为绑定目标 。 等号右边,引号中的文本是一个 模板语句
完整代码如下:
app/click-me.component.ts 文件:
import{Component}from'@angular/core'; @Component({selector: 'click-me', template: ` <button(click)="onClickMe()">点我!</button> {{clickMessage}}`})exportclassClickMeComponent{clickMessage=''; onClickMe(){this.clickMessage='菜鸟教程!'; }}
通过 $event 对象取得用户输入
我们可以绑定到所有类型的事件。
让我们试试绑定到一个输入框的 keyup 事件,并且把用户输入的东西回显到屏幕上。
app/keyup.component.ts (v1) 文件:
@Component({selector: 'key-up1', template: ` <input(keyup)="onKey($event)"> <p>{{values}}</p> `})exportclassKeyUpComponent_v1{values=''; /* // 非强类型 onKey(event:any) { this.values +=event.target.value + ' | '; } */// 强类型onKey(event: KeyboardEvent){this.values +=(<HTMLInputElement>event.target).value + ' | '; }}
以上代码中我们监听了一个事件并捕获用户输入,Angular 把事件对象存入 $event 变量中。
组件的 onKey() 方法是用来从事件对象中提取出用户输入的,再将输入的值累加到 values 的属性。
从一个模板引用变量中获得用户输入
你可以通过使用局部模板变量来显示用户数据,模板引用变量通过在标识符前加上井号 (#) 来实现。
下面的实例演示如何使用局部模板变量:
app/loop-back.component.ts 文件:
@Component({selector: 'loop-back', template: ` <input #box(keyup)="0"> <p>{{box.value}}</p> `})exportclassLoopbackComponent{}
我们在
<input>
元素上定义了一个名叫box
的模板引用变量。box
变量引用的就是<input>
元素本身,这意味着我们可以获得 input 元素的value
值,并通过插值表达式把它显示在<p>
标签中。我们可以使用模板引用变量来修改以上 keyup 的实例:
app/keyup.components.ts (v2) 文件:
@Component({selector: 'key-up2', template: ` <input #box(keyup)="onKey(box.value)"> <p>{{values}}</p> `})exportclassKeyUpComponent_v2{values=''; onKey(value: string){this.values +=value + ' | '; }}
按键事件过滤 ( 通过 key.enter)
我们可以只在用户按下回车 (enter) 键的时候才获取输入框的值。
(keyup) 事件处理语句会听到每一次按键,我们可以过滤按键,比如每一个 $event.keyCode,只有在按下回车键才更新 values 属性。
Angular 可以为我们过滤键盘事件,通过绑定到 Angular 的 keyup.enter 伪事件监听回车键的事件。
app/keyup.components.ts (v3):
@Component({selector: 'key-up3', template: ` <input #box(keyup.enter)="values=box.value"> <p>{{values}}</p> `})exportclassKeyUpComponent_v3{values='';}
blur( 失去焦点 ) 事件
接下来我们可以使用blur( 失去焦点 ) 事件,它可以再元素失去焦点后更新 values 属性。
以下实例同时监听输入回车键与输入框失去焦点的事件。
app/keyup.components.ts (v4):
@Component({selector: 'key-up4', template: ` <input #box(keyup.enter)="values=box.value"(blur)="values=box.value"> <p>{{values}}</p> `})exportclassKeyUpComponent_v4{values='';}
本章节我们将为大家介绍如何使用组件和模板构建一个 Angular 表单。
利用 Angular 模板,我们可以创建各种类型表单,例如:登录表单,联系人表单,商品详情表单等,而且我们也为这些表单的字段添加数据校验。
接下来我们一步步来实现表单的功能。
创建项目
导入初始化项目。
完整的项目创建可以参考:AngularJS2 TypeScript 环境配置
创建 Site 模型
以下创建了一个简单的模型类 Site,包含了三个必需字段:id,name,url,一个可选字段:alexa。
在 angular-forms/app 目录下创建 site.ts 文件,代码如下:
app/site.ts 文件:
exportclassSite{constructor(publicid: number, publicname: string, publicurl: string, publicalexa?: number){}}
以下代码中,标为 public 的为公有字段,alexa 后添加一个问号(?)表示可选字段。
创建一个表单组件
每个 Angular 表单分为两部分:一个基于 HTML 的模板,和一个基于代码的组件,它用来处理数据和用户交互。
在 angular-forms/app 目录下创建 site-form.component.ts 文件,代码如下:
app/site-form.component.ts 文件:
import{Component}from'@angular/core';import{Site}from'./site'; @Component({moduleId: module.id, selector: 'site-form', templateUrl: 'site-form.component.html'})exportclassSiteFormComponent{urls=['www.runoob.com', 'www.google.com', 'www.taobao.com', 'www.facebook.com']; model=newSite(1, '菜鸟教程', this.urls[0], 10000); submitted=false; onSubmit(){this.submitted=true; }// TODO: 完成后移除getdiagnostic(){returnJSON.stringify(this.model); }}
实例中导入了 Component 装饰器和 Site 模型。
@Component 选择器 "site-form" 表示我们可以通过一个 <site-form> 标签,把此表单扔进父模板中。
templateUrl 属性指向一个独立的HTML模板文件,名叫 site-form.component.html。
diagnostic 属性用于返回这个模型的JSON形式。
定义应用的根模块
修改 app.module.ts 来定义应用的根模块,模块可中指定了引用到的外部及声明属于本模块中的组件,比如 SiteFormComponent。
因为模板驱动的表单有它们自己的模块,所以我们得把 FormsModule 添加到本应用的 imports 数组中,这样我们才能使用表单。
app/app.module.ts 文件代码如下
app/app.module.ts 文件:
import{NgModule}from'@angular/core';import{BrowserModule}from'@angular/platform-browser';import{FormsModule}from'@angular/forms';import{AppComponent}from'./app.component';import{SiteFormComponent}from'./site-form.component'; @NgModule({imports: [BrowserModule, FormsModule], declarations: [AppComponent, SiteFormComponent], bootstrap: [AppComponent]})exportclassAppModule{}
创建根组件
修改根组件文件 app.component.ts,将 SiteFormComponent 将被放在其中。
app/app.component.ts 文件:
import{Component}from'@angular/core'; @Component({selector: 'my-app', template: '<site-form></site-form>'})exportclassAppComponent{}
创建一个初始 HTML 表单模板
创建模板文件 site-form.component.html ,代码如下所示:
app/site-form.component.html 文件:
<divclass="container"><h1>网站表单</h1><form><divclass="form-group"><labelfor="name">网站名</label><inputtype="text"class="form-control"id="name"required></div><divclass="form-group"><labelfor="alterEgo">alexa 排名</label><inputtype="text"class="form-control"id="alexa"></div><buttontype="submit"class="btn btn-default">提交</button></form></div>
required 属性设置的该字段为必需字段,如果没有设置则是可选。
在 angular-forms 目录下输入以下命令:
cnpm install bootstrap --save
打开 index.html 文件,把以下样式链接添加到 <head> 中:
<linkrel="stylesheet"href="node_modules/bootstrap/dist/css/bootstrap.min.css">
执行 npm start 后,访问:http://localhost:3000/,输出效果如下:
使用 ngModel 进行双向数据绑定
接下来我们使用 ngModel 进行双向数据绑定,通过监听 DOM 事件,来实现更新组件的属性。
修改 app/site-form.component.html ,使用 ngModel 把我们的表单绑定到模型。代码如下所示:
app/site-form.component.html 文件:
<divclass="container"><h1>网站表单</h1><form> {{diagnostic}} <divclass="form-group"><labelfor="name">网站名</label><inputtype="text"class="form-control"id="name"required [(ngModel)]="model.name"name="name"></div><divclass="form-group"><labelfor="alexa">alexa 排名</label><inputtype="text"class="form-control"id="alexa" [(ngModel)]="model.alexa"name="alexa"></div><divclass="form-group"><labelfor="url">网站 URL </label><selectclass="form-control"id="url"required [(ngModel)]="model.url"name="url"><option *ngFor="let p of urls" [value]="p">{{p}}</option></select></div><buttontype="submit"class="btn btn-default">提交</button></form></div>
每一个 input 元素都有一个 id 属性,它被 label 元素的 for 属性用来把标签匹配到对应的 input 。
每一个 input 元素都有一个 name 属性, Angular 的表单模块需要使用它为表单注册控制器。
运行以上实例输出结果如下:
{{diagnostic}} 只是用于测试时候输出数据使用。
我们还可以通过 ngModel 跟踪修改状态与有效性验证,它使用了三个 CSS 类来更新控件,以便反映当前状态。
状态 | 为 true 时的类 | 为 false 时的类 |
---|---|---|
控件已经被访问过 | ng-touched | ng-untouched |
控件值已经变化 | ng-dirty | ng-pristine |
控件值是有效的 | ng-valid | ng-invalid |
这样我们就可以添加自定义 CSS 来反应表单的状态。
在 angular-forms 目录下创建 forms.css 文件,代码如下:
forms.css 文件:
.ng-valid[required], .ng-valid.required{border-left:5pxsolid#42A948; /* green */}.ng-invalid:not(form) {border-left:5pxsolid#a94442; /* red */}
打开 index.html 文件,把以下样式链接添加到 <head> 中:
<linkrel="stylesheet"href="forms.css">
修改 app/site-form.component.html ,代码如下所示:
app/site-form.component.html 文件:
<divclass="container"><h1>网站表单</h1><form> {{diagnostic}} <divclass="form-group"><labelfor="name">网站名</label><inputtype="text"class="form-control"id="name"required [(ngModel)]="model.name"name="name" #name="ngModel"><div [hidden]="name.valid || name.pristine"class="alert alert-danger"> 网站名是必需的 </div></div><divclass="form-group"><labelfor="alexa">alexa 排名</label><inputtype="text"class="form-control"id="alexa" [(ngModel)]="model.alexa"name="alexa"></div><divclass="form-group"><labelfor="url">网站 URL </label><selectclass="form-control"id="url"required [(ngModel)]="model.url"name="url"><option *ngFor="let p of urls" [value]="p">{{p}}</option></select></div><buttontype="submit"class="btn btn-default">提交</button></form></div>
模板中通过把 div 元素的 hidden 属性绑定到 name 控件的属性,我们就可以控制"name"字段错误信息的可见性了。
删除掉 name 字段的数据,显示结果如下所示:
添加一个网站
接下来我们创建一个用于添加网站的表单,在 app/site-form.component.html 添加一个按钮:
app/site-form.component.html 文件:
<buttontype="button"class="btn btn-default" (click)="newSite()">添加网站</button>
将以上按钮事件绑定到组件方法上:
app/site-form.component.ts 文件:
active=true; newSite(){this.model=newSite(5, '', ''); this.active=false; setTimeout(()=> this.active=true, 0);}
我们给组件添加一个 active 标记,把它初始化为 true 。当我们添加一个新的网站时,它把 active 标记设置为 false , 然后通过一个快速的 setTimeout 函数迅速把它设置回 true 。
通过 ngSubmit 来提交表单
我们可以使用 Angular 的指令 NgSubmit 来提交表单, 并且通过事件绑定机制把它绑定到 SiteFormComponent.submit() 方法上。
<form *ngIf="active" (ngSubmit)="onSubmit()" #siteForm="ngForm">
我们定义了一个模板引用变量 #siteForm ,并且把它初始化为 "ngForm" 。
这个 siteForm 变量现在引用的是 NgForm 指令,它代表的是表单的整体。
site-form.component.ts 文件完整代码如下:
app/site-form.component.ts 文件:
import{Component}from'@angular/core';import{Site}from'./site'; @Component({moduleId: module.id, selector: 'site-form', templateUrl: 'site-form.component.html'})exportclassSiteFormComponent{urls=['www.runoob.com', 'www.google.com', 'www.taobao.com', 'www.facebook.com']; model=newSite(1, '菜鸟教程', this.urls[0], 10000); submitted=false; onSubmit(){this.submitted=true; }// TODO: 完成后移除getdiagnostic(){returnJSON.stringify(this.model); }active=true; newSite(){this.model=newSite(5, '', ''); this.active=false; setTimeout(()=> this.active=true, 0); }}
app/site-form.component.html 完整代码如下:
app/site-form.component.html 文件:
<divclass="container"><div [hidden]="submitted"><h1>网站表单</h1><form *ngIf="active" (ngSubmit)="onSubmit()" #siteForm="ngForm"> {{diagnostic}} <divclass="form-group"><labelfor="name">网站名</label><inputtype="text"class="form-control"id="name"required [(ngModel)]="model.name"name="name" #name="ngModel"><div [hidden]="name.valid || name.pristine"class="alert alert-danger"> 网站名是必需的 </div></div><divclass="form-group"><labelfor="alexa">alexa 排名</label><inputtype="text"class="form-control"id="alexa" [(ngModel)]="model.alexa"name="alexa"></div><divclass="form-group"><labelfor="url">网站 URL </label><selectclass="form-control"id="url"required [(ngModel)]="model.url"name="url"><option *ngFor="let p of urls" [value]="p">{{p}}</option></select></div><buttontype="submit"class="btn btn-default" [disabled]="!siteForm.form.valid">提交</button><buttontype="button"class="btn btn-default" (click)="newSite()">新增网站</button></form></div><div [hidden]="!submitted"><h2>你提交的信息如下:</h2><divclass="row"><divclass="col-xs-3">网站名</div><divclass="col-xs-9 pull-left">{{ model.name }}</div></div><divclass="row"><divclass="col-xs-3">网站 alexa 排名</div><divclass="col-xs-9 pull-left">{{ model.alexa }}</div></div><divclass="row"><divclass="col-xs-3">网站 URL </div><divclass="col-xs-9 pull-left">{{ model.url }}</div></div><br><buttonclass="btn btn-default" (click)="submitted=false">编辑</button></div></div>
模板中我们把 hidden 属性绑定到 SiteFormComponent.submitted 属性上。
主表单从一开始就是可见的,因为 submitted 属性是 false ,当我们提交了这个表单则隐藏,submitted 属性是 true:
submitted=false; onSubmit(){this.submitted=true; }
最终的目录结构为:
前面几个章节我们已经接触了 Angular 的模板,本文我们将具体介绍 Angular 的语法使用。
模板扮演的是一个视图的角色,简单讲就是展示给用户看的部分。
HTML
插值表达式
模板表达式
模板语句
绑定语法
属性绑定
HTML 属性、 class 和 style 绑定
事件绑定
使用 NgModel 进行双向数据绑定
内置指令
* 与 <template>
模板引用变量
输入输出属性
模板表达式操作符
HTML
HTML 是 Angular 模板的"语言",除了 <script> 元素是被禁用的外 ,其他 HTML 元素都是支持的,例如:
<h1>我的第一个 Angular 应用</h1>
插值表达式
插值表达式的语法格式为:{{ ... }}。
插值表达式可以把计算的字符串插入HTML中,也可以作为属性值来使用。
<h3> {{title}} <imgsrc="{{imageUrl}}"style="height:30px"></h3>
模板表达式
{{ ... }} 里头其实就是一个模板表达式,Angular 会对其进行求值并转化为字符串输出。
以下实例是两个数相加:
<!-- "The sum of 1 + 1 is 2" --><p>The sum of 1 + 1 is {{1 + 1}}</p>
我们可以使用 getVal() 来获取这个表达式的值:
<divclass="example"><divclass="example_code">[mycode3 type="html"]<!-- "4" --><p>{{1 + 1 + getVal()}}</p>
模板表达式类似 JavaScript 的语言,很多 JavaScript 表达式也是合法的模板表达式,但不是全部。
以下 JavaScript 表达式是禁止的:
赋值表达式(
=
,+=
,-=
...)new操作符
带有
;
或者'
的连接表达式自增和自减操作(
++
和--
) 其他与Javascript语法不同的值得注意的包括:不支持位运算符(
|
和&
)模板表达式的操作符,如
|
和?.
等,被赋予了新的含义属性绑定
模板的属性绑定可以把视图元素的属性设置为模板表达式 。
最常用的属性绑定是把元素的属性设置为组件中属性的值。 下面这个例子中, image 元素的 src 属性会被绑定到组件的 imageUrl 属性上:
<img [src]="imageUrl">
当组件为 isUnchanged( 未改变 ) 时禁用一个按钮:
<button [disabled]="isUnchanged">按钮是禁用的</button>
设置指令的属性:
<div [ngClass]="classes">[ngClass]绑定到classes 属性</div>
设置一个自定义组件的属性(这是父子组件间通讯的重要途径):
<user-detail [user]="currentUser"></user-detail>
HTML 属性(Attribute)、 class 和 style 绑定
模板语法为那些不太适合使用属性绑定的场景提供了专门的单向数据绑定形式。
属性(Attribute)、绑定
当元素没有属性可绑的时候,使用HTML标签属性(Attribute)绑定。
考虑 ARIA, SVG 和 table 中的 colspan/rowspan 等属性(Attribute) 。它们是纯粹的属性 。 它们没有对应的属性可供绑定。
以下实例会报错:
<tr><tdcolspan="{{1 + 1}}">Three-Four</td></tr>
我们会得到这个错误:
Template parse errors:Can't bind to 'colspan' since it isn't a known native property模板解析错误:不能绑定到'colspan',因为它不是已知的原生属性
正如提示中所说,
<td>
元素没有colspan
属性。 但是插值表达式和属性绑定只能设置 属性 ,而不是 Attribute,所以需HTML标签 Attribute 绑定来创建和绑定类似的Attribute。HTML标签特性绑定在语法上类似于属性绑定,但中括号中的部分不是一个元素的属性名,而是由一个attr.的前缀和HTML标签属性的名称组成,然后通过一个能求值为字符串的表达式来设置HTML标签属性的值。如:
<tableborder=1><tr><td [attr.colspan]="1 + 1">One-Two</td></tr><tr><td>Five</td><td>Six</td></tr></table>
css类绑定
借助 CSS 类绑定 ,我们可以从元素的 class 属性上添加和移除 CSS 类名。
CSS 类绑定在语法上类似于属性绑定。但方括号中的部分不是一个元素的属性名,而是包括一个 class 前缀,紧跟着一个点 (.) ,再跟着 CSS 类的名字组成。 其中后两部分是可选的。例如: [class.class-name] 。
下面的例子展示了如何通过css类绑定类添加和移除"special"类:
<!-- 标准HTML样式类设置 --><divclass="bad curly special">Bad curly special</div><!-- 通过绑定重设或覆盖样式类 --><divclass="bad curly special" [class]="badCurly">Bad curly</div><!-- 通过一个属性值来添加或移除special样式类 --><div [class.special]="isSpecial">这个样式比较特殊</div>
style样式绑定
通过样式绑定,可以设置内联样式。样式绑定语法上类似于属性绑定,但中括号里面的部分不是一个元素的属性名,样式绑定包括一个style.,紧跟着css样式的属性名,例如:[style.style-property]。
<button [style.color]="isSpecial ? 'red': 'green'">红色</button><button [style.background-color]="canSave ? 'cyan': 'grey'">保存</button><!-- 带有单位的样式绑定 --><button [style.font-size.em]="isSpecial ? 3 : 1">大</button><button [style.font-size.%]="!isSpecial ? 150 : 50">小</button>
样式属性可以是中线命名法(font-size),也可以是驼峰是命名法(fontSize)。
事件绑定
在事件绑定中,Angular通过监听用户动作,比如键盘事件、鼠标事件、触屏事件等来响应相对应的数据流向-从视图目标到数据源。
事件绑定的语法是由等号左侧小括号内的 目标事件 和右侧引号中的 模板声明 组成。
比如下面这个例子,是事件绑定监听按钮的点击事件。只要点击鼠标,都会调用组件的 onSave()方法。
<button (click)="onSave()">保存</button>
圆括号中的名称 ——比如 (click) ——标记出了目标事件。在下面这个例子中,目标是按钮的 click 事件。
<button (click)="onSave()">Save</button>
也可以使用on- 前缀的形式:
<buttonon-click="onSave()">On Save</button>
使用 NgModel 进行双向数据绑定
当开发数据输入表单时,期望的结果是既能将组件的数据显示到表单上,也能在用户修改时更新组件的数据。
以下是一个通过 [(NgModel)] 来实现双向绑定:
<input [(ngModel)]="currentUser.firstName">
[]实现了数据流从组件到模板,()实现了数据流从模板到组件,两者一结合[()]就实现了双向绑定。
使用前缀形式的语法:
<inputbindon-ngModel="currentUser.firstName">
内置指令
Angular 的内置指令有 NgClass、NgStyle、NgIf、NgFor、NgSwitch等。
NgClass
通过绑定到 NgClass 动态添加或删除 CSS 类。
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'"> 这个div是大号的。</div>
NgStyle
NgStyle 通过把它绑定到一个key:value控制对象的形式,可以让我们同时设置很多内联样式。
setStyles() { let styles={ // CSS属性名 'font-style': this.canSave ? 'italic' : 'normal', // italic 'font-weight': !this.isUnchanged ? 'bold' : 'normal', // normal 'font-size': this.isSpecial ? '24px' : '8px', // 24px }; return styles;}
通过添加一个NgStyle属性绑定,让它调用setStyles,并据此来设置元素的样式:
<div [ngStyle]="setStyles()"> 这个div的样式是italic, normal weight, 和extra large (24px)。</div>
NgIf
通过把NgIf指令绑定到一个真值表达式,可以把一个元素及其子元素添加到DOM树上。
<div *ngIf="currentUser">Hello,{{currentUser.firstName}}</div>
相反,绑定到一个假值表达式将从DOM树中移除该元素及其子元素。如:
<!-- 因为isActive的值为false,所以User Detail不在DOM树种--><user-detail *ngIf="isActive"></user-detail>
NgSwitch
当需要从一组可能的元素树中根据条件显示其中一个时,就需要NgSwitch了。Angular将只把选中的元素添加进DOM中。如:
<span [ngSwitch]="userName"><span *ngSwitchCase="'张三'">张三</span><span *ngSwitchCase="'李四'">李四</span><span *ngSwitchCase="'王五'">王五</span><span *ngSwitchCase="'赵六'">赵六</span><span *ngSwitchDefault>龙大</span></span>
把作为父指令的
NgSwitch
绑定到一个能返回开关值的表达式,例子中这个值是字符串,但它可以是任何类型的值。父指令NgSwitch
控制一组<span>
子元素。每个<span>
或者挂在一个匹配值的表达式上,或者被标记为默认情况。任何时候,这些span中最多只有一个会出现在DOM中。如果这个span的匹配值和开关值相等,Angular2就把这个<span>
添加DOM中。如果没有任何span匹配上,Angular2就会把默认的span添加到DOM中。Angular2会移除并销毁所有其他的span。三个相互合作的指令:
ngSwitch:绑定到一个返回开关值的表达式
ngSwitchCase:绑定到一个返回匹配值的表达式
ngSwitchDefault:一个用于标记默认元素的属性 注意:不要再ngSwitch前使用
*
,而应该用属性绑定,但ngSwitchCase和ngSwitchDefault前面要放*
。NgFor
当需要展示一个由多个条目组成的列表时就需要这个指令了。如下面这个例子,就是在一个HTML块上应用NgFor。
<div *ngFor="let user of users">{{user.fullName}}</div>
NgFor也可以应用在一个组件元素上,如:
<user-detail *ngFor="let user of users" [user]="user"></user-detail>
ngFor指令支持一个可选的index索引,在迭代过程中会从0增长到数组中的长度。
可以通过模板输入变量来捕获这个index,并应用在模板中。下面的例子就把index捕获到了一个名为i的变量中。
<div *ngFor="let user of users; let i=index">{{i + 1}} - {{user.fullName}}</div>
NgForTrackBy
ngFor 指令有时候会性能较差,特别是在大型列表中。 对一个条目的一点小更改、移除或添加,都会导致级联的 DOM 操作。
比如,当通过重新从服务器来刷新通讯录,刷新后的列表可能包含很多(如果不是全部的话)以前显示过的联系人。但在Angular看来,它不知道哪些是以前就存在过的,只能清理旧列表、舍弃那些DOM元素,并用新的DOM元素来重建一个新列表。
解决这个问题,可以通过追踪函数来避免这种折腾。追踪函数会告诉Angular:我们知道两个具有相同user.id的对象是同一个联系人。如:
trackByUsers(index: number, user: User){return user.id}
然后,把NgForTrackBy指令设置为那个追踪函数:
<div *ngFor="let user of users; trackBy:trackByUsers">({{user.id}}) {{user.fullName}}</div>
追踪函数不会排除所有DOM更改。如果用来判断是否为同一个联系人的属性变化了,就会更新DOM元素,反之就会留下这个DOM元素。列表界面就会变得比较更加平滑,具有更好的响应效果。
模板引用变量
模板引用变量是模板中对 DOM 元素或指令的引用。
它能在原生 DOM 元素中使用,也能用于 Angular 组件——实际上,它可以和任何自定义 Web 组件协同工作。
我们可以在同一元素、兄弟元素或任何子元素中引用模板引用变量。
这里是关于创建和使用模板引用变量的两个例子:
<!-- phone 引用了 input 元素,并将 `value` 传递给事件句柄 --><input #phoneplaceholder="phone number"><button (click)="callPhone(phone.value)">Call</button><!-- fax 引用了 input 元素,并将 `value` 传递给事件句柄 --><inputref-faxplaceholder="fax number"><button (click)="callFax(fax.value)">Fax</button>
"phone" 的 (#) 前缀意味着我们将要定义一个 phone 变量。
相关文章
https://gf-rd.gitbooks.io/angular-2-step-by-step/content/chapters/2.3.html
定义好的组件需要注册才能被使用。 注册方式有两种
.component() 方法,让组件在当前 Vue 应用中全局可用。 在 main.ts 中
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import GlobalTitle from '@/components/GlobalTitle.vue'
import App from './App.vue'
import router from './router'
const app=createApp(App)
app.component('GlobalTitle', GlobalTitle)
app.use(createPinia())
app.use(router)
app.mount('#app')
在 vue 中直接使用无需导入
<script lang="ts" setup>
import { ref } from 'vue'
</script>
<template>
<div class="container">
<GlobalTitle></GlobalTitle>
</div>
</template>
<style lang="scss" scoped>
.container {
}
</style>
效果:
全局注册的组件可以在此应用的任意组件的模板中使用,所有的子组件也可以使用全局注册的组件。
全局组件缺点
局部注册的组件需要在使用它的父组件中显式导入,并且只能在该父组件中使用。
它的优点是使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好。
定义组件
<script lang="ts" setup>
import { ref } from 'vue'
</script>
<template>
<div class="container">
<h2>这是一个局部组件</h2>
</div>
</template>
<style lang="scss" scoped>
.container {
}
</style>
局部使用组件
<script lang="ts" setup>
import { ref } from 'vue'
import PartTitle from '@/components/PartTitle.vue'
</script>
<template>
<div class="container">
<PartTitle />
</div>
</template>
<style lang="scss" scoped>
.container {
}
</style>
推荐 PascalCase 作为组件名的注册格式。
在单文件组件和内联字符串模板中,我们都推荐这样做。
为了方便,Vue 支持将模板中使用 kebab-case 的标签解析为使用 PascalCase 注册的组件。
这意味着一个以 MyComponent 为名注册的组件,在模板中可以通过 <MyComponent> 或 <my-component> 引用。这让我们能够使用同样的 JavaScript 组件注册代码来配合不同来源的模板。
一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props,哪些是透传 attribute。
setup 写法
组件定义
<script lang="ts" setup>
import { ref } from 'vue'
const props=defineProps(['person'])
</script>
<template>
<div class="container">
<h1>{{ props.person }}</h1>
</div>
</template>
<style lang="scss" scoped>
.container {
}
</style>
组件使用
<script lang="ts" setup>
import { ref } from 'vue'
import Com14 from '@/components/Com14.vue'
</script>
<template>
<div class="container">
<Com14 person="'王大可'" />
</div>
</template>
<style lang="scss" scoped>
.container {
}
</style>
声明对象形式的属性 setup JavaScript 形式
defineProps({
title: String,
likes: Number
})
setup TypeScript 形式
<script setup lang="ts">
defineProps<{
title?: string
likes?: number
}>()
</script>
对象形式的 props 声明不仅可以一定程度上作为组件的文档,而且如果其他开发者在使用你的组件时传递了错误的类型,也会在浏览器控制台中抛出警告。
如果一个 prop 的名字很长,应使用 camelCase 形式,因为它们是合法的 JavaScript 标识符
defineProps({
greetingMessage: String
})
<span>{{ greetingMessage }}</span>
//推荐写法
<MyComponent greeting-message="hello" />
//或者
<MyComponent greetingMessage="hello" />
模版上使用属性则通常会将其写为 kebab-case 形式,这样和 HTML attribute 写法就一致了。
简单讲 就是传入数值是字符串还是变量
//这是静态
<Com14 person="王大可" />
//这是动态
<Com14 :person="personName" />
v-bind 直接绑定对象,即只使用 v-bind 而非某一个属性
const post={
id: 1,
title: 'My Journey with Vue'
}
<BlogPost v-bind="post" />
// 实际等价于
<BlogPost :id="post.id" :title="post.title" />
const props=defineProps(['foo'])
// ? 警告!prop 是只读的!
props.foo='bar'
「更改 props 的场景:」
prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。
在这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可
const props=defineProps(['initialCounter'])
// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter=ref(props.initialCounter)
需要对传入的 prop 值做进一步的转换。
在这种情况中,最好是基于该 prop 值定义一个计算属性:
const props=defineProps(['size'])
// 该 prop 变更时计算属性也会自动更新
const normalizedSize=computed(()=> props.size.trim().toLowerCase())
defineProps({
// 基础类型检查
// (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
propA: Number,
// 多种可能的类型
propB: [String, Number],
// 必传,且为 String 类型
propC: {
type: String,
required: true
},
// Number 类型的默认值
propD: {
type: Number,
default: 100
},
// 对象类型的默认值
propE: {
type: Object,
// 对象或数组的默认值
// 必须从一个工厂函数返回。
// 该函数接收组件所接收到的原始 prop 作为参数。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定义类型校验函数
// 在 3.4+ 中完整的 props 作为第二个参数传入
propF: {
validator(value, props) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 函数类型的默认值
propG: {
type: Function,
// 不像对象或数组的默认,这不是一个
// 工厂函数。这会是一个用来作为默认值的函数
default() {
return 'Default function'
}
}
})
defineProps() 宏中的参数不可以访问 <script setup> 中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中。
额外说明:
当 prop 的校验失败后,Vue 会抛出一个控制台警告 (在开发模式下)。
运行时类型检查
校验选项中的 type 可以是下列这些原生构造函数:
type 也可以是自定义的类或构造函数,Vue 将会通过 instanceof 来检查类型是否匹配。
class Person {
constructor(firstName, lastName) {
this.firstName=firstName
this.lastName=lastName
}
}
defineProps({
author: Person
})
Vue 会通过 instanceof Person 来校验 author prop 的值是否是 Person 类的一个实例。
defineProps({
disabled: Boolean
})
<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />
<!-- 等同于传入 :disabled="false" -->
<MyComponent />
只有当 Boolean 出现在 String 之前时,Boolean 转换规则才适用
一节讲到对于元素的操作和浏览器的常用操作如何通过代码实现,这次来学习如何通过定位元素,来获取元素的信息(元素属性、信息等)
获取元素相关的信息
size:元素的大小
text:元素内文本
is_displayed( ) :元素是否可见
is_enabled(): 元素是否可用(一般用于判断按钮是否置灰)
is_selected( ) : 元素是否被选中(一般用于表单中的单选框和复选框)
get_attribute ( ) : 元素的属性(可以获取到所选标签内的属性信息)
通过如图所示选中的元素来演示如何获取元素的属性
from selenium import webdriver
driver=webdriver.Chrome()
driver.get("http://news.baidu.com/")
# 新闻标题
element1=driver.find_element_by_css_selector("label[class='not-checked']")
# 新闻标题选择框
element2=driver.find_element_by_css_selector("#newstitle")
# 新闻标题的大小({'height', 'width'})
print(element1.size)
# 新闻标题的文本
print(element1.text)
# 新闻标题是否可见
print(element1.is_displayed())
# 新闻标题标签内的for属性
print(element1.get_attribute("for"))
# 新闻标题选择框是否被选中
print(element2.is_selected())
结果:
*请认真填写需求信息,我们会在24小时内与您取得联系。