Angular 脚手架的脚本是 TypeScript
使用 node.js
配置 Angular-cli
的环境。
命令:
npm install -g @angular/cli
创建新的项目
ng new my-app
ng new
命令执行时,会提示要把哪些特性添加到应用中,按 Enter
或 Return
键可以接收默认值。
运行项目
cd my-app
ng serve --open
目录结构简介
# 一级目录介绍
node_modules:
第三方依赖包存放目录
e2e
端到端的测试目录 用来做自动测试的
src
应用源代码目录
angular.json
Angular命令行工具的配置文件。后期可能会去修改它,引一些其他的第三方的包
karma.conf.js
karma是单元测试的执行器,karma.conf.js是karma的配置文件
package.json
是一个标准的npm工具的配置文件,这个文件里面列出了该应用程序所使用的第三方依赖包。实际上我们在新建项目的时候,等了半天就是在下载第三方依赖包。下载完成后会放在node_modules这个目录中,后期我们可能会修改这个文件。
README.md
说明文件
tslint.json
是tslint的配置文件,用来定义TypeScript代码质量检查的规则,不用管它
# src目录介绍
app目录
包含应用的组件和模块
assets目录
资源目录,存储静态资源的 比如图片
environments目录
环境配置。Angular是支持多环境开发的,我们可以在不同的环境下(开发环境,测试环境,生产环境)共用一套代码,主要用来配置环境的
index.html
整个应用的根html,程序启动就是访问这个页面
main.ts
整个项目的入口点,Angular通过这个文件来启动项目
polyfills.ts
主要是用来导入一些必要库,为了让Angular能正常运行在老版本下
styles.css
主要是放一些全局的样式
在项目的命令行输入创建一个组件 LeftMenu
的命令:
ng generate component LeftMenu
app
目录下会自动生成组件的相关文件:
left-menu.component.css
当前组件的css
left-menu.component.html
当前组件的html
left-menu.component.spec.ts
当前组件的测试文件
left-menu.component.ts
当前组件的脚本
一个组件被创建好之后,它的脚本代码中会有一些默认生成的内容:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-left-menu',
// 当前组建类结构的路径
templateUrl: './left-menu.component.html',
// 当前组建类样式的路径
styleUrls: ['./left-menu.component.css']
})
export class LeftMenuComponent implements OnInit {
// 导出当前组建类,在类中定义组件的应用逻辑,为视图提供支持。 组件通过一些由属性和方法组成的 API 与视图交互
// 组建类的构造函数
constructor() { }
// 组建类初始化的生命周期钩子函数
ngOnInit() {
}
}
@Component 详解
@Component
是一个装饰器,绘指出紧随其后的这个类是个组建类(当前示例中为 LeftMenuComponent
),并规定其元数据。
如果一个类没有被 @Component
装饰器修饰,那么这个类就是个普通的类,不代表任意组件,你只能使用这个类完成基础的 JS 逻辑交互。
CSS
选择器,他会告诉 Angular
,一旦在模板 HTML 中找到了这个选择器对应的标签,就创建并插入该组件的一个实例。 比如,如果应用的 HTML
中包含
,Angular
就会在这些标签中插入一个 ``LeftMenuComponent` 实例的视图。HTML
模板文件相对于这个组件文件的地址。 另外,你还可以用 template
属性的值来提供内联的 HTML
模板。 这个模板定义了该组件的宿主视图。一个组件被创建之后可以在任意组件中直接使用组件的 @Component
中的 selector
调用组件
// left-menu.component.ts
import { Component } from '@angular/core'
@Component({
selector: 'app-left-menu',
templateUrl: './left-menu.component.html'
})
export class LeftMenuComponent {
// 方式一
title = '菜单栏';
list = ['孙权', '周瑜', '鲁肃', '吕蒙', '陆逊'];
className = 'active';
// 方式二
flag: boolean;
constructor() {
this.flag = true;
}
ngOnInit() {}
onBtnClick() {
this.flag = false;
}
}
通过不同方式声明的组件的数据,意义和使用方式都是相同的。
Angular
支持双向数据绑定,这是一种对模板中的各个部件与组件中的各个部件进行协调的机制。 往模板 HTML 中添加绑定标记可以告诉 Angular 该如何连接它们。
1. 插值表达式
<h1>{{ title }}h1>
<h2>{{ flag ? '海子' : '三体' }}h2>
<h3>{{ flag && '仙逆' }}h3>
2. 属性中动态的绑定数据
<h1 [class]="className">{{ title }}h1>
<input [value]="list[0]" />
3. 将组建类的函数绑定给组件中的某个事件
<button (click)="onBtnClick()">点我button>
4. 双向数据绑定
双向数据绑定主要应用于模板驱动表单中。Angular
提供了 ngModel
指令实现数据双向绑定。
但是要使用 ngModel
需要在导入 FormsModule
包:
// app.module.ts
...
import {FormsModule} from '@angular/forms';
@NgModule({
...
imports: [
...
FormsModule
]
...
})
export class AppModule { }
<input [(ngModel)]="title" />
我们可以把管道理解为数据过滤器。
Angular
的管道可以让你在模板中声明显示值的转换逻辑。 带有 @Pipe
装饰器的类中会定义一个转换函数,用来把输入值转换成供视图显示用的输出值。
Angular
自带了很多管道,比如 date
管道和 currency
管道。
你可以把管道串联起来,把一个管道函数的输出送给另一个管道函数进行转换。 管道还能接收一些参数,来控制它该如何进行转换。比如,你可以把要使用的日期格式传给 date 管道:
<p>Today is {{1566635864356 | date}}p>
<p>The date is {{1566635864356 | date:'fullDate'}}p>
<p>The time is {{1566635864356 | date:'shortTime'}}p>
Angular
的模板是动态的。当 Angular
渲染它们的时候,会根据指令给出的指示对 DOM
进行转换。 指令就是一个带有 @Directive()
装饰器的类。
组件从技术角度上说就是一个指令,但是由于组件对 Angular 应用来说非常独特、非常重要,因此 Angular
专门定义了 @Component()
装饰器,它使用一些面向模板的特性扩展了 @Directive()
装饰器。
除组件外,还有两种指令:结构型指令(ngFor,ngIf) 和 属性型指令( [(ngModel)] 就是一个属性型指令)。 Angular
本身定义了一系列这两种类型的指令,你也可以使用 @Directive()
装饰器来定义自己的指令。
像组件一样,指令的元数据把它所装饰的指令类和一个 selector
关联起来,selector
用来把该指令插入到 HTML 中。 在模板中,指令通常作为属性出现在元素标签上,可能仅仅作为名字出现,也可能作为赋值目标或绑定目标出现。
结构型指令通过添加、移除或替换 DOM 元素来修改布局。 这个范例模板使用了两个内置的结构型指令来为要渲染的视图添加程序逻辑
简单示例:
1. ngFor
<ul>
<li *ngFor="let item of list">{{ item }}li>
ul>
2. ngIf
<h1 *ngIf="flag">{{ title }}h1>
Angular 提供了 @Input,@Output 和 @ViewChild 来实现组件之间的数据通信
示例需求,实现父级组件 Father
和 子级组件 Kid
之间的数据交互。
// father.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-father',
templateUrl: './father.component.html',
styleUrls: ['./father.component.css']
})
export class FatherComponent implements OnInit {
tip = '我是来自父组件的数据';
constructor() { }
// 父组件中的函数
fnOfFather() {
/*
*****
当前组件调用该函数,this 为当前组建类
如果将当前函数传递给子组件,在子组件中调,this 指向自组建类
*/
console.log('fnOfFather');
console.log(this.title);
}
ngOnInit() {
}
}
<app-kid [msg]="tip" [fn]="fnOfFather">app-kid>
子组件中要接收
// kid.component.ts
// 导入 @Input 装饰器
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-kid-content',
templateUrl: './kid-content.component.html',
styleUrls: ['./kid-content.component.css']
})
export class KidComponent implements OnInit {
// 通过 @Input 装饰器接收数据,接收之后可以直接加以使用
@Input()msg: string;
@Input()fn: any;
constructor() { }
ngOnInit() {
this.fn(); // 调用 fn 会执行 Father 中的 fnOfFather 函数
}
}
<h1>{{ msg }}h1>
点击
组件中的某个按钮,向父级组件传递一个数据。
<button (click)="toFather()">传值button>
// kid.component.ts 导入传参需要使用的 @Output 装饰器和 EventEmitter 类
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'kid-menu',
templateUrl: './kid.component.html',
styleUrls: ['./kid.component.css']
})
export class KidComponent implements OnInit {
// 创建一个 EventEmitter 对象
// 并规定使用这个对象的 emit 函数导出事件时,参数为一个对象
// 将这个对象定义成当前类的私有属性,并使用 @Output 装饰器修饰
@Output() private outer = new EventEmitter<object>();
list = ['孙权', '周瑜', '鲁肃', '吕蒙', '陆逊'];
ngOnInit() {
this.fn();
}
toFather() {
this.outer.emit(this.list);
}
}
父组件调用子组件时,需要预留接口接受子组件通过事件传递的参数:
<app-kid (outer)="fn($event)">app-kid>
// father.component.ts
// ...
export class CenterContentComponent implements OnInit {
fn(param) {
// 当子组件中的传值按钮被点击时,父组件中的 fn 函数会被自动调用
// param 是子组件触发事件的同事传递过来的参数
console.log(param);
}
ngOnInit() {
}
}