属性、元素操作以及指令
普通数据
新建 news 组件,首先在 news.component.ts 文件中定义变量:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-news',
templateUrl: './news.component.html',
styleUrls: ['./news.component.css']
})
export class NewsComponent implements OnInit {
title = 'Hello hresh';
public name: any = 'hresh';
content: any = '<h3>Hello Java</h3>';
msg = '中国,你好';
constructor() {
this.msg = '你好中国';
}
ngOnInit(): void {
}
}
在 html 文件中定义标签来获取定义的变量并显示:
<div>
<p>news works!</p>
<!--数据文本绑定-->
<h3>{{title}}</h3>
<hr />
<h1>{{content}}</h1>
<br>
<!--绑定 html-->
<span [innerHTML]="content"></span>
<br>
<h2>{{msg}}</h2>
<br>
1+1={{1+1}}
</div>
效果图如下:
图片展示
图片资源可以是本地资源,也可以从网上获取,在 html 文件中做如下配置:
<h3>引入图片</h3>
<img src="assets/images/10001.png" alt="Angular基础知识学习(一)" alt="hello" />
<hr>
<img [src]="picUrl" />
<img src="{{picUrl}}" />
本地静态资源存放位置如下:
网上图片资源链接可以在 argular01.component.ts 中定义:
public picUrl = 'https://cn.bing.com/th?id=OIP.bbd7bi181qua_NdZzguE3QHaE6&pid=Api&rs=1';
网页展示图如下:
模板引用变量
模板引用变量 通常是对模板中 DOM 元素的引用。它还可以引用指令(包含组件)、元素、TemplateRef 或 Web Component 。
使用井号(#)声明模板引用变量。以下模板引用变量 #phone
会在 input
元素上声明了一个 phone
变量。
<input #phone placeholder="phone number" />
<!-- lots of other elements -->
<!-- phone refers to the input element; pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button>
模板引用变量的范围是整个模板。因此,不要在同一模板中多次定义相同的变量名,因为它在运行时的值将不可预测。
替代语法
你也可以用 ref-
前缀代替 #
。 下面的例子中就用把 fax
变量声明成了 ref-fax
而不是 #fax
。
<input ref-fax placeholder="fax number" />
<button (click)="callFax(fax.value)">Fax</button>
NgFor
ngFor
指令迭代父组件的 items
属性所返回的 items
数组,并在每次迭代期间将 item
设置为该数组中的当前条目。 NgFor
指令上下文中的 index
属性在每次迭代中返回该条目的从零开始的索引。 您可以在模板输入变量中捕获 index
,并在模板中使用它。
同样在 news 组件中,首先定义数组内容:
nums: any[] = [111, 2222, 333];
public values: Array<string> = ['111', '222', '333'];
userList: any[] = [
{
name : 'hresh',
age : 22
},
{
name : 'hresh2',
age : 22
},
{
name : 'hresh3',
age : 22
}
]
cars: any[] = [
{
name: '宝马',
list: [
{
title: 'x1',
price: '30万'
},
{
title: 'x2',
price: '30万'
},
{
title: 'x3',
price: '30万'
}
]
},
{
name: '奔驰',
list: [
{
title: 'x1',
price: '30万'
},
{
title: 'x2',
price: '30万'
},
{
title: 'x3',
price: '30万'
}
]
}
]
在 html 文件中添加内容:
<ul>
<li *ngFor="let item of nums">
{{item}}
</li>
</ul>
<br>
<ul>
<li *ngFor="let item of userList">
{{item.name}}---{{item.age}}
</li>
</ul>
<br>
<ul>
<li *ngFor="let item of cars">
{{item.name}}
<ul>
<li *ngFor="let car of item.list">
{{car.title}}----{{car.price}}
</li>
</ul>
</li>
</ul>
效果如下:
带 trackBy
的 *ngFor
比如有这样一个例子:
import{ Component } from '@angular/core';
@Component({
selector: 'trackBy-test',
template: `
<ul><li *ngFor="let item of items;>{{item.name}}</li></ul>
<button (click)="getItems()">Get Items</button>
`
})
export class TrackByCmp{
items: any[]=[];
constructor(){
this.items = [{id:'1',name:'Tom'},{id:'2',name:'Jerry'},{id:'3',name:'Kitty'}];
}
getItems(){
this.items = [{id:'1',name:'Tom'},{id:'2',name:'Jerry'},{id:'4',name:'Mac'},{id:'5',name:'John'}];
}
}
有时你会需要改变这个集合,比如从后端接口返回了新的数据。那么问题来了,Angular 不知道怎么跟踪这个集合里面的项,不知道哪些该添加哪些该修改哪些该删除。结果就是,Angular 会把该集合里的项全部移除然后重新添加。就像这样:
这样做的弊端是会进行大量的 DOM 操作,而 DOM 操作是非常消耗性能的。
那么解决方案是,为*ngFor 添加一个 trackBy 函数,告诉 Angular 该怎么跟踪集合的各项。trackBy 函数需要两个参数,第一个是当前项的 index,第二个是当前项,并返回一个唯一的标识,就像这样:
import{ Component } from '@angular/core';
@Component({
selector: 'trackBy-test',
template: `
<ul><li *ngFor="let item of items;trackBy: trackByIndex">{{item.name}}</li></ul>
<button (click)="getItems()">Get Items</button>
`
})
export class TrackByCmp{
items: any[]=[];
constructor(){
this.items = [{id:'1',name:'Tom'},{id:'2',name:'Jerry'},{id:'3',name:'Kitty'}];
}
getItems(){
this.items = [{id:'1',name:'Tom'},{id:'2',name:'Jerry'},{id:'4',name:'Mac'},{id:'5',name:'John'}];
}
trackByIndex(index, item){
return index;
}
}
修改之后,Angular 就知道哪些项变动了:
关于 trackBy
的更多讲解可以参考:Angular-使用好NgForOf的trackBy带来性能上的提升
NgSwitch和NgIf
首先需要在 argular01.component.ts 中定义相关数据内容:
nums: any[] = [111, 222, 333];
flag = false;
order = 1;
html 文件内容如下:
<h3>循环,显示数据的索引</h3>
<div>
<ul>
<li *ngFor="let item of nums; let key =index">
<span *ngIf="key == 1" class="red">{{key+1}}----{{item}}</span>
<span *ngIf="key != 1">{{key+1}}----{{item}}</span>
</li>
</ul>
</div>
<br>
<h3>判断</h3>
<div *ngIf="flag">
<p>我是一个P标签</p>
</div>
<div *ngIf="!flag">
<p>我是一个PP标签</p>
</div>
<br>
<h3>NgSwitch</h3>
<span [ngSwitch]="order">
<p *ngSwitchCase="1">
1111111111
</p>
<p *ngSwitchCase="2">
2222222222222
</p>
<p *ngSwitchDefault>
00000000000
</p>
</span>
上述内容除了介绍 ngIf 和 ngSwitch 的用法,还提到关于循环索引的定义(索引从0开始),同 ngIf 配合使用。
网页效果图如下:
NgClass和NgStyle
用 ngClass
同时添加或删除几个 CSS 类。
<div>
<div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special.</div>
<div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>
<div [class]="isSpecial ? 'special2' : ''">This div is special</div>
</div>
考虑一个 setCurrentClasses()
组件方法,该方法设置一个组件属性 currentClasses
,该对象具有一个根据其他三个组件属性的 true
/ false
状态来添加或删除三个 CSS 类的对象。该对象的每个键(key)都是一个 CSS 类名。如果要添加上该类,则其值为 true
,反之则为 false
。
home2.component.ts
canSave = true;
isUnchanged = true;
isSpecial = true;
constructor() { }
ngOnInit(): void {
this.setCurrentClasses();
}
setCurrentClasses() {
this.currentClasses = {
'saveable': this.canSave,
'modified': !this.isUnchanged,
'special': this.isSpecial
};
}
CSS 样式:
.saveable{
background-color: blue;
}
.modified{
font-size: 21px;
}
.special{
font-weight: 200;
}
.special2{
font-weight: 200;
}
页面测试:
从上述例子可以看出,当添加单个类时,使用类绑定和 Ngclass 效果是一致的。所以官方文档推荐: 要添加或删除单个类,请使用类绑定而不是 NgClass
。
使用 NgStyle
根据组件的状态同时动态设置多个内联样式。
<div [ngStyle]="currentStyles">
This div is initially italic, normal weight, and extra large (24px).
</div>
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'">
This div is x-large or smaller.
</div>
下面的例子是一个 setCurrentStyles()
方法,它基于该组件另外三个属性的状态,用一个定义了三个样式的对象设置了 currentStyles
属性。
currentStyles: any = {};
canSave = true;
isUnchanged = true;
isSpecial = true;
constructor() { }
ngOnInit(): void {
this.setCurrentStyles();
}
setCurrentStyles() {
// CSS styles: set per current state of component properties
this.currentStyles = {
'font-style': this.canSave ? 'italic' : 'normal',
'font-weight': !this.isUnchanged ? 'bold' : 'normal',
'font-size': this.isSpecial ? '24px' : '12px'
};
}
页面测试:
同 ngClass 一样,官方文档同样推荐设置单个样式值采用样式绑定,设置多个内联样式,请使用 NgStyle
指令 。
管道
管道是格式化字符串、金额、日期和其它显示数据的好办法
Angular 自带了很多管道,比如 date 管道和 currency 管道,完整的列表参见 Pipes API 列表。你也可以自己定义一些新管道。
在 birthday.component.ts
文件中设置如下:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-birthday',
templateUrl: '
<p>The hero's birthday is {{ birthday | date:format }}</p>
<button (click)="toggleFormat()">Toggle Format</button>
'
})
export class BirthdayComponent implements OnInit {
birthday = new Date(1988, 3, 15); // April 15, 1988
toggle = true; // start with true == shortDate
constructor() { }
ngOnInit(): void {
}
get format() { return this.toggle ? 'shortDate' : 'fullDate'; }
toggleFormat() { this.toggle = !this.toggle; }
}
网页展示效果:
安全导航运算符( ?
)和空属性路径
Angular 安全导航运算符 ?
可以对在属性路径中出现 null
和 undefined
值进行保护。在这里,如果 item
为 null
,它可以防止视图渲染失败。
<p>The item name is: {{item?.name}}</p>
如果 item
为 null
,则视图仍然渲染,但显示的值为空白;您只会看到 “The item name is:” ,后面没有任何内容。
考虑接下来这个带有 nullItem
的例子。
The null item name is {{nullItem.name}}
由于没有安全导航运算符,并且 nullItem
为 null
,因此 JavaScript 和 Angular 会引发空指针错误并中断 Angular 的渲染过程:
content_copyTypeError: Cannot read property 'name' of null.
但是,有时在某些情况下,属性路径中的 null
值可能是可接受的,尤其是当该值开始时为空但数据最终会到达时。
使用安全导航运算符 ?
,当 Angular 表达式遇到第一个空值时,它将停止对表达式的求值,并渲染出无错误的视图。
绑定语法
数据绑定是一种机制,用来协调用户可见的内容,特别是应用数据的值。 虽然也可以手动从 HTML 中推送或拉取这些值,但是如果将这些任务转交给绑定框架,应用就会更易于编写、阅读和维护。 您只需声明数据源和目标 HTML 元素之间的绑定关系就可以了,框架会完成其余的工作。
Angular 提供了多种数据绑定方式。绑定类型可以分为三类,按数据流的方向分为:
- 从数据源到视图
- 从视图到数据源
- 双向:视图到数据源到视图
绑定类型与绑定目标
数据绑定的目标是 DOM 中的对象。 根据绑定类型,该目标可以是 Property 名(元素、组件或指令的)、事件名(元素、组件或指令的),有时是 Attribute 名。下表中总结了不同绑定类型的目标。 关于这一部分会结合例子进行演示,没有固定篇幅进行讲解,详细内容可以参考:Angular模块语法
事件
普通点击事件
首先修改 html 文件:
<h3>事件</h3>
<button (click)="run()">执行事件</button>
<br>
<br>
<button (click)="getData()">获取数据</button>
<br>
<br>
<strong>{{title}}</strong>
<br>
<br>
<button (click)="setData()">设置数据</button>
<br>
<br>
<button (click)="runEvent($event)" id="btn">执行方法获取事件对象</button>
<br>
然后在 argular01.component.ts 文件中添加实现方法
title = '这是一个主题';
keywords = '这是一个input'
run() {
alert('hello');
}
constructor() { }
ngOnInit(): void {
}
getData() {
alert(this.title);
}
setData() {
this.title = '新的主题';
}
runEvent(e) {
var dom = e.target;
dom.style.color = 'red';
}
网页展示效果如下:
表单事件
html 文件:
<h3>表单事件 事件对象</h3>
<!--<input type="text" (keydown)="keydown()">-->
<br>
<input type="text" (keydown)="keydown(event)">
<br>
<input type="text" (keyup)="keyup(event)" >
然后在 argular01.component.ts 文件中添加实现方法
keydown(e) {
console.log(e.target.value);
}
keyup(e) {
if (e.keyCode == 13) {
console.log('敲了一下回车');
}
}
网页展示效果如下:
补充语句与事件绑定的例子,语句上下文可以引用模板自身上下文中的属性,在上面例子中把模板的$event对象传给了组件中的事件处理方法,还可以将模板输入变量 (let key
)和模板引用变量 (#inputDom
) 传到组件方法中。
<input type="text" #inputDom (input)="getData2(inputDom.value)" />
<br>
<div>
<ul>
<li *ngFor="let item of nums; let key =index">
<span>{{key+1}}----{{item}}</span>
<button (click)="delete(key)">X</button>
</li>
</ul>
</div>
事件方法定义如下:
getData2(data: any) {
console.log(data);
}
delete(key) {
this.nums.splice(key, 1);
}
页面测试:
双向数据绑定
双向绑定会做两件事:
- 设置特定的元素属性。
- 监听元素的变更事件。
Angular 为此提供了一种特殊的双向数据绑定语法
[()]
。[()]
语法将属性绑定的括号[]
与事件绑定的括号()
组合在一起。
首先在 app.module.ts 里面引入 NgModule 并声明。
// 浏览器解析的模块
import { BrowserModule } from '@angular/platform-browser';
// Angular核心模块
import { NgModule } from '@angular/core';
import {FormsModule} from '@angular/forms';
// 根组件
......
// @NgModule装饰器,@NgModule接受一个元数据对象,告诉 Angular 如何编译和启动应用
@NgModule({
declarations: [// 配置当前项目运行的组件
AppComponent, NewsComponent, Argular01Component, FormComponent, SearchComponent
],
imports: [// 配置当前模块运行依赖的其他模块
BrowserModule,
FormsModule
],
providers: [StorageService],
bootstrap: [AppComponent]
})
export class AppModule { }
html 文件内容:
<h3>双向绑定</h3>
<p><input type="text" [(ngModel)]="keywords"></p>
<span>{{keywords}}</span>
<br>
<br>
<span><button (click)="upkeywords()">修改数据</button></span>
最后在 argular01.component.ts 定义变量和方法。
keywords = '这是一个input';
upkeywords() {
this.keywords = '改变后的数据';
}
网页展示效果:
关于 input 框双向数据绑定,在上面介绍的 (input)="getData2(inputDom.value)"
方法,修改一下也可以实现同样的效果。
$event 和事件处理语句
html 文件:
<p>input数据绑定:<input [value]="keywords"
(input)="keywords=$event.target.value" ></p>
<span>{{keywords}}</span>
上面的代码在把输入框的 value
属性绑定到 name
属性。 要监听对值的修改,代码绑定到输入框的 input
事件。 当用户造成更改时,input
事件被触发,并在包含了 DOM 事件对象 ($event
) 的上下文中执行这条语句。
要更新 name
属性,就要通过路径 $event.target.value
来获取更改后的值。
页面测试:
Form表单
新建一个组件:
ng g component components/form
首先在 app.module.ts 里面引入 NgModule 并声明。
1、form.component.html
<h2>人员登记系统</h2>
<!--讲解表单 input、checkbox、radio、select、textarea实现在线预约功能-->
<div class="people_list">
<ul>
<li>
姓 名:
<input type="text" [(ngModel)]="peopleInfo.username" id="username" class="form_input">
</li>
<li>
性 别:
<input type="radio" value="1" name="sex" id="man" [(ngModel)]="peopleInfo.sex"> <label for="man">男</label>
<input type="radio" value="2" name="sex" id="woman" [(ngModel)]="peopleInfo.sex"> <label for="woman">女</label>
</li>
<li>
城 市:
<select [(ngModel)]="peopleInfo.city">
<option *ngFor="let item of peopleInfo.cities">
{{item}}
</option>
</select>
</li>
<li>
爱 好:
<span *ngFor="let item of peopleInfo.hobbies;let key=index" >
<input type="checkbox" [id]="'check'+key" [(ngModel)]="item.checked"><label [for]="'check'+key">{{item.title}}</label>
</span>
</li>
<li>
备 注:
<textarea [(ngModel)]="peopleInfo.remark" cols="30" rows="2"></textarea>
</li>
<br>
<button (click)="getData()">获取表单的值</button>
<pre>
{{peopleInfo | json}}
</pre>
</ul>
</div>
2、form.component.css
ul,ol{
list-style-type: none;
}
*{
margin: 0px;
padding:0px;
}
h2{
text-align: center;
}
.people_list{
width: 400px;
margin: 40px auto;
padding: 20px;
border: 1px solid #eeeeee;
}
.people_list li{
height: 50px;
line-height: 50px;
}
.form_input{
width: 300px;
height: 28px;
}
3、form.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.css']
})
export class FormComponent implements OnInit {
peopleInfo: any = {
username: '',
sex: '1',
cities: ['北京', '上海', '深圳'],
city: '上海',
hobbies: [
{
title: '吃饭',
checked: false
},
{
title: '睡觉',
checked: false
},
{
title: '写代码',
checked: true
}
],
remark: ''
}
constructor() { }
ngOnInit(): void {
}
getData() {
console.log(this.peopleInfo);
}
}
4、网页展示效果
5、小结
对于性别单选和爱好多选框,勾选时前台发生了什么变化,双向数据绑定了什么样的数据。
上面动图中注意观察 ng-reflect-model 值的变化。
搜索
新建一个组件:
ng g component components/search
首先在 app.module.ts 里面引入 NgModule 并声明。
1、search.component.html
<h2>search</h2>
<div class="search">
<input type="text" [(ngModel)]="keyWord" (keyup)="keyup($event)"> <button (click)="search()">搜索</button>
<hr>
<ul>
<li *ngFor="let item of keyWordsOld;let key=index"> {{item}} ----- <button (click)="delete(key)">X</button></li>
</ul>
</div>
2、search.component.ts
keyWord: any = '';
keyWordsOld: any[] = [];
keyup(e) {
if (e.keyCode === 13){
if (this.keyWordsOld.indexOf(this.keyWord) === -1) {
this.keyWordsOld.push(this.keyWord);
}
this.keyWord = '';
}
}
search() {
if (this.keyWordsOld.indexOf(this.keyWord) == -1) {
this.keyWordsOld.push(this.keyWord);
}
this.keyWord = '';
}
delete(key) {
this.keyWordsOld.splice(key, 1);
}
3、网页展示效果
结合我们日常使用淘宝京东的习惯,搜索记录都会保留下来,这就涉及到数据持久化,后续会整合讲解。
TodoList(待办事项和已完成事项)
练习双向绑定来实现代办事项和已完成事项的转变。
1、search.component.html
<h2>搜 索todoList</h2>
<div class="search">
<input type="text" [(ngModel)]="product" (keyup)="add($event)" >
<hr>
待办事项
<ul>
<li *ngFor="let item of products;let key=index" [hidden]="item.status == 1">
<input type="checkbox" [(ngModel)]="item.status">{{item.status}} ---- {{item.title}}
----- <button (click)="deleteWay(key)">X</button>
</li>
</ul>
<hr>
已办事项
<ul>
<li *ngFor="let item of products;let key=index" [hidden]="item.status == 0">
<input type="checkbox" [(ngModel)]="item.status" >{{item.status}} ---- {{item.title}}
----- <button (click)="deleteWay(key)">X</button>
</li>
</ul>
<hr>
<div>
<pre>
{{products | json}}
</pre>
</div>
</div>
2、search.component.ts
product: any = '';
products: any[] = [];
add(e) {
// tslint:disable-next-line:triple-equals
if (e.keyCode == 13) {
if (!this.equalProduct(this.products, this.product)) {
this.products.push({
title: this.product,
status: 0
});
this.product = '';
} else {
alert('数据已存在');
this.product = '';
}
}
}
deleteWay(key) {
this.products.splice(key, 1);
}
equalProduct(products: any[], value: any) {
if (!value || value === '') {
return false;
}
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < products.length; i++) {
// tslint:disable-next-line:triple-equals
if (products[i].title == value) {
return true;
}
}
return false;
}
3、网页展示效果
本文作者为hresh,转载请注明。