Angular
简介
AngularJS 是一个 JavaScript 框架。它可通过
AngularJS 通过 指令 扩展了 HTML,且通过 表达式 绑定数据到 HTML。
AngularJS 扩展了 HTML
AngularJS 通过 ng-directives 扩展了 HTML。
ng-app 指令定义一个 AngularJS 应用程序。
ng-model 指令把元素值(比如输入域的值)绑定到应用程序。
ng-bind 指令把应用程序数据绑定到 HTML 视图。
Angular提倡的文件命名方式
组件名称.component.ts
组件的HTML模板命名为: 组件名称.component.html
组件的样式文件命名为: 组件名称.component.css
在使用angular前,需要先配置环境
-
node.js
-
安装完node后可以将npm资源库设置成国内淘宝镜像,下载cnpm
npm config set registry https://registry.npm.taobao.org
npm install -g cnpm --registry=https://registry.npm.taobao.org
-
安装CLI
3.1:在安装了cnpm的目录下打开命令行
3.2:输入cnpm install -g @angular/cli
3.3:通过ng v命令可查看是否安装成功
-
安装TypeScript
4.1:cnpm install -g typescript
4.2:tsc -v可查看是否安装成功
-
安装saas
5.1:npm install node-sass --registry=http://registry.npm.taobao.org
5.2:安装saas中间会报几次错误,遇到错误重新安装即可
搭建项目
1.使用命令行创建
1.1cnpm下创建项目。(做Demo时候可以使用)
切换到要创建项目的目录,命令行输入:ng new frontend
2.使用cnpm安装Primeng
2.1 进入项目根目录
2.2 cnpm install primeng --save
2.3安装第三方字体awesome:
cnpm install font-awesome
2.4安装Augular4+动画
cnpm install @angular/animations --save
2.5安装jquery
npm install jquery --save(遇到错误重复安装即可)
npm install @types/jquery --save
2.6命令行输入ng serve即可通过localhost:4200访问欢迎界面
@Component
:这是一个 Decorator(装饰器),其作用类似于 Java 里面的注解。Decorator 这个语言特性目前(2017-10)处于 Stage 2(草稿)状态,还不是 ECMA 的正式规范。selector
:组件的标签名,外部使用者可以这样来使用这个组件:。默认情况下,ng 命令生成出来的组件都会带上一个 app 前缀,如果你不喜欢,可以在 angular-cli.json 里面修改 prefix 配置项,设置为空字符串将会不带任何前缀。templateUrl
:引用外部的 HTML 模板。如果你想直接编写内联模板,可以使用 template,支持 ES6 引入的“模板字符串”写法。styleUrls
:引用外部 CSS 样式文件,这是一个数组,也就意味着可以引用多份 CSS 文件。export class AppComponent
:这是 ES6 里面引入的模块和 class 定义方式
何为“轻逻辑”?
简而言之,所谓“轻逻辑”就是说,你不能在模板里面编写非常复杂的 JavaScript 表达式。比如,Angular 的模板语法就有规定:
- 你不能在模板里面 new 对象
- 不能使用=、+=、-=这类的表达式
- 不能用++、–运算符
- 不能使用位运算符
第一个组件
命令行窗口输入 ng generate component login --inline-template --inline-style
。
参数generate用来生成文件,参数component说明要生成一个组件,login是组件名称,后面的两个参数是告诉angular-cli:生成组件时,请把组件的HTML模板和CSS样式和组件放在同一个文件中(其实分开文件更清晰。)
可以把上面的命令改写成 ng g c login -it -is
angular-cli为我们在\src\app目录下生成了一个新文件夹login,在login目录下生成了2个文件
@Component修饰配置中的 selector: 'app-login'
,意味着可以在其他组件的template中使用 <app-login></app-login>
来引用这个组件。
什么是模块?
简单来说模块就是提供相对独立功能的功能块,每块聚焦于一个特定业务领域。Angular内建的很多库是以模块形式提供的,比如FormsModule封装了表单处理,HttpModule封装了Http的处理等等。每个Angular应用至少有一个模块类 —— 根模块,我们将通过引导根模块来启动应用。按照约定,根模块的类名叫做AppModule,被放在 app.module.ts
文件中。
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
@NgModule({ //@NgModule装饰器用来为模块定义元数据。
declarations: [
AppComponent,
LoginComponent
],
imports: [
BrowserModule,
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Angular的服务与依赖注入
创建一个 AuthService , 在 src\app
下建立一个 core 的子文件夹( src/app/core
),命令行中输入 ng g s core/auth
( s这里是service的缩写,core/auth 是说在 core 的目录下建立 auth 服务相关文件
service 添加一个方法,为这个方法指定了返回类型和参数类型。这就是 TypeScript 的好处,有了类型约束,在别处调用这个方法时,如果给出的参数类型或返回类型不正确,IDE就可以直接告诉你错了。
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor() { }
loginWithCredentials(username: string, password: string): boolean {
if(username === 'wangpeng')
return true;
return false;
}
}
这个service虽然被创建了,但仍无法在Component中使用,可以直接在Component中import这个服务,但这样耦合性比较高,Angular提供了依赖性注入的方法
如何使用DI
在组件的修饰器中配置AuthService,然后在组件的构造函数中使用参数进行依赖注入。
import { Component, Inject, OnInit } from '@angular/core';
import { AuthService } from '../core/auth.service';
@Component({
selector: 'app-login',
template: `
<div>
<input #usernameRef type="text">
<input #passwordRef type="password">
<button (click)="onClick(usernameRef.value, passwordRef.value)">Login</button>
</div>
`,
styles: [
],
providers:[AuthService]//在providers中配置AuthService
})
export class LoginComponent implements OnInit {
//在构造函数中将AuthService示例注入到成员变量service中
//而且我们不需要显式声明成员变量service了
constructor(private service:AuthService) { }
ngOnInit(): void {
}
onClick(username:string, password:string) {
console.log('auth result is: ' + this.service.loginWithCredentials(username, password));
}
}
双向数据绑定
angular 提供了一个双向数据绑定的机制。这个机制是这样的,在组件中提供成员数据变量,然后在模板中引用这个数据变量。
在class中声明变量:
username = "";
password = "";
方法改造,去掉onClick中的参数,改为this.*调用
onClick() {
console.log('auth result is: ' + this.service.loginWithCredentials(this.username, this.password));
}
模板:
<div>
<input type="text"
[(ngModel)]="username"
/>
<input type="password"
[(ngModel)]="password"
/>
<button (click)="onClick()">Login</button>
</div>
[(ngModel)]="username"
:
[]的作用:把等号后面当成表达式来解析而不是当成字符串,如果去掉方括号等于将 ngModel 赋值成 username
这个字符串。方括号的含义是单向绑定,即在组件中给 model
赋的值会设置到 HTML 的 input 控件中。 [()]
是双向绑定的意思,即HTML对应控件的状态的改变会反射设置到组件的 model 中。
ngModel 是 FormModule 中提供的指令,负责从Domain Model(这里就是 username 或 password )中创建一个 FormControl 的实例,并将这个实例和表单的控件绑定起来。
表单数据的验证
<div>
<input type="text"
[(ngModel)]="username"
#usernameRef="ngModel"
required
minlength="3"
/>
{{ usernameRef.errors | json }}
<div *ngIf="usernameRef.errors?.required">this is required</div>
<div *ngIf="usernameRef.errors?.minlength">should be at least 3 charactors</div>
<input required type="password"
[(ngModel)]="password"
#passwordRef="ngModel"
/>
<div *ngIf="passwordRef.errors?.required">this is required</div>
<button (click)="onClick()">Login</button>
</div
ngIf
是一个Angular2的指令,是用于做条件判断的。*ngIf="usernameRef.errors?.required"
的意思是当usernameRef.errors.required
为true
时显示div
标签。那么那个?
是干嘛的呢?因为errors
可能是个null,如果这个时候调用errors
的required
属性肯定会引发异常,那么?
就是标明errors
可能为空,在其为空时就不用调用后面的属性了。
<div>
<form #formRef="ngForm">
<input type="text"
name="username"//要指定name不然会报错
[(ngModel)]="username"
#usernameRef="ngModel"
required
minlength="3"
/>
<div *ngIf="usernameRef.errors?.required">this is required</div>
<div *ngIf="usernameRef.errors?.minlength">should be at least 3 charactors</div>
<input type="password"
name="password"
[(ngModel)]="password"
#passwordRef="ngModel"
required
/>
<div *ngIf="passwordRef.errors?.required">this is required</div>
<button (click)="onClick()">Login</button>
<button type="submit">Submit</button>
</form>
</div>
表单项过多时可以用HTML提供的fieldset
标签来处理,<fieldset ngModelGroup="login">
对于 fieldset 之内的数据都分组到了 login
对象中。
*ngFor
是一个 “结构型指令”。结构型指令会通过添加、删除和操纵它们的宿主元素等方式塑造或重塑 DOM 的结构。带有星号 *
的指令都是结构型指令。
Angular 模板语法的五个常用特性:
*ngFor
*ngIf
- 插值
{{}}
- 属性绑定
[]
- 事件绑定
()
组件
组件在用户界面(也就是 UI)中定义了一些责任区,让你能复用这些 UI 功能集。你已经通过商品列表组件构建了一个。
组件包含三部分:
- 一个组件类,它用来处理数据和功能。上一节,我们在组件类中定义了商品数据和
share()
方法,它们分别用来处理数据和功能。 - 一个 HTML 模板,它决定了 UI。在上一节中,商品列表的 HTML 模板用来显示每个商品的名称、描述和 “Share” 按钮。
- 组件专属的样式定义了外观和感觉。商品列表中还没有定义任何样式,那属于组件 CSS 负责。
app-root
(橙色框)是应用的外壳。这是要加载的第一个组件,也是所有其它组件的父组件。你可以把它想象成一个基础页面。app-top-bar
(蓝色背景)是商店名称和结帐按钮。app-product-list
(紫色框)是你在上一节中修改过的商品列表。
import { Input} from '@angular/core';
定义一个带 @Input()
装饰器的 product
属性。@Input()
装饰器指出其属性值是从该组件的父组件商品列表组件中传入的。
import { Output, EventEmitter } from '@angular/core';
@Output()
装饰器,事件发射器 EventEmitter()
@Output() notify = new EventEmitter();
notify 属性发生变化时发出事件。
父组件要接收子组件发出的事件
路由
Angular 路由器能让你在不同的视图中显示产品的详情,每个产品都有自己的 URL。当用户执行应用任务时,路由器可以从一个视图导航到另一个视图(但在同一个页面)。比如:
- 在地址栏中输入一个 URL,导航到相应的页面。
- 点击页面上的链接,导航到新页面。
- 点击浏览器的后退和前进按钮,在浏览器的历史中前后导航
-
为商品详情生成一个新组件。把组件命名为
product-details
。提示:在文件列表框中,右键单击
app
文件夹,选择Angular Generator
和Component
。 -
在
app.module.ts
中,添加一个商品详情路由,该路由的path
是products/:productId
,component
是ProductDetailsComponent
。路由会将一个或多个 URL 路径与一个组件关联起来。
-
该指令配置组件的模板,以定义用户如何导航到路由或 URL。当用户点击商品名称时,应用就会显示那个商品的详情。
-
打开
product-list.component.html
。 -
修改
*ngFor
指令,在遍历列表的过程中把products
数组中的每个索引赋值给productId
变量。 -
修改商品名称的链接,使其包含
routerLink
。
-
-
RouterLink 指令让路由器控制了一个链接元素。在这种情况下,路由或 URL 包含一个固定的区段(
/products
),但其最后一个区段是变量,要插入当前商品的 id 属性。例如,id
为 1 的商品的 URL 类似于https://getting-started-myfork.stackblitz.io/products/1
。
使用路由信息
-
打开
product-details.component.ts
文件 -
改用外部文件中的商品数据。
-
从
@angular/router
包导入ActivatedRoute
,从../products
文件导入products
数组。 -
定义
product
属性,并将ActivatedRoute
作为参数添加到构造函数的括号中,以便把它注入到构造函数中。(
ActivatedRoute
专门用于由 Angular 路由器加载的每个路由组件。它包含关于该路由,路由参数以及与该路由关联的其它数据的信息。)
-
-
在
ngOnInit()
方法中订阅了路由参数,并且根据productId
获取了该产品。 -
修改模板,在
*ngIf
中显示商品详情。currency
管道来把product.price
从数字转换成货币字符串。管道给了你一种在 HTML 模板中转换数据的方式。
管理数据
- 修改商品详情视图,让它包含一个 “Buy” 按钮,它会把当前商品添加到由 “购物车服务” 管理的商品列表中。
- 添加一个购物车组件,它会显示购物车中的商品。
- 添加一个配送组件,它会使用 Angular 的
HttpClient
从.json
文件中检索配送数据来取得购物车中这些商品的运费。
创建购物车服务
生成service文件,并在其中写入有关商品与购物车操作的函数,引入HttpClient包,注入构造函数中。
在 product-details.component.ts
中导入购物车服务,并在构造函数的参数中注入服务
添加购物车组件,在app.module.ts
中添加路由:{ path: 'cart', component: CartComponent },
显示购物车商品
在购物车组件中注入购物车服务,将购物车服务中定义的方法写入
检索运费价格
服务器通常采用流的形式返回数据。 流是很有用的,因为它们可以很容易地转换返回的数据,也可以修改你请求数据的方式。 Angular 的 HTTP 客户端( HttpClient
)是一种内置的方式,可以从外部 API 中获取数据,并以流的形式提供给你的应用。
要使用Angular的HTTP客户端,必须在app.module.ts
中导入HttpClientModule
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
BrowserModule,
HttpClientModule,//要import进来
ReactiveFormsModule,
...省略
],
新建组件Shipping来使用CartService
利用 async
管道修改配送组件的模板,以显示配送类型和价格;
async
管道从数据流中返回最新值,并在所属组件的生命期内持续返回。当 Angular 销毁该组件时,async
管道会自动停止。
<h3>Shipping Prices</h3>
<div class="shipping-item" *ngFor="let shipping of shippingCosts | async">
<span>{{ shipping.type }}</span>
<span>{{ shipping.price | currency }}</span>
</div>
在购物车视图中添加一个到配送视图的链接
<p>
<a routerLink="/shipping">Shipping Prices</a>
</p>
Angular中的表单
Angular 中的表单建立在标准 HTML 表单功能之上,以帮助你创建自定义表单控件和轻松的验证体验。Angular 响应式表单有两个部分:组件中那些用于存储和管理表单的对象,以及表单在模板中的可视化。
ReactiveFormsModule
中提供了 FormBuilder
服务,FormBuilder
服务为生成控件提供了方便的方法。需要导入并注入该服务,然后才能使用它。
-
在CartComponent类中,定义
checkoutForm
来存储表单模型 -
把
checkoutForm
属性设置为一个包含name
和address
字段的表单模型。使用FormBuilder
的group()
方法来创建它,把该语句加入构造函数的花括号{}
中间。 -
创建结账表单
-
使用
formGroup
属性绑定把checkoutForm
绑定到模板中的form
标签上 -
在
form
标签上,使用ngSubmit
事件绑定来监听表单提交,并使用checkoutForm
值调用onSubmit()
方法 -
为
name
和address
添加输入字段。使用formControlName
属性绑定来把checkoutForm
表单控件中的name
和address
绑定到它们的输入字段<form [formGroup]="checkoutForm" (ngSubmit)="onSubmit(checkoutForm.value)"> <div> <label for="name"> Name </label> <input id="name" type="text" formControlName="name"> </div> <div> <label for="address"> Address </label> <input id="address" type="text" formControlName="address"> </div> <button class="button" type="submit">Purchase</button> </form>
-
-
最终效果: