这篇文章主要围绕Angular4官方文档(二)【数据的展现】和angular数据实时变化展开,旨在为您提供一份详细的参考资料。我们将全面介绍Angular4官方文档(二)【数据的展现】的优缺点,解答a
这篇文章主要围绕Angular 4官方文档(二)【数据的展现】和angular数据实时变化展开,旨在为您提供一份详细的参考资料。我们将全面介绍Angular 4官方文档(二)【数据的展现】的优缺点,解答angular数据实时变化的相关问题,同时也会为您带来Angular 2 + 折腾记 :(1)初识Angular-cli[官方脚手架]及脱坑要点、Angular 2 :初识 Angular-cli[官方脚手架] 及脱坑要点、Angular 4官方文档(一)【架构】、Angular 5 快速入门开发非视频教程,告别Angular.js , Angular 2 , Anuglar 4吧的实用方法。
本文目录一览:- Angular 4官方文档(二)【数据的展现】(angular数据实时变化)
- Angular 2 + 折腾记 :(1)初识Angular-cli[官方脚手架]及脱坑要点
- Angular 2 :初识 Angular-cli[官方脚手架] 及脱坑要点
- Angular 4官方文档(一)【架构】
- Angular 5 快速入门开发非视频教程,告别Angular.js , Angular 2 , Anuglar 4吧
Angular 4官方文档(二)【数据的展现】(angular数据实时变化)
通过把 Angular 组件的属性绑定到 HTML 模板的控件上,我们可以展示数据。
本小节中,我们将会创建一个英雄列表组件。我们将展示英雄名字的列表,并根据条件来决定是否在列表下方展示一条信息。
最终的 效果是这样的:
在线例子 / 下载示例代码 演示了这一节所有的提到的代码片段和语法。
1.用插值表达式展示组件的属性
通过插值表达式来绑定属性名,是展示组件属性最简单的方式。使用双花括号的插值表达式{{myHero}} ,我们可以把属性名myHero
放置在视图模板中。
(通过 Angular CLI 命令:ng new displaying-data
)创建一个名为displaying-data
的新项目,然后修改app.component.ts
文件,改成如下代码所示:
src/app/app.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'app-root',template: `<h1>{{title}}</h1> <h2>My favorite hero is: {{myHero}}</h2> ` }) export class AppComponent { title = 'Tour of Heroes'; myHero = 'Windstorm'; }
我们添加了两个属性(title
和 myHero
)到原先空的组件中。
通过使用插值表达式,更改后的模板展示组件的两个属性:
src/app/app.component.ts (template)
template: ` <h1>{{title}}</h1> <h2>My favorite hero is: {{myHero}}</h2> `
模板是ES6标准规定的以反引号(`) 括起来的多行字符串。反引号(`)与普通的单引号(’)不同,它允许多行书写,这使得代码可读性更好。
Angular 会自动从组件中获取title
和 myHero
这两个属性的值,把这些值传递给浏览器。 当这些属性改变时,Angular 会更新显示的值。
更准确地说,更新发生在一些与视图有关的异步事件之后,比如说键盘事件、计时器结束、或者一个 HTTP 请求。
注意,我们不会直接调用 new
来创建appComponent
类的实例。Angular 会为我们创建这个实例。它是怎么做到的呢?
@Component
装饰器中的 CSS selector
指定了一个名为<app-root>
的元素。这个元素在index.html
文件中是一个占位符:
src/index.html (body)
<body> <app-root>loading...</app-root> </body>
当我们在main.ts
中设置bootstrap
为AppComponent
类后,Angular 会在index.html
中寻找<app-root>
, 找到后,会实例化AppComponent
,并把它渲染到这个标签内。
运行这个项目(npm start
)。 应该能够看到如下所示的内容:
接下来的部分内容,回顾这个应用里的代码选择。
2.行内模板 or 模板文件?
组件的模板可在两个地方中选择一个存放。我们可以使用template
属性来定义模板为行内模板,或者在一个单独的 HTML 文件中定义模板,然后使用 @Component
装饰器的 templateUrl
属性,把它链接到组件的元数据
中。
选择行内模板还是单独的 HTML 文件取决于 个人的代码风格、开发环境 或者 公司机构的代码规范。本章节的示例代码使用的是 行内的 HTML 模板, 因为这个模板很小。没有额外的 HTML 文件,这个 demo 更简单。
以上两种风格,模板数据绑定有相同的方法访问到组件的属性。
3.构造函数 or 变量实例化?
虽然,这个例子使用 变量赋值
来初始化组件,你也可以通过构造函数
来声明并实例化属性。
src/app/app-ctor.component.ts (class)
export class AppCtorComponent { title: string; myHero: string; constructor() { this.title = 'Tour of Heroes'; this.myHero = 'Windstorm'; } }
为了简便起见,本节中的示例代码,会更多地使用精简的变量赋值
的形式。
4.*ngFor
显示数组的属性
为了展示一个英雄列表,我们增加一个英雄名字的数组到组件中,并重新定义myHero
作为数组中的第一项。
src/app/app.component.ts (class)
export class AppComponent { title = 'Tour of Heroes'; heroes = ['Windstorm','Bombasto','Magneta','Tornado']; myHero = this.heroes[0]; }
现在,在 模板中使用 Angular 的*ngFor
指令来展示英雄列表的每一项内容。
src/app/app.component.ts (template)
template: ` <h1>{{title}}</h1> <h2>My favorite hero is: {{myHero}}</h2> <p>Heroes:</p> <ul> <li *ngFor="let hero of heroes"> {{ hero }} </li> </ul> `
我们使用了无序列表。在<li>
元素中的*ngFor
是 Angular 的重复指令。它标记<li>
元素(以及它的子节点)作为重复的模板。
src/app/app.component.ts (li)
<li *ngFor="let hero of heroes"> {{ hero }} </li>
* 不要忘了ngFor
前面的星号*
, 它是这个指令语法必不可少的组成部分。 更多的内容,请查阅 模板语法章节 *。
注意 在*ngFor
指令的双引号之间的 hero
是模板的输入变量。更多关于模板输入变量的信息,请查阅模板语法章节的 微语法部分 。
Angular 为列表中的每一项复制了<li>
标签, 把 hero
变量设置为当前循环的值。Angular 把这个变量作为 双花括号里插值表达式的上下文。
在这个例子中,ngFor
展示了一个数组, 但ngFor
可用于循环任何可迭代对象
的。
现在,英雄们出现在一个无序列表中:
5.为数据创建类
这个应用的代码直接在组件里定义数据,这不是最好的实现方式。当然,在一个简单的 demo 的,这样写没什么问题。
现在,这里绑定的是一个字符型的数组。在实际的应用中,绑定的大多是特殊的对象。
为了将绑定改为使用特殊的对象,我们把这个英雄名字的数组改写成 Hero
对象的数组。 为此,我们需要一个 Hero
类。
在这个app
文件夹里创建一个新的文件,命名为hero.ts
, 代码如下:
src/app/hero.ts
export class Hero { constructor( public id: number,public name: string) { } }
我们定义了一个类,它有一个构造函数和两个属性(id
和 name
)。
它也许看起来不像是一个带有属性的类,但它确实是有属性的。这个构造函数参数的声明使用了 TypeScript
简写形式。
想想最初的参数:
src/app/hero.ts (id)
public id: number,
这种简写语法做了许多事情:
声明构造函数的参数及其类型。
声明公开的同名的属性。
创建类的实例时,根据相应的参数初始化属性。
5.1 使用 Hero 类
在导入Hero
后,AppComponent.heroes
属性返回一个Hero
类型的数组。
src/app/app.component.ts (heroes)
heroes = [ new Hero(1,'Windstorm'),new Hero(13,'Bombasto'),new Hero(15,'Magneta'),new Hero(20,'Tornado') ]; myHero = this.heroes[0];
下一步,更新模板
。现在,模板会展示 hero 的 id
和 name
。我们需要修改一下,变成只显示 hero 的name
属性。
src/app/app.component.ts (template)
template: ` <h1>{{title}}</h1> <h2>My favorite hero is: {{myHero.name}}</h2> <p>Heroes:</p> <ul> <li *ngFor="let hero of heroes"> {{ hero.name }} </li> </ul> `.
显示效果看起来一样,但是代码变得更加干净。
6.*ngIf
有条件地展示数据
有时,应用只在某个特定的条件下,才需要显示视图或者视图的一部分。
让我们来改一下例子:如果超过3个 hero,才显示一条消息。
Angular 的ngIf
指令 基于 true 或false 的条件判断来插入或者移除一个元素。
为了看到它实际效果,我们在模板的底部增加一个段落:
src/app/app.component.ts (message)
<p *ngIf="heroes.length > 3">There are many heroes!</p>
* 不要忘了ngIf
前面的星号*
, 它是这个指令语法必不可少的组成部分。 更多的内容,请查阅 模板语法章节的ngIf部分 *。
在双引号里的模板表达式"heroes.length >3"
看起来很像 typescript
。 当组件的 heroes 列表有超过3个 hero 时, Angular 会在 DOM 增加 一个段落,这条消息就会出现。如果小于或等于3个 hero, 那么 Angular 会就忽略这个段落,也就不显示这条消息。更多内容,请查阅模板语法章节的 模板表达式部分 。
Angular 不是显示或者隐藏这条消息。它是从DOM 里新增或者移除了这个<p>
元素。这样做可以提高性能,尤其是在大型项目中需要包含或者排除 大量的有许多数据绑定的 HTML 时。
试一试吧。因为数组里有4个成员,这条消息会显示出来。回到 app.component.ts
中,删除或者注释掉 hero 数组中的一个成员。 浏览器会自动刷新,那条消息应该会消失。
7.总结
现在我们知道如何使用:
- 使用双花括号的插值表达式
{{ }}
来展示组件的属性。 *ngFor
指令用来展示数组。- TypeScript 类,用来定义组件的数据类型以及展示这个数据的属性。
*ngIf
指令基于表达式的布尔值来决定是否展示一段 HTML 代码。
最终的代码是这样的:
src/app/app.component.ts
import { Component } from '@angular/core'; import { Hero } from './hero'; @Component({ selector: 'my-app',template: ` <h1>{{title}}</h1> <h2>My favorite hero is: {{myHero.name}}</h2> <p>Heroes:</p> <ul> <li *ngFor="let hero of heroes"> {{ hero.name }} </li> </ul> <p *ngIf="heroes.length > 3">There are many heroes!</p> ` }) export class AppComponent { title = 'Tour of Heroes'; heroes = [ new Hero(1,'Tornado') ]; myHero = this.heroes[0]; }
src/app/hero.ts
export class Hero { constructor( public id: number,public name: string) { } }
src/app/app.module.ts
import { NgModule } from '@angular/core'; import { browserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; @NgModule({ imports: [ browserModule ],declarations: [ AppComponent ],bootstrap: [ AppComponent ] }) export class AppModule { }
main.ts
import { platformbrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; platformbrowserDynamic().bootstrapModule(AppModule);
Angular 2 + 折腾记 :(1)初识Angular-cli[官方脚手架]及脱坑要点
前言
这个系列的进度有些跳跃性,我尽量直白点解释,但是我不是官方文档,直入主题!!!!
什么是Angular-cli
简言之:就是NG团队自行维护的一个`脚手架`[内置单元测试及webpack2打包工具等] -- 这货前身是ember-cli
;
官网:https://cli.angular.io/
Github: https://github.com/angular/angular-cli
npm: https://www.npmjs.com/package/angular-cli
我最早是从beta18开始用的,截止beta28.3 – 这个分支已经废弃,已经迁移,之前npm install angular-cli
不推荐;
目前最新的是v1.0.0-rc.2
;从旧版本到rc期间坑了太多次,每次升级各种酸爽;
rc2开始基本变化不大,可以直接拿来用了。。
安装之前
- window下:
- 安装
lts
版本的nodejs[6.10.0] , Angular-cli中的node-sass
不支持7.x,装不上的 - 装了visual studio 2015+及python2+ [node-sass及部分模块需要调用这两个依赖进行编译]
- 或者采用国内的cnpm安装,记得带版本号,有时候不带版本会安装
0.0.1
版本,cnpm好像会把编译好node-sass装上,不用本地再次编译 – 亲测多次可用 - 或者安装
windows-build-tools
:windows下的依赖库,再执行官方安装命令
- 安装
Linux下:
- nodejs控制推荐用
nvm
来管理 :https://github.com/creationix/nvm - 先下载
nvm
的脚本,用curl
或者wget
都行,前者有些不预装,后者基本都有
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
- 记得重新读取bash的配置文件,因为脚本没法实时生效,用linux的
source
命令一下子就搞定了
source ~/.bashrc
: 意思就是重新加载当前用户的bash配置文件
nvm
的命令不多,仔细看看文档哈,我们这里只需要稳定版本(lts)
nvm install --lts
: 之后node怎么用就怎么用哈
- 其次,linux下推荐用
yarn
替代npm
,使用起来体验好很多,速度也快很多
# 下载公钥 curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - # 把源写进去源请求列表 echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list # 我用的是deepin !!!! 支持一下国产,挺好用哈~~~~ sudo apt-get update && sudo apt-get install yarn
- nodejs控制推荐用
开发工具这些就不扯了,我选择VSCODE
安装
npm install -g @angular/cli
– 无压力过墙的孩子推荐
或者
cnpm install -g @angular/cli@v1.0.0-rc.2
– 国内淘宝源(cnpm的安装自行搜索)
或者
yarn add global @angular/cli
– 看网络了。。。
初始化项目
angular-cli可以初始化ng2或者ng4的项目,我这里说2+;
脚手架的命令很多,我这里只列出最常用的;
- 新建东东
范围 | 命令 | 作用 |
---|---|---|
new | ng new new_project | 初始化新项目 |
Component | ng g component my-new-component | 新建一个组件 |
Directive | ng g directive my-new-directive | 新建一个指令 |
Pipe | ng g pipe my-new-pipe | 新建一个管道 |
Service | ng g service my-new-service | 新建一个服务 |
Class | ng g class my-new-class | 新建一个类 |
Interface | ng g interface my-new-interface | 新建一个接口 |
Enum | ng g enum my-new-enum | 新建一个枚举 |
Module | ng g module my-module | 新建一个模块 |
- 测试及检测
范围 | 命令 | 作用 |
---|---|---|
e2e | ng e2e | 跑自动化测试-自己写测试测试用例 |
test | ng test | 跑单元测试 – 自己写 |
lint | ng lint | 调用tslint跑整个项目,可以收获一堆警告和错误,–force –fix –format可以帮助格式和修复部分问题 |
- 启动
ng serve
: 启动脚手架服务,默认端口4200;自定义什么看帮助额
- 打包
ng build
: 开发模式打包,调用的环境文件是/src/environments/environments.ts
;
ng build --prod
: 以前调用aot打包还需要带上--aot
,从beta31开始,--prod
模式下自动调用aot打包,
调用的环境文件是/src/environments/environments.prod.ts
- 弹出配置文件(还原真实的配置文件)
我们看到的ng
开头的命令都是二重封装的。。。有时候我们想要改源文件或者看到原始配置是怎么样的这货就用到了
ng eject
: 这个东西的配置很多,可以弹出各种各样的源配置和文件
生成的目录树小解释
总结
- 这个脚手架支持sass和less,手动改下
.angular-cli.json
就可以了。或者执行命令改下支持,,一个道理的 - 当然可以配置接口反向代理,但是我感觉不实用,推荐还是把不同接口的url写在不同的environment里面,用
Nginx
做反向代理!ng serve --proxy-config proxy.conf.json
;这个老版本是支持的,现在不知道支不支持
{ "/": { "target": "http://localhost:3000","secure": false } }
Angular 2 :初识 Angular-cli[官方脚手架] 及脱坑要点
什么是Angular-cli
简言之:就是NG团队自行维护的一个`脚手架`[内置单元测试及webpack2打包工具等] -- 这货前身是 ember-cli ;
官网: https://cli.angular.io/
Github: https://github.com/angular/angular-cli
npm: https://www.npmjs.com/package/angular-cli
我最早是从beta18开始用的,截止beta28.3-- 这个分支已经废弃,已经迁移,之前 npm install angular-cli 不推荐;
目前最新的是 v1.0.0-rc.2 ;从旧版本到rc期间坑了太多次,每次升级各种酸爽;
rc2开始基本变化不大,可以直接拿来用了。。
安装之前
window下:
安装 lts 版本的nodejs[6.10.0] , Angular-cli中的 node-sass 不支持7.x,装不上的
装了visual studio 2015+及python2+ [node-sass及部分模块需要调用这两个依赖进行编译]
或者采用国内的cnpm安装,记得带版本号,有时候不带版本会安装 0.0.1 版本,cnpm好像会把编译好node-sass装上,不用本地再次编译 -- 亲测多次可用
或者安装windows-build-tools:windows下的依赖库,再执行官方安装命令
Linux下:
nodejs控制推荐用 nvm 来管理 : https://github.com/creationix/nvm
先下载 nvm 的脚本,用 curl 或者 wget 都行,前者有些不预装,后者基本都有
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
记得重新读取bash的配置文件,因为脚本没法实时生效,用linux的 source 命令一下子就搞定了
source ~/.bashrc : 意思就是重新加载当前用户的bash配置文件
nvm 的命令不多,仔细看看文档哈,我们这里只需要稳定版本(lts)
nvm install --lts : 之后node怎么用就怎么用哈
其次,linux下推荐用 yarn 替代 npm ,使用起来体验好很多,速度也快很多
# 下载公钥
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
# 把源写进去源请求列表
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
# 我用的是deepin !!!! 支持一下国产,挺好用哈~~~~
sudo apt-get update && sudo apt-get install yarn
开发工具这些就不扯了,我选择VSCODE
安装
npm install -g @angular/cli -- 无压力过墙的孩子推荐
或者
cnpm install -g @angular/cli@v1.0.0-rc.2 -- 国内淘宝源(cnpm的安装自行搜索)
或者
yarn add global @angular/cli -- 看网络了。。。
初始化项目
angular-cli可以初始化ng2或者ng4的项目,我这里说2+;
脚手架的命令很多,我这里只列出最常用的;
范围
命令
作用
1、new
ng new new_project
初始化新项目 /
2、Component
ng g component my-new-component
新建一个组件
3、Directive
ng g directive my-new-directive
新建一个指令
4、Pipe
ng g pipe my-new-pipe
新建一个管道
5、Service
ng g service my-new-service
新建一个服务
6、Class
ng g class my-new-class
新建一个类
7、Interface
ng g interface my-new-interface
新建一个接口
8、Enum
ng g enum my-new-enum
新建一个枚举
9、Module
ng g module my-module
新建一个模块
测试及检测
范围 命令 作用
e2e
ng e2e
跑自动化测试-自己写测试测试用例
test
ng test
跑单元测试 -- 自己写
lint
ng lint
调用tslint跑整个项目,可以收获一堆警告和错误,--force --fix --format可以帮助格式和修复部分问题
启动
ng serve : 启动脚手架服务,默认端口4200;自定义什么看帮助额
打包
ng build : 开发模式打包,调用的环境文件是 /src/environments/environments.ts ;
ng build --prod : 以前调用aot打包还需要带上 --aot ,从beta31开始, --prod 模式下自动调用aot打包,
调用的环境文件是 /src/environments/environments.prod.ts
弹出配置文件(还原真实的配置文件)
我们看到的 ng 开头的命令都是二重封装的。。。有时候我们想要改源文件或者看到原始配置是怎么样的这货就用到了
ng eject : 这个东西的配置很多,可以弹出各种各样的源配置和文件
在 cnpm install -g @angular/cli@latest 安装完成(latest代表ng最新版本)之后
如果想安装指定版本 cnpm或者npm install -g @angular/cli@1.4.1
用 ng -v 查看一下版本以及是否安装成功
会出现 Angular-cli
angular-cli:1.0.0-beta.28.3 ng版本号
node:8.2.1 node版本号
os:win32 x64 电脑系统
npm install 之后 ng-serve 然后在地址栏:localhost:4200 出现以下页面
如果之前安装失败了一定要卸载干净
npm uninstall -g angular-cli
npm cache clean
生成的目录树小解释
总结
这个脚手架支持sass和less,手动改下 .angular-cli.json 就可以了。或者执行命令改下支持,,一个道理的
当然可以配置接口反向代理,但是我感觉不实用,推荐还是把不同接口的url写在不同的environment里面,用 nginx 做反向代理! ng serve --proxy-config proxy.conf.json ;这个老版本是支持的,现在不知道支不支持
{
"/": {
"target": "http://localhost:3000",
"secure": false
}
}
Angular 4官方文档(一)【架构】
初识 Angular 之(一)架构
Angular 是一个构建客户端应用的框架。它使用 HTML 和 JavaScript 或者可以被编译成 JavaScript(比如 TypeScript) 的语言。
Angular 框架由多个库文件(libraries)组成。其中, 有些是核心库,有些则是可选的。
Angular 应用是这样写的:编写有 Augular 的指令的 HTML 模板(templates),编写组件类(component classes)来管理这些模板,在服务(services)中添加应用逻辑, 再把这些组件和服务封装在模块(modules)中。
然后,通过引导 根模块(root module) 来启动这个应用。Angular 接管应用程序,在浏览器中展示应用的内容,同时根据提供的指令来响应用户的交互行为。
当然,Angular 框架远不止上面提到的内容。 你将在后面学到更多细节。现在,先关注框架的整体的架构。
1.Modules 模块
Angular 应用是模块化的,并且 Angular 有自己的模块化系统,叫做 NgModules.
Angular模块(NgModules) 很重要。这里会介绍模块(Modules),在 NgModules 章节会深入讲解模块与Angular模块(NgModules)。
每一个 Angular 应用至少有一个 NgModules 类,即根模块( root module),通常命名为 AppModule.
小的应用也许只有根模块这一个模块。大部分应用有许多 特性模块(feature modules)。每一个特性模块都是一段紧密结合的代码块,专注于一个应用领域,工作流程或者一组密切关联的功能。
一个 NgModule,无论是根模块还是特性模块,都是一个带有 @NgModule 装饰器( decorator)的类。
装饰器是一种函数,它能够修改 JavaScript 类。Angular 有许多装饰器。它们把元数据(Metadata)附加到类中,这样 Angular 就知道这些类有什么含义 以及它们应该如何工作。
@NgModule 是一个装饰器函数,它有一个元数据(Metadata)对象。这个对象的属性(properties)用于修饰这个模块。主要属性有以下这5个:
1) declarations: 从属于这个模块的视图类( view classes)。Angular 有3种视图类:组件(components),指令(directives) 以及管道(pipes)
2) exports: declarations 的子集,在其他模块的组件模板中可见以及可用的视图类(即暴露给其他模块用的视图类)。
3) imports: 在这个模块的组件模板中需要使用到的类所在的其它模块(即依赖的其他模块)。
4) providers: 服务(service)的创建者,这个模块将这些服务添加到整个全局服务集合中。这些服务在整个应用中可用。
5) bootstrap: 应用的主视图,称作根组件(root component), 存储着应用其他的视图。只有根模块(root modul)需要设置这个属性。
以下是一个根模块示例:
src/app/app.module.ts
import { NgModule } from '@angular/core'; import { browserModule } from '@angular/platform-browser'; @NgModule({ imports: [ browserModule ],providers: [ Logger ],declarations: [ AppComponent ],exports: [ AppComponent ],bootstrap: [ AppComponent ] }) export class @H_301_69@AppModule { }
这里导出 AppComponent 只是为了演示如何导出的,在这个例子中不是必须的。根模块不需要导出任何东西,因为其他组件不需要导入这个根模块。
通过引导根模块来启动一个应用程序。在开发期间中,你很可能在 main.ts 文件中像这样引导 AppModule:
src/main.ts
import { platformbrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; platformbrowserDynamic().bootstrapModule(AppModule);
1.1 Angular模块(NgModules) vs. JavaScript 模块
Angular模块(NgModule) 是由 @NgModule 修饰的类,它是 Angular 的基本特性。
JavaScript 也有自己的用于管理 JavaScript 对象集合的模块系统。它与 NgModule 模块系统完全不同且毫不相关。
在 JavaScript 中,每个文件都是一个模块,所有定义在这个文件中的对象都从属于这个模块。模块使用关键字 export 把它的某些对象声明为公开的对象。JavaScript 模块使用 import 声明语句 从其他模块中引用这些公开的对象。
这是两种不同但互补的模块系统,在你的应用中都将会使用到。
1.2 Angular 库(Angular libraries)
Angular 以 一系列的JavaScript 模块的形式推出市场。你可以把他们当做库模块。
每一个 Angular 库的名字都带有 @angular 前缀。
通过 npm 包管理工具安装这些库,通过 JavaScript 的 import 声明语句来导入他们中的部分内容。
比如,从 @angular/core 库模块中导入 Angular 的 Component 装饰器:
import { Component } from '@angular/core';
也可以使用 JavaScript 的 import 声明语句 来从 Angular 库模块中导入Angular模块(NgModules):
import { browserModule } from '@angular/platform-browser';
在之前的 根模块示例中,应用的模块需要使用 browserModule 里的素材。为了访问到这些素材,需要把 browserModule 添加到 @NgModule 的元数据中:
imports: [ browserModule ]
这样,同时使用了 Angular和 JavaScript 的模块系统。
因为它们使用相同的单词如 imports exports,很容易搞混淆这两种模块系统。
2.组件 Components
一个组件控制着屏幕上的一块区域(即视图)。
我们在类里定义组件的应用逻辑。这个类通过 API 的属性和方法与视图交互。
例如,HeroListComponent 组件类有一个 hero 属性返回一个从服务(service)中获取的 heroes 数组。 HeroListComponent 还有一个 selectHero() 方法。当用户从列表中点击选中一个 hero 时,这个方法设置 selectedHero 属性。
export class @H_301_69@HeroListComponent implements @H_301_69@OnInit { heroes: Hero[]; selectedHero: Hero; constructor(private service: HeroService) { } ngOnInit() { this.heroes = this.service.getHeroes(); } selectHero(hero: Hero) { this.selectedHero = hero; } }
在用户使用应用程序的过程中,Angular 会创建、更新、销毁组件。在应用程序的生命周期的任意时刻,它都可以通过可选的生命周期钩子(lifecycle hooks)来作出响应,比如 上面声明的 ngOnInit() 钩子。
3.模板 Templates
我们在组件自带的模板中定义组件的视图。 模板以 HTML 形式存在,它告诉 Angular 如何去渲染组件。
除了有一点点差别,模板看起来像是普通的 HTML。
<@H_301_69@h2>Hero List</@H_301_69@h2> <@H_301_69@p><@H_301_69@i>Pick a hero from the list</@H_301_69@i></@H_301_69@p> <@H_301_69@ul> <@H_301_69@li *ngFor="let hero of heroes" (click)="selectHero(hero)"> {{hero.name}} </@H_301_69@li> </@H_301_69@ul> <@H_301_69@hero-detail *ngIf="selectedHero" [hero]="selectedHero"></@H_301_69@hero-detail>
虽然这个模板使用典型的 HTML 元素 比如 <h2>
和 <p>
, 它有些差异。
像 *ngFor
,{{hero.name}}
,(click)
,[hero]
以及 <hero-detail>
这些代码,它们使用的是 Angular的模板语法。
这个模板最后一行,<hero-detail>
标签 是一个自定义的元素,用来展示一个新的组件,即 HeroDetailComponent.
HeroDetailComponent 与 HeroListComponent 组件不同,它用于展示关于某个特定 hero 的事实。
这个 hero 是用户从 HeroListComponent 的列表中选择的。HeroDetailComponent 是 HeroListComponent 的子组件。
注意 <hero-detail>
与原生的 HTML 元素 融洽地放置在一起。 在同一个布局中,自定义组件与原生 HTML 完美地混合在一起。
4.元数据 Metadata
元数据告诉 Angular 如何处理一个类。
返回去看 HeroListComponent 的代码, 它仅仅是一个类。完全看不到没有框架的痕迹,里面没有 Angular。
事实上,HeroListComponent 真的是仅仅是一个类,直到我们告诉 Angular 它是一个组件。
告诉 Angular HeroListComponent 是一个组件, 把元数据附加到这个类上。
在 TypeScript 中,我们通过 装饰器(decorator)来添加元数据。 以下代码是组件 HeroListComponent 的一些元数据:
@Component({ selector: 'hero-list',templateUrl: './hero-list.component.html',providers: [ HeroService ] }) export class @H_301_69@HeroListComponent implements @H_301_69@OnInit { /* . . . */ }
装饰器 @Component 把紧挨着它的类指定为一个组件类。
装饰器 @Component 必须接收一个配置对象作为参数,它包含 Angular 创建并展示这个组件及其视图所需要的信息。
以下是一些最有用的 @Component 的配置选项:
1)selector: CSS 选择器。告诉 Angular 创建这个组件的实例并将这个实例插入到 CSS 选择器(<hero-list>
标签) 所在的父级 HTML 上。如果一个 app 的 HTML 包含 标签, Angular 会将组件 HeroListComponent 视图的实例插入到这个标签之间。
2)templateUlr: 这个组件的 HTML 模板所在的相对位置。
3)providers: 组件所需要的服务的依赖注入提供商数组。它告知 Angular 这个组件的构造函数需要 HeroService 服务,以便获取一个 heroes 的列表来展示。
@Component 装饰器里的元数据 告知 Angular 从哪里去获取我们为这个组件指定的构建单元。
模板、元数据和组件一起描绘视图。
我们以相似的方式使用其他元数据装饰器来指导 Angular 的行为。@Injectable 、@Input 和 @Output 是几个比较常用的装饰器。
我们必须在代码中添加元数据,这样 Angular 才知道该做什么。
5. 数据绑定 Data binding
不用框架时,我们需要自己把数据传递到 HTML 控件、响应用户的行为并更新数据。自己写这些推送/拉取逻辑的代码枯燥的、易错的,这样的代码读起来像噩梦。任何有经验的 jQuery 程序员都能够作证。
Angular 支持数据绑定,一种使模板各部分与组件各部分相互配合的机制。在模板的 HTML 中添加绑定的标记来告诉 Angular 如何连接这两部分。
正如这张图片所示,Angular 有4种数据绑定的方式。每种方式都有(数据流动的)方向:可能是从 DOM 到组件,也可能是从 组件到 DOM, 也可能是双向的。
组件 HeroListComponent 的示例模板中 有三种形式:
<@H_301_69@li>{{hero.name}}</@H_301_69@li> <@H_301_69@hero-detail [hero]="selectedHero"></@H_301_69@hero-detail> <@H_301_69@li (click)="selectHero(hero)"></@H_301_69@li>
1)插值表达式(interpolation):插值表达式 {{ hero.name }}
在 <li>
元素之间展示组件的 hero.name 属性值。
2)属性绑定(property binding):属性绑定 [hero]
将父组件 HeroListComponent 的 selectedHero 的值传递给向子组件 的 hero 属性。
3)事件绑定(event binding): 当用户点击一个 hero 的名字是,(click)
事件绑定调用了组件的 selectHero 方法。
双向绑定是第四种重要的绑定方法。它使用 [(ngModel)]
指令,在一个表达式中同时绑定属性和事件。
<@H_301_69@input [(ngModel)]="hero.name">
在双向绑定中,数据的属性值像属性绑定一样,从组件流向 input 输入框。用户输入值改变,数据从 input 输入框流回到组件,将最后的值重置为组件里的属性的值,正如事件绑定一样。
每次 JavaScript 事件循环,Angular 都会处理所有的数据绑定,从应用组件的根部到所有的子组件。
数据绑定在组件与其模板的通讯中起着重要的作用。
数据绑定在父、子组件的通讯中也起着重要的作用。
6. 指令 Directives
Angular 模板是动态的。当 Angular 渲染模板时,它会根据指令(directives)给的指示去动态地改变 DOM.
指令是带有 @Directive 装饰器的类。组件是带有模板的指令。 @Component 装饰器实际上是 拓展了模板化功能的 @Directive 装饰器。
虽然从技术上来说,组件是指令,但组件对于 Angular 应用来说非常地特别和重要,所以这个架构速览 把组件和指令分开来论述。
还有两种指令:结构指令和属性指令。
它们在元素标签中看起来像属性(attribute)那样工作。有时指令仅仅是以名字形式存在,更多地时候是作为赋值或者绑定的目标。
结构指令可以通过增加、移除、替换 DOM 元素来改变布局。
以下示例代码使用两种内置的结构指令:
src/app/hero-list.component.html (structural)
<@H_301_69@li *ngFor="let hero of heroes"></@H_301_69@li> <@H_301_69@hero-detail *ngIf="selectedHero"></@H_301_69@hero-detail>
*ngFor
告知 Angular 在每一个 <li>
标签中展示 heroes 列表中的 hero.
只有当 selectedHero 存在时,*ngIf
才会把 HeroDetail 组件包含进来。
属性指令(Attribute directives)改变已存在元素的外观或行为。在模板中,它们看起来像是普通的 HTML 属性,故得名属性指令。
ngModel
指令使用双向的数据绑定。它是典型的属性指令。 通过设置元素显示的值以及响应 change 事件,ngModel
指令改变已存在元素(尤其是 input)的行为。
src/app/hero-detail.component.html (ngModel)
<@H_301_69@input [(ngModel)]="hero.name">
Angular 还有许多指令。有些可以改变布局结构(比如 ngSwitch),有些则可以改变 DOM 元素和组件的外观(比如 ngStyle、ngClass)。
当然,我们还可以编写自定义的指令。像 HeroListComponent 的组件,可以看做是一种自定义指令。
7. 服务 Services
服务(Service)是一个很宽泛的类别,包括应用需要的任何值、函数、或者功能。
几乎任何东西都可以是服务。服务是一个典型的类,它作用有限且定义明确。它应该很好地完成特定的任务。
例如:
- logging service 日志服务
- data service 数据服务
- message bus 消息总线
- tax calculator 税收计算器
- application configuration 应用配置
关于服务,没有什么Angular特性。没有关于服务的定义,没有服务的基类,也没有地方注册服务。
然而,服务对于任何 Angular 应用都是至关重要的。组件是服务的主要使用者。
以下是一个在浏览器控制台打印日志的服务类的示例代码:
src/app/logger.service.ts (class)
content_copyexport class Logger { log(msg: any) { console.log(msg); } error(msg: any) { console.error(msg); } warn(msg: any) { console.warn(msg); } }
这个 HeroService 使用 Promise
来获取 heroes 数据。它基于 Logger 服务和另外一个处理服务器通讯的枯燥工作的 BackendService 服务。
src/app/hero.service.ts (class)
content_copyexport class @H_301_69@HeroService { private heroes: Hero[] = []; constructor( private backend: BackendService,private logger: Logger) { } getHeroes() { this.backend.getAll(Hero).@H_301_69@then( (heroes: Hero[]) => { this.logger.log(`Fetched ${heroes.length} heroes.`); this.heroes.push(...heroes); // fill cache }); @H_301_69@return @H_301_69@this.@H_301_69@heroes; } }
到处都有服务。
组件类应该是精简的。它们不从服务器中获取数据,不验证用户的输入或者直接在控制台打印日志。它们授权服务来处理这些任务。
组件的工作只是实现用户体验,不做其他的事情。它周旋于视图(由模板渲染)和应用逻辑(通常包含一些模型的概念)。 好的组件展示数据绑定的属性和方法。它授权服务处理其他重要的事情。
Angular 并不强制要求执行这些原则。它不会抱怨你用 3000 行代码写一个 kitchen sink 组件。
Angular 帮我们遵循这些原则,它使得在服务中考虑应用逻辑很容易,并且通过依赖注入(dependency injection)
使得服务在组件中可用。
8. 依赖注入 Dependency injection
依赖注入 是提供类的新的实例的一种方式,这个实例有它所需的所有依赖。大多数依赖都是服务。 Angular 使用依赖注入来为新的组件提供它们所需要的服务。
Angular 通过查看组件构造函数的参数的类型来识别组件所需要的服务。例如,HeroListComponent 的构造函数需要一个 HeroService 类型的参数。
src/app/hero-list.component.ts (constructor)
content_copyconstructor(private service: HeroService) { }
当 Angular 创建组件时,它会先向注入器( injector) 请求组件所需要的服务。
注入器维护着一个之前创建的服务实例的容器。如果被请求的服务实例不在这个容器中,注入器在返回服务给 Angular 之前,会创建这个服务的实例并把它添加到容器中。当所有请求的服务都被解决并返回后,Angular 会调用组件的构造函数并把这些服务作为参数传入。这就是依赖注入。
HeroService 注入的过程看起来像这样:
如果注入器没有 HeroService
,它怎么知道如何创建一个HeroService
呢?
简单地说,我们必须在之前就已经注册过了HeroService
的提供商( provider)。 提供商能够创建并返回服务,通常它就是服务类本身。
我们可以在模块或组件中注册providers.
通常,我们在根模块中添加providers,这样服务的同一个实例在应用的每个地方都可用。
src/app/app.module.ts (module providers)
content_copyproviders: [
BackendService,HeroService,Logger
],
或者,添加在@Component
的元数据中的providers属性中,注册在组件层级。
src/app/hero-list.component.ts (component providers)
content_copy@Component({ selector: 'hero-list',templateUrl: './hero-list.component.html',providers: [ HeroService ] })
注册在组件层级意味着在这个组件的每一个新的实例中,我们都会获得这个服务的新的实例。
关于依赖注入需要记住的几个要点:
- 依赖注入缠绕在 Angular 框架中,被到处使用。
- 注入器是主要的机制。
- 注入器维持一个存放它所创建的服务实例的容器。
- 注入器从 提供商(provider)中创建一个新的服务实例。
- 提供商是一个创建服务的配方。
- 用 注入器注册提供商。
结语:
我们已经学习了一个 Angular 应用的八个主要构建单元的基础知识:
- Modules 模块
- Components 组件
- Templates 模板
- Metadata 元数据
- Data binding 数据绑定
- Directives 指令
- Services 服务
- Dependency injection 依赖注入
这是 Angular 应用中其他一切的基础,有了这些基础就足以继续开发。但这没有包含我们所需要知道的所有内容。
以下是其他重要的 Angular 特性和服务。 大部分将涵盖在这份文档中。
- Animations 动画: 不需要深入了解动画技术或者CSS,使用 Angular 动画库给组件添加动画效果。
- Change detection 变化检测: 变化检测文档将会涵盖 Angular 如何决定组件属性值是否已改变,何时更新视图,如何使用zones拦截异步活动,以及运行变化检测策略的。
- Events 事件: 事件文档将涵盖通过订阅和发布事件的机制,如何使用组件和服务来唤起事件。
- Forms 表单: 使用 基于HTML的验证和脏检查机制,支持复杂的数据输入情形。
- HTTP: 通过 HTTP 客户端与服务端通讯以获取数据、保存数据以及来唤起 服务端的响应。
- Lifecycle hooks 生命周期钩子: 利用组件的生命周期的关键时刻,从创建到销毁,应用生命周期钩子接口。
- Pipes 管道: 使用模板中的管道来改变展示的值,从而改善用户体验。比如
currency
这个管道表达式:
price | currency:'USD':true
它将 42.33 的价格显示为 $42.33
。
Router 路由: 在客户端应用中,从一个页面导航到另一个页面,从不离开浏览器。
Testing 测试: 使用 Angular Testing Platform,当应用的各个部分与 Angular 框架交互时,进行单元测试。
Angular 5 快速入门开发非视频教程,告别Angular.js , Angular 2 , Anuglar 4吧
一、概述
尽管被称为Angular5
,实际上它只是这个诞生于2012年的前端框架的的第四个版本:
看起来差不多半年就发布一个新版本,不过实际上从重写的版本2
开始,开发接口与核心思想就稳定下来了,并基本保持着与前序版本的兼容性。
在5
这个新的版本中,Angular
团队将改进重点放在以下特性方面:
- 更易于构建渐进式
Web
应用 —— __P__rogressive __W__eb __A__pp - 使用构建优化器剔除无用代码,以获得更小的应用、更快的网络加载时间
- 使物化设计组件兼容服务端渲染
PWA
是Google
提出的一个标准,旨在让Web应用在移动终端上获得媲美原生应用的用户体验。一个PWA
应用主要利用Service Worker
和浏览器缓存来提省交互体验,它不仅可以直接部署在手机桌面,而且可以离线应用:
二、引入angular环境
Angular
推荐使用TypeScript
来开发应用,这要求使用一个在线编译器(JIT
)实时编译代码,或者在开发期采用预编译器(AOT
)提前编译代码。
为了避免这个繁琐的过程影响对Angular
框架本质的思考,我们将这些必需品进行了必要的配置和打包,以便适应在线编写和实验。现在只需要引入一个库a5-loader
就可以了。
下图是库的构成示意,其中的蓝色部件均打包在库中:
你可能注意到Angular
框架并不是蓝色的。的确,我们没有把它打包在a5-loader
中,而是让模块加载器(SystemJS
)根据应用的需要自动加载。这么做的目的,是为了让应用代码,和后续课程中采用的后端构建方法保持一致。
如果你对这个库有兴趣,可以访问github上的 http://github.com/hubwiz/a5-l... 仓库。
三、创建Angular组件
Angular
是面向组件的前端开发框架。如果你从事过C/S图形化应用的开发,应该知道组件这个词的含义。基本上,组件代表着一些具有图形界面,并且具有内在逻辑能力的程序单元。下图列出了三种用于实现乒乓切换的组件:
组件提供了很好的复用性,在一堆组件的基础上,我们使用简单的胶水代码就可以实现相当复杂的交互功能。
现在让我们来创建Angular
组件,代码相当简单:
@Component({ selector: "ez-app",template: `<h1>Hello,angular5</h1>` }) class EzComp{}
在Angular
框架中,__组件__就是指一个应用了Component
装饰器的类。Component
装饰器的作用,就是为被装饰的类附加元数据信息:
Angular
框架对应用进行编译引导时,将使用这些元数据构造视图。其中的两个元数据非常重要:
- __selector__:组件宿主元素的
CSS
选择符,声明了组件在DOM树中的渲染锚点 - __template__:组件的模板,框架将以这个模板为蓝图构建视图
四、创建Angular模块
Angular
框架的核心是组件化,同时它的设计目标是适应大型应用的开发。因此,在应用开发中引入了模块(NgModule
)的概念来组织不同的组件(及服务),一个Angular应用至少需要创建一个模块。
为了区别于JavaScript语言本身的模块概念,在本课程中将使用__NG模块__来表示一个Angular模块。
类似于组件,NG模块就是一个应用了NgModule
装饰器的类。例如,下面的代码创建了一个NG模块EzModule
:
@NgModule({ imports: [ browserModule ],declarations: [ EzComp ],bootstrap: [ EzComp ] }) class EzModule{}
同样,NgModule
装饰器用来给被装饰的类附加模块元数据,可以查看被装饰类的__annotations__
属性来观察这一结果:
NgModule
装饰器声明了一些关键的元数据,来通知框架需要载入哪些NG模块、编译哪些组件以及启动引导哪些组件:
- __imports__: 需要引入的外部NG模块
- __declarations__:本模块创建的组件,加入到这个元数据中的组件才会被编译
- __bootstrap__:声明启动引导哪个组件,必须是编译过的组件
需要强调的是,bootstrap
元数据声明的组件必须是编译过的组件:它要么属于使用imports
元数据引入的外部NG模块,要么是已经在declarations
元数据中声明的本地组件。
NG模块browserModule
定义于包@angular/platform-browser
,它是Angular跨平台战略的重要组成部分。browserModule
封装了浏览器平台下的核心功能实现,与之对应的其他平台实现还有:
- ServerModule:服务端实现
- WorkerAppModule:WebWorker实现
通常情况下开发Web应用时,我们都需要引入browserModule
这一NG模块。
五、启动Angular应用
前面课程中,我们已经创建了一个组件和一个NG模块,不过似乎只是定义了一堆的元数据,几乎没有写太多有价值的代码。
但这就是Angular框架的一个特点:__声明式开发__。这些元数据是用来向框架声明如何引导启动应用程序的重要信息。
启动代码很简单,引入platformbrowserDynamic()
工厂函数、创建平台实例、启动指定模块:
<pre>
import { platformbrowserDynamic } from "@angular/platform-browser-dynamic"
const pref = platformbrowserDynamic()
pref.bootstrapModule(EzModule)
</pre>
√ 平台对象:PlatformRef
platformbrowserDynamic()
函数返回一个PlatformRef
对象(Angular对平台的抽象),这个函数最重要的作用,在于其内部创建了一个即时(__J__ust __I__n __T__ime)编译器,可以在线实时编译NG模块和组件,这也是它被称为动态(Dynamic
)的原因:
平台对象的bootstrapModule()
方法用来启动指定的NG模块,启动的绝大部分工作,在于利用JIT编译器编译NG模块和组件,当这些编译工作都完成后,则根据启动模块的bootstrap
元信息,渲染指定的组件。
六、为什么这么复杂?
可能你已经感觉有点复杂了:只是为了写一个Hello,World
,就要写这么多代码。
事实上这些复杂性是随着Angular的发展逐步引入的,从好的一方面说,是提供给开发者的可选项逐渐增多了,适用场景变多了。
比如,在Angular2正式版之前,都没有NG模块的概念,你只要写一个组件就可以直接启动应用了。Angular团队的预期应用场景是大规模前端应用开发,因此显式的NG模块声明要求也是容易理解的。不过即使是小型的应用,由于可以只使用一个NG模块,因此这一点的复杂性增加倒也不多,只是增加了学习和运用这个新概念的成本。
另一个显而易见的复杂性,在于多平台战略的引入。Angular希望让应用可以跨越浏览器、服务器等多个平台(基本)直接运行。因此免不了抽象一个中间层出来,我们需要在应用中显式地选择相应的平台实现模块:
第三个复杂性来源于对预编译(AOT
:Ahead Of Time)的支持。在早期,Angular只有即时编译(JIT
:Just In Time),也就是说应用代码是在运行时编译的。即时编译的第一个问题是在应用中需要打包编译器代码,这增加了最终发布的应用代码的
大小;另一个问题在于编译需要时间,这增加了用户打开应用的等待时间。因此现在的Angular是同时支持JIT和AOT的,但启动JIT编译的应用,和启动AOT编译的应用,在目前需要显式地进行选择:
对于Angular而言,编译将入口NG模块定义转换为NG模块工厂(NgModuleFactory
)。对于JIT而言,这一步是隐含在bootstrapModule()
中的。而对于AOT而言,生成模块工厂就结束了,应用启动时使用bootstrapModuleFactory()
调用生成的模块工厂即可。
尽管AOT编译通常在构建阶段运用,我们可以在浏览器里模拟这个分两步的过程。
七、理解Angular的初衷
除了框架本身的功能强大导致的复杂性,Angular的另一个复杂性来源在于其高度封装的声明式API,让开发者难以揣摩、洞察框架的实现机制,因此使用起来就很心虚,一旦出现问题则难以分析排错:
不能把Angular当作黑盒来使用。
一方面原因在于,Angular是以其声明式的模板语法为核心提供API开发接口的,开发者书写的模板,经过框架相当复杂的编译处理,才渲染出最终的视图对象。如果不尝试了解从模板到视图对象这个过程究竟发生了什么,我相信你始终会有一种失控的感觉。
另一方面原因在于,Angular是一个框架,它搭好了应用程序的架子,留了一些空隙让开发者填充。如果不尽可能地了解框架的运行机制,很难充分地利用好框架。
开发Angular的出发点,是为了实现用HTML来编写用户界面,想想一个静态网页有多容易开发,你就知道这是多么好的想法:
原生HTML的问题在于,首先它需要借助于JavaScript才能实现过得去的用户交互,其次它只有那么多标签可用,难以担当开发用户界面的大任。
既然浏览器不能直接解释<ez-gauge>
这样的标签,Angular团队就引入了编译器的概念:
在送给浏览器之前,先把有扩展标签的HTML翻译成浏览器支持的原生HTML:
写在文末:相信很多学习angular的同学都看过我写的内容,想来或多或少的应该有些帮助。希望这个内容能给大家带来更多的帮助,让大家能够更快的进入angular5的世界,更多精彩内容请移步:
http://xc.hubwiz.com/course/59de66862d4f22811dc6b2f7
今天关于Angular 4官方文档(二)【数据的展现】和angular数据实时变化的分享就到这里,希望大家有所收获,若想了解更多关于Angular 2 + 折腾记 :(1)初识Angular-cli[官方脚手架]及脱坑要点、Angular 2 :初识 Angular-cli[官方脚手架] 及脱坑要点、Angular 4官方文档(一)【架构】、Angular 5 快速入门开发非视频教程,告别Angular.js , Angular 2 , Anuglar 4吧等相关知识,可以在本站进行查询。
本文标签: