三:Angular 模板 (Templates)
1 什么是模板
它是页面呈现的骨架,是盛装组件数据的容器。与之相关的内容包括了-----模板与组件件 '数据交互'、'内置指令'、'表单'、'管道'等。
1.1 模板语法概览
//插值:绑定属性变量的值到模板中
//插值:绑定属性变 <p>{{ detail.telNum }}</p>//DOM元素属性绑定:将模板表达式name的值绑定到元素<div>的属性title上
<div [title]="name"></div>//HTML标签特性绑定:将模板表达式的返回值绑定到元素<td>标签特性colspan上
<td [attr.colspan]="{{ 1+2 }}">合并单元格</td>//Class类绑定:当isBlue()函数值为true时为div添加类名为isBlue的样式
<div [class.isblue]="isBlue()"></div>//Style样式绑定:当表达式isRed的值为为true时设置button的文字颜色为红色,否则为绿色
<button [style.color]="isRed ? 'red':'green'">红色</button>//事件绑定:单击元素时会触发click事件,需要时也可以传递$event对象,如(click)="editContact($event)"
<a class='edit' (click)="editContact()"></a>//双向绑定:组件和模板间双向数据绑定,等价于<div [title]="name" (titleChange)="name=$event"></div>
<div [(title)]="name"></div>//模板局部变量:在当前模板中创建一个对id值为name的input元素的引用变量name,相当于document.getElementById("name")
<input type='text' ##name name="name" id="name"/>//管道操作符:原始数据birthday经管道转换后输出期望数据并显示在模板中
<p>张三的生日是{{ birthday | date }}</p>//模板表达式操作符:模板表达式操作符表明detail.telNum属性不是必须存在的,如果它的值是undefined,那么后面的表达式将会被忽略,不会引发异常
<p>{{ detail?.telNum }}</p>//星号前缀:使用星号前缀可以简化对结构指令的使用,Angular会将带有星号的指令引用替换成带有<template>标签的代码,
//等价于<template [myUnless]="boolValue"><p>myUnless is false now.</p></template>
<p *myUnless="boolValue">myUnless is false now.</p>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
2 数据绑定
根据数据流向可以分为三种:
- 单向:数据源到视图
//插值DOM元素属性 <p>{{ detail.telNum }}</P>//绑定HTML标签特性 <div [title]="name"></div>//绑定 <div [style.color]="color">hello world</div>
- 单向:从视图目标到数据源
//事件绑定 (click)="editContact()" on-click="editContact()"
- 双向
//双向绑定<div [(title)]="name"></div> <div bindon-title="name"></div>
- 1
- 2
- 3
NOTE
Property为DOM对象属性,以DOM元素作为多想,其附加内容,是在文档对象模型里定义的,如childNodes、firstChild。 而Attribute为HTML标签特性,是DOM节点自带的属性,在HTML中定义的,即只要是在HTML标签中出现的属性(HTML代码)都是Attribute。
数据绑定是借助于DOM对象属性和事件来运作的。
2.1 插值
双大括号{{ }}语法来实现。
2.2 模板表达式
类似于JS的表达式,绝大多数JS表达式均为合法模板表达式。它应用于插值语法双大括号中和属性绑定“=”右侧的括号中。但以下JS表达式不是合法模板表达式:
- 带有new运算符的表达式
- 赋值表达式
- 带有 ; 或者 , 的链式表达式
- 带有自增自减
模板表达式不支持位运算。
2.3 属性绑定
DOM元素属性绑定:把DOM对象属性绑定到组件的属性上,而绑定目标可以是中括号,也可以加前缀,还可以使用属性绑定设置自定义组件的输入属性。
//中括号 <div [title]="titleText"></div>//加前缀 <div bind-title="titleText"></div>//自定义组件的输入属性 <user-detail [user]="currentUser"></user-detail>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
NOTE:
中括号的作用是计算右侧模板表达式,如果没有中括号,右侧表达式会被当成字符串常量,如果是字符串常量则建议省略中括号,例如:
<user-detail detail="我是字符串常量" [user]="currentUser"></user-detail>
- 1
HTML标签特性绑定:纯粹的HTML标签特性比如<table>
的colspan
采用和DOM
一样的绑定方式会报错,例如:
//以下模板会出现解析错误 <table><tr><td colspan="{{ 1 + 2 }}"></td></tr> </table>//正确的HTML标签特性绑定 <table><tr><td [attr.colspan]="{{ 1 + 2 }}"></td></tr> </table>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
HTML标签特性绑定类似于属性绑定,但是中括号的部分不是一个元素的属性名,而是由attr.
前缀和HTML元素特性名称组成的形式。
CSS类绑定:CSS类既属于DOM对象属性,也属于HTML标签特性,所以可以使用以上两种方式绑定:
<div class='red font14' [class]="changeGreen">14号 绿色字</div>
- 1
特有的绑定方式:[class.class-name]语法,被赋值为true时,将class-name这个类添加到该绑定的标签上,否则移除这个类。
<div [class.class-blue]="isBlue()">若isBlue()返回true,这里的字体将变成蓝色</div><div class="footer" [class.footer]="showFooter">若showFooter为false,则footer这个css被移除</div>
- 1
- 2
- 3
Style样式绑定:HTML标签内联样式可以通过Style
样式绑定的方式设置。语法为[style.style-property],可以带单位如px和%:
<button [style.background-color]="canClick ? 'blue' : 'red'">若canClick为true,则按钮背景颜色为蓝色</button><button [style.font-size.px]="isLarge ? 18 : 13">若isLarge为true,则按钮字体变为18px</button>
- 1
- 2
- 3
2.4 事件绑定
单向,数据从模板到组件类流动。Angular监听用户操作时间,如键盘事件、鼠标事件、触屏事件等方法。事件绑定的语法为:“=”左侧小括号内的目标事件和“=”右侧引号中的模板语句组成。
模板语句与模板表达式一样,和JS表达式类似,有一些JS表达式在模板语句中不被支持:
- 赋值操作,如+=或-=
- 自增和自减操作符(++和–)
- new 操作符
- 位运算符 | 和 &
- 模板表达式运算符
模板语句和模板运算符一样,只能访问其上下文环境的成员,模板语句的上下文环境就是绑定事件对应组件的实例。也可以包含组件外的对象,如模板局部变量和事件绑定语句中的$event
目标事件:小括号中的事件名表示目标事件,还可以带on-前缀的形式来标记目标事件,还可以是自定义指令的事件:
<a class="edit" (click)="editContact()"></a><a class="edit" on-click="editContact()"></a><a class="edit" (myClick)="editContact=$event"></a>
- 1
- 2
- 3
- 4
- 5
$event事件对象:$event
事件对象用来获取事件的相关信息,如果目标事件是原生DOM元素事件(可以是自定义事件),则$event
将是一个包含了target
和target.value
属性的DOM时间对象,例如:
<input [value]="currentUser.firstName" (input)="currentUser.firstName=$event.target.value"/>
- 1
自定义事件:借助EventEmitter
实现。它的实现步骤:一、在组件中创建EventEmitter
实例对象,并将其以输出属性形式暴露;二、父组件通过绑定一个属性来自定义一个事件;三、在组件中调用EventEmitter.emit()
触发自定义事件;四、父组件绑定的事件通过$event
对象访问数据。
//父组件collection.component.ts import { Component } from '@angular/core';@Component({selector: 'collection',template: `<contact-collect [contact]="detail" (onCollect)="collectTheContact($event)"></contact-collect>` })export class CollectionComponent implements OnInit{detail: any = {};collectTheContact(){this.detail.collection == 0 ? this.detail.collection = 1 : this.detail.collection = 0;} }//子组件contactCollect.component.ts import { Component } from '@angular/core';@Component({selector: 'contact-collect',template: `<i [ngClass]="{collected: contact.collection}" (click)="collectTheContact()">收藏</i>` })export class CollectionComponent implements OnInit{@Input() contact: any = {};@Output() onCollect = new EventEmitter<boolean>();collectTheContact(){this.onCollect.emit();} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
子组件click
事件触发collectTheContact
方法,该方法调用EventEmitter
实例化对象onCollect()
的emit
方法,向父组件发送数据;父组件绑定了子组件的onCollect
事件,该事件被触发后将调用父元素的collectTheContact($event)
方法,并以$event
访问数据。
2.5 双向数据绑定
//最原始的实现方式 <input [value]="currentUser.firstName" (input)="currentUser.firstName=$event.target.value"/>//借助于NgModel,展开形式 <input [ngModel]="currentUser.phoneNumber" (ngModelChange)="currentUser.phoneNumver=$event"/>//最简洁的方式 <input [(ngModel)]="currentUser.phoneNumber"/>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
[ ]实现了数据流从组件类到模板,( )实现了数据流从模板到组件类。
2.6 输入输出属性
绑定声明中,“=”左侧的称为绑定目标,“=”右侧称为绑定源。
//list.component.html
<list-item [contact]="contact" (routerNavigate)="routerNavigate($event)"></list-item>
- 1
- 2
list-item
中,数据通过模板表达式流向目标属性contact
,因而contact
在ListComponent
中是一个输入属性。而事件绑定中,数据流向routerNavigate
绑定源,传递给接收者,routerNavigate
是一个输出属性。
绑定目标必须被明确地标记为输入或输出属性,可以以修饰符(@Input
和@Output
)或组件元数据(inputs
和outputs
)两种方式声明。
//goto是别名
@Output('goto') clicks = new EventEmitter<number>();//元数据方式
@Component({outputs: ['clicks:goto']
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
3 内置指令
NgClass:通过它,可以动态添加或移除多个类。NgClass
绑定一个由CSS
类名:value
的对象,value是一个布尔类型的数据值,当value
为true
时添加对应的类名到模板元素中,反之删除。
setClasses(){let classes={red: this.red,font14: !this.font14,title: this.isTitle}return classes }<div [ngClass]="setClass()"></div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
NgStyle:设置多个内联样式。绑定刑如CSS
属性名:value
的对象。
setStyles(){let styles = {'color': this.red ? 'red' : 'blue','font-size': !this.font14 ? '14px' : '16px','font-weight': this.isSpecial ? 'bold' : 'normal' };return styles; }<div [ngStyle]="setStyles"></div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
NgIf:绑定一个布尔类型的表达式,当表达式真时候,DOM树节点上添加一个元素及其子元素,否则移除(查看DOM树不能看到该元素)。
<div *ngIf="collect.length === 0"></div>
- 1
NgStitch:根据NgSwitch
绑定的模板表达式返回值决定添加那个模板元素到DOM节点上。
<span [ngSwitch]="contactName"><span *ngSwitchCase="'TimCook'">蒂姆·库克</span><span *ngSwitchCase="'BillGates'">比尔盖茨</span><span *ngSwitchDefault>无名氏</span> </span>
- 1
- 2
- 3
- 4
- 5
NgFor:重复执行某些步骤来展现数据,它支持一个可选的index索引,下标范围为0<=index<数组的长度。
<div *ngFor="let contact of contacts;let i=index">{{i + 1}} - {{ contact.id }}</div>
- 1
NgForTrackBy:每次更改都会引发很多相关联的DOM操作,使用NgFor
会让性能变得很差,比如重新从服务器拉取列表数据,虽然大部分数据没变化,但是因为不知道哪些数据变化了,需要清空并重新渲染。可以通过使用追踪函数避免重复渲染的性能浪费。
trackByContacts(index: number, contact: Contact){return contact.id; }<div *ngFor="let contact of contacts; trackBy: trackByContacts">{{ contact.id }}</div>
- 1
- 2
- 3
- 4
- 5
4 表单
HTML内置表单标签一些特性存在浏览器兼容性问题,在自定义规则、表单数据获取、处理、提交等流程都比较复杂。Angualr提供了双向数据绑定、强大的检验规则以及自定义检验错误提示等功能。Angular提供了模板驱动(使用模板表单内置指令、内置检验方式)和模型驱动(自定义)两种方式构建表单。
4.1 一个模板表单例子
@Component{selector: 'add-content',template: `<h3>添加联系人</h3><form><ul><li><label for="name">姓名:</label><input type='text' name='name'/></li><!--...--><li><button type='submit'>添加</button><button type='button'>取消</button></li></ul></form> ` }export class FormComponent {}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
4.2 表单指令
NgForm:表单控制中心,负责处理表单页面逻辑,扩展了额外表单特性,表单指令在NgForm
指令内部才能正常运行。
NgForm
的使用步骤如下:
- 在根模块导入
FormsModule
模块和FormComponent
组件 - 在
FormComponent
组件中直接使用NgForm
import { NgModule } from '@angular/core'; import { BrowserModule} from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppComponent} from './app.component'; import { FormComponent} from './form.component';@NgModule({imports:[BrowserModule,FormsModule],declarations:[AppComponent,FormComponent] })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
NgModel:NgModel
实现了表单控件的数据绑定,提供了控件状态跟踪和检验功能。
<input type='text' name='contactName' [(ngModel)]="curContact.name"/>
- 1
控件中使用NgModel
,必须添加name
属性,否则报错。原因是,NgForm
指令为表单建立一个控件对象FormControl的集合,以此来作为表单控件的容器。控件的NgModel
属性绑定会以name
作为唯一标识来注册并生成一个FormControl
,并将其加入到FormControl
的集合中。
单选框:NgModel
会绑定选中的单选框的值
<input type='radio' name="sex" [(ngModel)]="curContact.sex" value="female" />女
<input type='radio' name="sex" [(ngModel)]="curContact.sex" value="male" />男
- 1
- 2
复选框:NgModel
会绑定一个布尔值
<input type='checkbox' name="lock" [(ngModel)]="curContact.lock" />
- 1
单选下拉框:option
绑定目标有两种,value
和ngValue
,value
返回值类型为基本数据类型,ngValue
返回值为对象数据类型。
//第一步:定义下拉框列表所需的数据 export class FormComponent {interests:any[] = [{value: 'reading', display: '阅读'},{value: 'traveling', display: '旅游'},{value: 'sport', display: '运动'}] }//第二步:构建下拉框模板 <select name="interestValue" [(ngModel)]="curContact.interestValue"><option *ngFor="let interst of intersts" [value]="interest.value">{{interest.display}}</option> </select>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
多选下拉框: 与单选下拉框类似,不过返回值为选中数据的数组。
模板局部变量:模板中对DOM元素或指令(包括组件)的引用(作用类似于getElementById
),可以使用在当前元素、兄弟元素或任何子元素中。
DOM元素局部变量:局部变量名前加#符号或者加ref-前缀
<input type='text' #contactName name="contactName" id="contactName"/>
<input type='number' ref-telNum name="telNum" id="telNum"/>
- 1
- 2
表单指令局部变量:表单指令的局部变量在定义时需手动初始化为特定指令的代表值,最终解析后会被赋值为表单指令实例对象的引用。
- NgForm表单局部变量
<form #contactForm="ngForm">//...
</form>
- 1
- 2
- 3
局部变量#contactForm
为NgForm
指令实例对象的引用,可以在模板中读取NgForm
实例对象的属性值,如追踪表单的valid
属性状态。
- NgModel控件局部变量
<input type='text' name="contactName" [(ngModel)]="curContact.name" #contactName="ngModel"/>
<p>{{ contactName.valid }}</p>
- 1
- 2
局部变量contactName
是NgModel
指令实例对象的引用,可以通过它读取NgModel
的属性值。
表单状态:NgForm
和NgModel
指令都可以用于追踪表单状态来实现数据检验,他们都有五个表示状态的属性,属性值为布尔类型,可通过对应的局部变量来获取。NgForm
追踪的是整个表单控件的状态,NgModel
追踪单个控件。
valid
:表单值是否改变pristine
:表单值是否未改变dirty
:表单值是否已改变touched
:表单是否已被访问过untouched
:表单时是否未被访问过
表单状态检验有三个时段,初始状态、输入后状态(valid
、pristine
、dirty
状态改变)、失去焦点后状态(touched
和untouched
状态改变)。
NgModelGroup指令:对表单输入内容进行分组,方便在语义上区分不同类型的输入。
<fieldset ngModelGroup="nameGroup" #nameGroup="ngModelGroup"><label>姓:</label><input type='text' name="firstName" [(ngModel)]="curContact.firstName" required/><label>名:</label><input type='text' name="lastName" [(ngModel)]="curContact.lastName" required/> </fieldset> 1 2 3 4 5 6 //这是form中的数据格式 {nameGroup: {firstName: '',lastName: ''} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
ngSubmit事件:
<form #contactForm="ngForm" (ngSubmit)="doSubmit(contactForm.value)"><li><button type='submit' [disabled]="!contactForm.value">添加</button><button type='reset'>重置</button></li> </form> export class FormComponent{doSubmit(formValue: any){} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
4.3 自定义表单样式
.ng-valid[required] {border-left: 5px solid #0f0; } .ng-invalid {border-left: 5px solid #f00; }<p [hidden]="contactName.valid || contactName.pristine">用户名长度为3-10个字符</p>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
4.4 表单检验
表单内置检验:required
、minlength
、maxlength
、pattern
表单自定义检验
- 创建自定义检验
//validate-username.ts import { FormControl } from '@angular/forms';const EMAIL_REGEXP = new RegExp("[a-z0-9]+@[a-z0-9]+.com"); const TEL_REGEXP = new RegExp("1[0-9]{10}");export function validateUserName(c: FormControl) {return (EMAIL_REGEXP.test(c.value) || TEL_REGEXP.test(c.value)) ? null : {userName: {valid: false,errorMsg: '用户名必须是手机号或者邮箱账号'}}; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 使用自定义检验
//... import { ReactiveFormsModule } from '@angular/forms'; import { FormComponent } from './form.component'; import { AppComponent} from './app.component';@NgModule({imports: [BrowserModule, ReactiveFormsModule],declarations: [AppComponent, FormComponent],bootstrap: [AppComponent] })export class AppModule {}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
首先要导入ReactiveFormsModule
import { Component } from '@angular/core'; import { FormGroup, FormControl } from '@angular/forms'; import { validateUserName } from './validate-username';@Component({selector: 'add-contact',template: `<form [formGroup]="customForm"><label>姓名:</label><input type='text' formControlName='customName'/></form> ` })export class FormComponent{customForm = new FormGroup({customName: new FormControl('', validateUserName)}); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
分别定义了FormGroup
和FormControl
的实例化对象
5 管道
Angular中,管道可以按照开发者指定的规则将模板内的数据进行转换。
5.1 什么是管道
模板中,通过管道操作符 | 使用管道,| 左边的为输入数 据,右边为管道。管道可以带有参数,通过传入参数输出不同格式数据。同时,模板表达式中可以同时使用多个管道进行不同的处理。
<p>{{ birthday | date }}</p><P>{{ birthday | data:"MM/dd/y" }}</p><p>{{ expression | pipeName1 | pipeName2 }}</p>
- 1
- 2
- 3
- 4
- 5
5.2 几种管道
内置管道:Angular提供的,不需导入可以直接使用。
- DatePipe:日期管道,格式化日期,纯管道
- JsonPipe:将输入数据对象经过JSON.stringify()方法转换后输出对象字符串,非纯管道
- UpperCasePipe:文本中所有小写字母全转换为字母,纯
- LowerCasePipe:变成小写,纯
- DecimalPipe:将数值按特定格式显示文本,纯
- CurrencyPipe:数值转化为本地货币格式,纯
- PercentPipe:数值转百分比,纯
- SlicePipe:将数值或者字符串裁剪成新的子集,非纯管道
expression | date: formatexpression | jsonexpression | uppercaseexpression | lowercaseexpression | number[: digitInfo]expression | currency[: currencyCode[: symbolDisplay[: digitInfo]]]expression | percentexpression | slice: start[: end]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
自定义管道:通过以下几个步骤实现
- 定义元数据:引入Pipe和PipeTransform,同时为管道命名
//sexreform.pipe.ts import { Pipe, PipeTransform } from "@angular/core";@Pipe {name: 'sexReform' }export class SexReform implements PipeTransform {//... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 实现transform方法
export class SexReform implements PipeTransform {transform(val: string): string {switch(val) {case 'male': return '男';case 'female' return '女';default: return '未知性别';}} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 使用自定义管道
//使用管道前,需要在@NgModule的元数据declarations数组中添加自定义管道 import { SexReform } from 'pipes/sexreform.pipe';@NgModule ({//... declarations: [SexReform] })//可以像内置管道一般使用自定义管道咯 @Component ({selector: 'pipe-demo-custom',template: `<p>{{ sexValue | sexReform }}</p> ` })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
纯管道与非纯管道的区别:只有发生纯变才会调用该管道,这类管道称为纯管道,其余的管道成为非纯管道。纯变指的是对基本数据类型(String、Number、Boolean)输入值的变更或者对对象引用(Array、Function、Object)的更改。
只要数据发生改变,均会触发非纯管道,但不一定会触发纯管道,需要考察数据变化的情形是否为纯变化。看下面这个例子:
//... @Component ({selector: 'pure-pipe-demo',template: `<div><p>{{ dateObj | date: "y-MM-dd HH:mm:ss EEEE" }}</p><p>{{ dateStr | date: "y-MM-dd HH:mm:ss EEEE" }}</p></div> ` })export class PurePipeDemoComponent {dateObj: date = new Date('2016-06-08 20:05:08');dateStr: string = '2016-06-08 20:05:08';constructor(){setTimeout(() => {this.dateObj.setMonth(11),this.dateStr = '2016-12-08 20:05:08'},2000);} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
初始日期分别为:
'2016-06-08 20:05:08 Wednesday'
'2016-06-08 20:05:08 Wednesday'
- 1
- 2
2s之后变成了
'2016-06-08 20:05:08 Wednesday'
'2016-12-08 20:05:08 Thursday'
- 1
- 2
1 什么是模板
它是页面呈现的骨架,是盛装组件数据的容器。与之相关的内容包括了模板与组件件数据交互、内置指令、表单、管道等。
1.1 模板语法概览
//插值:绑定属性变量的值到模板中 <p>{{ detail.telNum }}</p>//DOM元素属性绑定:将模板表达式name的值绑定到元素<div>的属性title上 <div [title]="name"></div>//HTML标签特性绑定:将模板表达式的返回值绑定到元素<td>标签特性colspan上 <td [attr.colspan]="{{ 1+2 }}">合并单元格</td>//Class类绑定:当isBlue()函数值为true时为div添加类名为isBlue的样式 <div [class.isblue]="isBlue()"></div>//Style样式绑定:当表达式isRed的值为为true时设置button的文字颜色为红色,否则为绿色 <button [style.color]="isRed ? 'red':'green'">红色</button>//事件绑定:单击元素时会触发click事件,需要时也可以传递$event对象,如(click)="editContact($event)" <a class='edit' (click)="editContact()"></a>//双向绑定:组件和模板间双向数据绑定,等价于<div [title]="name" (titleChange)="name=$event"></div> <div [(title)]="name"></div>//模板局部变量:在当前模板中创建一个对id值为name的input元素的引用变量name,相当于document.getElementById("name") <input type='text' ##name name="name" id="name"/>//管道操作符:原始数据birthday经管道转换后输出期望数据并显示在模板中 <p>张三的生日是{{ birthday | date }}</p>//模板表达式操作符:模板表达式操作符表明detail.telNum属性不是必须存在的,如果它的值是undefined,那么后面的表达式将会被忽略,不会引发异常 <p>{{ detail?.telNum }}</p>//星号前缀:使用星号前缀可以简化对结构指令的使用,Angular会将带有星号的指令引用替换成带有<template>标签的代码,等价于<template [myUnless]="boolValue"><p>myUnless is false now.</p></template> <p *myUnless="boolValue">myUnless is false now.</p>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
2 数据绑定
根据数据流向可以分为三种:
- 单向:数据源到视图
//插值DOM元素属性 <p>{{ detail.telNum }}</P>//绑定HTML标签特性 <div [title]="name"></div>//绑定 <div [style.color]="color">hello world</div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 单向:从视图目标到数据源
//事件绑定
(click)="editContact()"
on-click="editContact()"
- 1
- 2
- 3
- 双向
//双向绑定
<div [(title)]="name"></div>
<div bindon-title="name"></div>
- 1
- 2
- 3
NOTE
Property为DOM对象属性,以DOM元素作为多想,其附加内容,是在文档对象模型里定义的,如childNodes、firstChild。 而Attribute为HTML标签特性,是DOM节点自带的属性,在HTML中定义的,即只要是在HTML标签中出现的属性(HTML代码)都是Attribute。
数据绑定是借助于DOM对象属性和事件来运作的。
2.1 插值
双大括号{{ }}语法来实现。
2.2 模板表达式
类似于JS的表达式,绝大多数JS表达式均为合法模板表达式。它应用于插值语法双大括号中和属性绑定“=”右侧的括号中。但以下JS表达式不是合法模板表达式:
- 带有new运算符的表达式
- 赋值表达式
- 带有 ; 或者 , 的链式表达式
- 带有自增自减
模板表达式不支持位运算。
2.3 属性绑定
DOM元素属性绑定:把DOM对象属性绑定到组件的属性上,而绑定目标可以是中括号,也可以加前缀,还可以使用属性绑定设置自定义组件的输入属性。
//中括号 <div [title]="titleText"></div>//加前缀 <div bind-title="titleText"></div>//自定义组件的输入属性 <user-detail [user]="currentUser"></user-detail>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
NOTE:
中括号的作用是计算右侧模板表达式,如果没有中括号,右侧表达式会被当成字符串常量,如果是字符串常量则建议省略中括号,例如:
<user-detail detail="我是字符串常量" [user]="currentUser"></user-detail>
- 1
HTML标签特性绑定:纯粹的HTML标签特性比如<table>
的colspan
采用和DOM
一样的绑定方式会报错,例如:
//以下模板会出现解析错误
<table><tr><td colspan="{{ 1 + 2 }}"></td></tr>
</table>//正确的HTML标签特性绑定
<table><tr><td [attr.colspan]="{{ 1 + 2 }}"></td></tr>
</table>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
HTML标签特性绑定类似于属性绑定,但是中括号的部分不是一个元素的属性名,而是由attr.
前缀和HTML元素特性名称组成的形式。
CSS类绑定:CSS类既属于DOM对象属性,也属于HTML标签特性,所以可以使用以上两种方式绑定:
<div class='red font14' [class]="changeGreen">14号 绿色字</div>
- 1
特有的绑定方式:[class.class-name]语法,被赋值为true时,将class-name这个类添加到该绑定的标签上,否则移除这个类。
<div [class.class-blue]="isBlue()">若isBlue()返回true,这里的字体将变成蓝色</div><div class="footer" [class.footer]="showFooter">若showFooter为false,则footer这个css被移除</div>
- 1
- 2
- 3
Style样式绑定:HTML标签内联样式可以通过Style
样式绑定的方式设置。语法为[style.style-property],可以带单位如px和%:
<button [style.background-color]="canClick ? 'blue' : 'red'">若canClick为true,则按钮背景颜色为蓝色</button><button [style.font-size.px]="isLarge ? 18 : 13">若isLarge为true,则按钮字体变为18px</button>
- 1
- 2
- 3
2.4 事件绑定
单向,数据从模板到组件类流动。Angular监听用户操作时间,如键盘事件、鼠标事件、触屏事件等方法。事件绑定的语法为:“=”左侧小括号内的目标事件和“=”右侧引号中的模板语句组成。
模板语句与模板表达式一样,和JS表达式类似,有一些JS表达式在模板语句中不被支持:
- 赋值操作,如+=或-=
- 自增和自减操作符(++和–)
- new 操作符
- 位运算符 | 和 &
- 模板表达式运算符
模板语句和模板运算符一样,只能访问其上下文环境的成员,模板语句的上下文环境就是绑定事件对应组件的实例。也可以包含组件外的对象,如模板局部变量和事件绑定语句中的$event
目标事件:小括号中的事件名表示目标事件,还可以带on-前缀的形式来标记目标事件,还可以是自定义指令的事件:
<a class="edit" (click)="editContact()"></a><a class="edit" on-click="editContact()"></a><a class="edit" (myClick)="editContact=$event"></a>
- 1
- 2
- 3
- 4
- 5
$event事件对象:$event
事件对象用来获取事件的相关信息,如果目标事件是原生DOM元素事件(可以是自定义事件),则$event
将是一个包含了target
和target.value
属性的DOM时间对象,例如:
<input [value]="currentUser.firstName" (input)="currentUser.firstName=$event.target.value"/>
- 1
自定义事件:借助EventEmitter
实现。它的实现步骤:一、在组件中创建EventEmitter
实例对象,并将其以输出属性形式暴露;二、父组件通过绑定一个属性来自定义一个事件;三、在组件中调用EventEmitter.emit()
触发自定义事件;四、父组件绑定的事件通过$event
对象访问数据。
//父组件collection.component.ts import { Component } from '@angular/core';@Component({selector: 'collection',template: `<contact-collect [contact]="detail" (onCollect)="collectTheContact($event)"></contact-collect> ` })export class CollectionComponent implements OnInit{detail: any = {};collectTheContact(){this.detail.collection == 0 ? this.detail.collection = 1 : this.detail.collection = 0;} }//子组件contactCollect.component.ts import { Component } from '@angular/core';@Component({selector: 'contact-collect',template: `<i [ngClass]="{collected: contact.collection}" (click)="collectTheContact()">收藏</i> ` })export class CollectionComponent implements OnInit{@Input() contact: any = {};@Output() onCollect = new EventEmitter<boolean>();collectTheContact(){this.onCollect.emit();} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
子组件click
事件触发collectTheContact
方法,该方法调用EventEmitter
实例化对象onCollect()
的emit
方法,向父组件发送数据;父组件绑定了子组件的onCollect
事件,该事件被触发后将调用父元素的collectTheContact($event)
方法,并以$event
访问数据。
2.5 双向数据绑定
//最原始的实现方式 <input [value]="currentUser.firstName" (input)="currentUser.firstName=$event.target.value"/>//借助于NgModel,展开形式 <input [ngModel]="currentUser.phoneNumber" (ngModelChange)="currentUser.phoneNumver=$event"/>//最简洁的方式 <input [(ngModel)]="currentUser.phoneNumber"/>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
[ ]实现了数据流从组件类到模板,( )实现了数据流从模板到组件类。
2.6 输入输出属性
绑定声明中,“=”左侧的称为绑定目标,“=”右侧称为绑定源。
//list.component.html
<list-item [contact]="contact" (routerNavigate)="routerNavigate($event)"></list-item>
- 1
- 2
list-item
中,数据通过模板表达式流向目标属性contact
,因而contact
在ListComponent
中是一个输入属性。而事件绑定中,数据流向routerNavigate
绑定源,传递给接收者,routerNavigate
是一个输出属性。
绑定目标必须被明确地标记为输入或输出属性,可以以修饰符(@Input
和@Output
)或组件元数据(inputs
和outputs
)两种方式声明。
//goto是别名 @Output('goto') clicks = new EventEmitter<number>();//元数据方式 @Component({outputs: ['clicks:goto'] })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
3 内置指令
NgClass:通过它,可以动态添加或移除多个类。NgClass
绑定一个由CSS
类名:value
的对象,value是一个布尔类型的数据值,当value
为true
时添加对应的类名到模板元素中,反之删除。
setClasses(){let classes={red: this.red,font14: !this.font14,title: this.isTitle}return classes }<div [ngClass]="setClass()"></div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
NgStyle:设置多个内联样式。绑定刑如CSS
属性名:value
的对象。
setStyles(){let styles = {'color': this.red ? 'red' : 'blue','font-size': !this.font14 ? '14px' : '16px','font-weight': this.isSpecial ? 'bold' : 'normal' };return styles; } <div [ngStyle]="setStyles"></div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
NgIf:绑定一个布尔类型的表达式,当表达式真时候,DOM树节点上添加一个元素及其子元素,否则移除(查看DOM树不能看到该元素)。
<div *ngIf="collect.length === 0"></div>
- 1
NgStitch:根据NgSwitch
绑定的模板表达式返回值决定添加那个模板元素到DOM节点上。
<span [ngSwitch]="contactName"><span *ngSwitchCase="'TimCook'">蒂姆·库克</span><span *ngSwitchCase="'BillGates'">比尔盖茨</span><span *ngSwitchDefault>无名氏</span>
</span>
- 1
- 2
- 3
- 4
- 5
NgFor:重复执行某些步骤来展现数据,它支持一个可选的index索引,下标范围为0<=index<数组的长度。
<div *ngFor="let contact of contacts;let i=index">{{i + 1}} - {{ contact.id }}</div>
- 1
NgForTrackBy:每次更改都会引发很多相关联的DOM操作,使用NgFor
会让性能变得很差,比如重新从服务器拉取列表数据,虽然大部分数据没变化,但是因为不知道哪些数据变化了,需要清空并重新渲染。可以通过使用追踪函数避免重复渲染的性能浪费。
trackByContacts(index: number, contact: Contact){
return contact.id;
}<div *ngFor="let contact of contacts; trackBy: trackByContacts">{{ contact.id }}</div>
- 1
- 2
- 3
- 4
- 5
4 表单
HTML内置表单标签一些特性存在浏览器兼容性问题,在自定义规则、表单数据获取、处理、提交等流程都比较复杂。Angualr提供了双向数据绑定、强大的检验规则以及自定义检验错误提示等功能。Angular提供了模板驱动(使用模板表单内置指令、内置检验方式)和模型驱动(自定义)两种方式构建表单。
4.1 一个模板表单例子
@Component{selector: 'add-content',template: `<h3>添加联系人</h3><form><ul><li><label for="name">姓名:</label><input type='text' name='name'/></li><!--...--><li><button type='submit'>添加</button><button type='button'>取消</button></li></ul></form> ` } export class FormComponent {}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
4.2 表单指令
NgForm:表单控制中心,负责处理表单页面逻辑,扩展了额外表单特性,表单指令在NgForm
指令内部才能正常运行。
NgForm
的使用步骤如下:
- 在根模块导入
FormsModule
模块和FormComponent
组件 - 在
FormComponent
组件中直接使用NgForm
import { NgModule } from '@angular/core'; import { BrowserModule} from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppComponent} from './app.component'; import { FormComponent} from './form.component';@NgModule({imports:[BrowserModule,FormsModule],declarations:[AppComponent,FormComponent] })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
NgModel:NgModel
实现了表单控件的数据绑定,提供了控件状态跟踪和检验功能。
<input type='text' name='contactName' [(ngModel)]="curContact.name"/>
- 1
控件中使用NgModel
,必须添加name
属性,否则报错。原因是,NgForm
指令为表单建立一个控件对象FormControl的集合,以此来作为表单控件的容器。控件的NgModel
属性绑定会以name
作为唯一标识来注册并生成一个FormControl
,并将其加入到FormControl
的集合中。
单选框:NgModel
会绑定选中的单选框的值
<input type='radio' name="sex" [(ngModel)]="curContact.sex" value="female" />女
<input type='radio' name="sex" [(ngModel)]="curContact.sex" value="male" />男
- 1
- 2
复选框:NgModel
会绑定一个布尔值
<input type='checkbox' name="lock" [(ngModel)]="curContact.lock" />
- 1
单选下拉框:option
绑定目标有两种,value
和ngValue
,value
返回值类型为基本数据类型,ngValue
返回值为对象数据类型。
//第一步:定义下拉框列表所需的数据 export class FormComponent {interests:any[] = [{value: 'reading', display: '阅读'},{value: 'traveling', display: '旅游'},{value: 'sport', display: '运动'}] }//第二步:构建下拉框模板 <select name="interestValue" [(ngModel)]="curContact.interestValue"><option *ngFor="let interst of intersts" [value]="interest.value">{{interest.display}}</option> </select>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
多选下拉框: 与单选下拉框类似,不过返回值为选中数据的数组。
模板局部变量:模板中对DOM元素或指令(包括组件)的引用(作用类似于getElementById
),可以使用在当前元素、兄弟元素或任何子元素中。
DOM元素局部变量:局部变量名前加#符号或者加ref-前缀
<input type='text' #contactName name="contactName" id="contactName"/>
<input type='number' ref-telNum name="telNum" id="telNum"/>
- 1
- 2
表单指令局部变量:表单指令的局部变量在定义时需手动初始化为特定指令的代表值,最终解析后会被赋值为表单指令实例对象的引用。
- NgForm表单局部变量
<form #contactForm="ngForm">//...
</form>
- 1
- 2
- 3
局部变量#contactForm
为NgForm
指令实例对象的引用,可以在模板中读取NgForm
实例对象的属性值,如追踪表单的valid
属性状态。
- NgModel控件局部变量
<input type='text' name="contactName" [(ngModel)]="curContact.name" #contactName="ngModel"/>
<p>{{ contactName.valid }}</p>
- 1
- 2
局部变量contactName
是NgModel
指令实例对象的引用,可以通过它读取NgModel
的属性值。
表单状态:NgForm
和NgModel
指令都可以用于追踪表单状态来实现数据检验,他们都有五个表示状态的属性,属性值为布尔类型,可通过对应的局部变量来获取。NgForm
追踪的是整个表单控件的状态,NgModel
追踪单个控件。
valid
:表单值是否改变pristine
:表单值是否未改变dirty
:表单值是否已改变touched
:表单是否已被访问过untouched
:表单时是否未被访问过
表单状态检验有三个时段,初始状态、输入后状态(valid
、pristine
、dirty
状态改变)、失去焦点后状态(touched
和untouched
状态改变)。
NgModelGroup指令:对表单输入内容进行分组,方便在语义上区分不同类型的输入。
<fieldset ngModelGroup="nameGroup" #nameGroup="ngModelGroup"><label>姓:</label><input type='text' name="firstName" [(ngModel)]="curContact.firstName" required/><label>名:</label><input type='text' name="lastName" [(ngModel)]="curContact.lastName" required/> </fieldset> 1 2 3 4 5 6 //这是form中的数据格式 {nameGroup: {firstName: '',lastName: ''} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
ngSubmit事件:
<form #contactForm="ngForm" (ngSubmit)="doSubmit(contactForm.value)"><li><button type='submit' [disabled]="!contactForm.value">添加</button><button type='reset'>重置</button></li> </form> export class FormComponent{doSubmit(formValue: any){} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
4.3 自定义表单样式
.ng-valid[required] {border-left: 5px solid #0f0; } .ng-invalid {border-left: 5px solid #f00; }<p [hidden]="contactName.valid || contactName.pristine">用户名长度为3-10个字符</p>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
4.4 表单检验
表单内置检验:required
、minlength
、maxlength
、pattern
表单自定义检验
- 创建自定义检验
//validate-username.ts import { FormControl } from '@angular/forms';const EMAIL_REGEXP = new RegExp("[a-z0-9]+@[a-z0-9]+.com"); const TEL_REGEXP = new RegExp("1[0-9]{10}");export function validateUserName(c: FormControl) {return (EMAIL_REGEXP.test(c.value) || TEL_REGEXP.test(c.value)) ? null : {userName: {valid: false,errorMsg: '用户名必须是手机号或者邮箱账号'}}; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 使用自定义检验
//... import { ReactiveFormsModule } from '@angular/forms'; import { FormComponent } from './form.component'; import { AppComponent} from './app.component';@NgModule({imports: [BrowserModule, ReactiveFormsModule],declarations: [AppComponent, FormComponent],bootstrap: [AppComponent] })export class AppModule {}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
首先要导入ReactiveFormsModule
import { Component } from '@angular/core'; import { FormGroup, FormControl } from '@angular/forms'; import { validateUserName } from './validate-username';@Component({selector: 'add-contact',template: `<form [formGroup]="customForm"><label>姓名:</label><input type='text' formControlName='customName'/></form> ` })export class FormComponent{customForm = new FormGroup({customName: new FormControl('', validateUserName)}); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
分别定义了FormGroup
和FormControl
的实例化对象
5 管道
Angular中,管道可以按照开发者指定的规则将模板内的数据进行转换。
5.1 什么是管道
模板中,通过管道操作符 | 使用管道,| 左边的为输入数 据,右边为管道。管道可以带有参数,通过传入参数输出不同格式数据。同时,模板表达式中可以同时使用多个管道进行不同的处理。
<p>{{ birthday | date }}</p><P>{{ birthday | data:"MM/dd/y" }}</p><p>{{ expression | pipeName1 | pipeName2 }}</p>
- 1
- 2
- 3
- 4
- 5
5.2 几种管道
内置管道:Angular提供的,不需导入可以直接使用。
- DatePipe:日期管道,格式化日期,纯管道
- JsonPipe:将输入数据对象经过JSON.stringify()方法转换后输出对象字符串,非纯管道
- UpperCasePipe:文本中所有小写字母全转换为字母,纯
- LowerCasePipe:变成小写,纯
- DecimalPipe:将数值按特定格式显示文本,纯
- CurrencyPipe:数值转化为本地货币格式,纯
- PercentPipe:数值转百分比,纯
- SlicePipe:将数值或者字符串裁剪成新的子集,非纯管道
expression | date: formatexpression | jsonexpression | uppercaseexpression | lowercaseexpression | number[: digitInfo]expression | currency[: currencyCode[: symbolDisplay[: digitInfo]]]expression | percentexpression | slice: start[: end]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
自定义管道:通过以下几个步骤实现
- 定义元数据:引入Pipe和PipeTransform,同时为管道命名
//sexreform.pipe.ts import { Pipe, PipeTransform } from "@angular/core";@Pipe {name: 'sexReform' }export class SexReform implements PipeTransform {//... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 实现transform方法
export class SexReform implements PipeTransform {transform(val: string): string {switch(val) {case 'male': return '男';case 'female' return '女';default: return '未知性别';}} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 使用自定义管道
//使用管道前,需要在@NgModule的元数据declarations数组中添加自定义管道 import { SexReform } from 'pipes/sexreform.pipe';@NgModule ({//... declarations: [SexReform] })//可以像内置管道一般使用自定义管道咯 @Component ({selector: 'pipe-demo-custom',template: `<p>{{ sexValue | sexReform }}</p> ` })
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
纯管道与非纯管道的区别:只有发生纯变才会调用该管道,这类管道称为纯管道,其余的管道成为非纯管道。纯变指的是对基本数据类型(String、Number、Boolean)输入值的变更或者对对象引用(Array、Function、Object)的更改。
只要数据发生改变,均会触发非纯管道,但不一定会触发纯管道,需要考察数据变化的情形是否为纯变化。看下面这个例子:
//... @Component ({selector: 'pure-pipe-demo',template: `<div><p>{{ dateObj | date: "y-MM-dd HH:mm:ss EEEE" }}</p><p>{{ dateStr | date: "y-MM-dd HH:mm:ss EEEE" }}</p></div> ` })export class PurePipeDemoComponent {dateObj: date = new Date('2016-06-08 20:05:08');dateStr: string = '2016-06-08 20:05:08';constructor(){setTimeout(() => {this.dateObj.setMonth(11),this.dateStr = '2016-12-08 20:05:08'},2000);} }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
初始日期分别为:
'2016-06-08 20:05:08 Wednesday'
'2016-06-08 20:05:08 Wednesday'
- 1
- 2
2s之后变成了
'2016-06-08 20:05:08 Wednesday'
'2016-12-08 20:05:08 Thursday'
- 1
- 2
你猜这是为什么呢??????
转载于:https://www.cnblogs.com/zzy-run-92/p/9396756.html
三:Angular 模板 (Templates)相关推荐
- 为PLOG增加了三套模板
博客程序PLOG真的不错,我现在在局域网内就在用它为用户提供博客服务,如果你有兴趣,可以goole一下,目前国内可以找到0.31版的不完全汉化版,如果你喜欢尝新,可以到它的 官方主页下载最新版0.32 ...
- abp angular 和mvc_使用.net core ABP和Angular模板构建博客管理系统(实现自己的业务逻辑)...
返回目录 之前写到使用.net core ABP 和Angular模板构建项目,创建后端服务.文章地址:http://www.jianshu.com/p/fde1ea20331f 创建完成后的api基 ...
- C++ 高级篇(一)—— 模板(Templates)
模板(Templates)是ANSI-C++ 标准中新引入的概念.如果你使用的 C++ 编译器不符合这个标准,则你很可能不能使用模板. 函数模板( Function templates) 模板(Tem ...
- poj 3486 A Simple Problem with Integers(树状数组第三种模板改段求段)
1 /* 2 树状数组第三种模板(改段求段)不解释! 不明白的点这里:here! 3 */ 4 #include<iostream> 5 #include<cstring> 6 ...
- Angular模板高级使用
Angular模板高级使用 使用ng-template Angular自带一个叫做ng-template的组件,它允许我们声明Angular模板的任何部分.这是给我们的模板赋予动态性的一种很好的方式, ...
- IntelliJ IDEA 快捷键及模板Templates设置
一.设置快捷键(Keymap) 1. 设置快捷为 Eclipse 的快捷键 2.通过快捷键功能修改快捷键设置 3.通过指定快捷键,查看或修改其功能 4.导入已有的设置 点击 0K 之后,重启 IDEA ...
- 探探php模板下载,PHP开发的优客365网址导航商业精华版1.1.6版本源码带WAP手机版附带三款模板和四款插件_随便下源码网...
优客365网站分类导航系统是个跨平台的开源软件,基于PHP+MYSQL开发构建的开源网站分类目录管理系统,具有操作简单.功能强大.稳定性好.扩展性及安全性强.二次开发及后期维护方便,可以帮您迅速.轻松 ...
- 带视频详细教程三套模板免授权交友盲盒1.8.0H5脱单盲盒交友源码
简介: 本套源码为H5独立版无需授权,无需公众号,有三套模板可自由切换,后台是thinkphp的框架. 有详细文档搭建教程. 本系统免公众号支持第三方免签约支付支持打包安卓+IOS双端 支付接口:微信 ...
- 《信息化项目文档模板三——会议纪要模板》
系列文章目录 <信息化项目文档模板一--项目需求说明书> <信息化项目文档模板二--项目启动会文档模板> <信息化项目文档模板三--会议纪要模板> <信息化项 ...
最新文章
- Golang正则笔记 :使用正则表达式处理题库文本
- AI 热潮之下,初创企业能否躲过科技巨头的碾压?
- 如何设置VSS源代码管理工具使用KDiff3
- GitHub换帅!开源大神辞任CEO,竞品GitLab刚完成IPO
- 关于串口接收数据不全的问题
- dvd管理器java_Java DVD管理器 基础示例代码下载
- IIS Express 使用详细说明
- [转载]在 WPF 專案中開啟 Blend
- 【Python笔记】AttributeError: module 'urllib3' has no attribute 'PoolManager'
- 太原冶金技师学院计算机系,山西冶金技师学院专业都有什么
- 加密项目必修科目:代币经济学
- 什么是前贴片、中贴片和后贴片广告?它们分别在哪里使用?
- 算法基础篇-05-排序-LowB三人组(冒泡/选择/插入排序)
- android系统添加内置APP(自带.so)
- 华为云mysql教程_华为云+NextCloud(私人云盘搭建)
- MySQL之23:59:59进位
- 浅墨博客《Real Time Rendering 3rd》提炼总结 截取(一)
- oc实时渲染的图如何导出_如何使用C4D制作逼真的循环三维动画
- 就在昨天,我也关闭了朋友圈
- e580显卡驱动_搭载AMD RX 550独显!联想Thinkpad E580评测:能玩大型游戏的亲民商务本...
热门文章
- [网络流24题]魔术球问题(简化版)
- H-ui 前端框架-免费
- 微信消息是怎么发出去?(计算机网络层次模型)
- 宝藏世界中什么叫服务器中断了,宝藏世界版本检查错误解决方法 Trove登陆不了怎么办...
- CMake Error: The source directory “/~./opencv-4.5.0/build“ does not appear to contain CMakeLists.tx
- 【人生成就达成】【#1】
- C++11 变量类型获取typeid
- 2023计算机毕业设计选题推荐 - 计算机毕业设计题目大全
- adb 发送文件到Android设备和从Android手机复制文件
- 用telnet+openocd+jtag_dpi+vcs仿真调试RISCV的cpu(二,不同cpu的TAP菊花链连接,2TAP)