angular

这篇博客详细介绍了Angular的环境安装步骤,包括`node_modules`、`package.json`、`angular.json`等核心文件的作用。文章深入讲解了Angular的目录结构,特别是`src`目录下各个部分的功能。此外,文章还重点阐述了组件的创建、生命周期、以及组件间的交互方式,如输入输出属性、事件发射和局部变量。同时,涵盖了Angular路由的基础知识,包括路由配置和路由顺序。最后,讨论了父子组件间通过服务进行通信的实现方法。

Angular环境安装

XXXX

Angular目录结构

请添加图片描述

重要的都是用*号标注出来了

目录作用
node_modulespackage.json中定义的项目中所依赖的模块,通过npm install安装之后存放的地址
angular.jsonangular的配置文件
package.json项目的配置文件,定义项目的名称、版本,以及项目中所需要的各种依赖
src存放项目的所有文件
src/app存放组件(component)、服务(service)、模块(module)
src/app/app.module.ts描述应用的组件时如何组合在一起的
src/assets存放静态资源
src/index.htmlhtml入口文件 默认生成 一般不会去修改
src/main.tsts入口文件
src/style.scss公共的全局样式

node_modules 根据package.json文件中定义的模块通过npm install被安装到这个文件夹下

src 项目的所有文件都放到这个目录下面

angular.json

app.module.ts 文件分析

请添加图片描述

这个文件告诉angular如何组装该应用

模块类描述应用的组件时如何组合在一起的

组件

angular中所有的页面都是由一个一个的组件组装而成

组件包括

  • html
  • css
  • typescript

利用angular cli中指令快速创建组件

运行 ng generate component <component-name> 命令,其中 <component-name> 是新组件的名字

通常该指令除了上述说到的三个文件还会创建一个测试文件

默认情况下,该命令会创建以下内容:

  • 一个以该组件命名的文件夹
  • 一个组件文件 <component-name>.component.ts
  • 一个模板文件 <component-name>.component.html
  • 一个 CSS 文件, <component-name>.component.css
  • 测试文件 <component-name>.component.spec.ts

组件生命周期

你可以通过实现一个或多个 Angular core 库中定义的生命周期钩子接口来响应组件或指令生命周期中的事件。这些钩子让你有机会在适当的时候对组件或指令实例进行操作,比如说你的应用可以使用生命周期钩子方法来触发组件或指令生命周期中的关键事件,以初始化新实例,需要时启动变更检测,在变更检测过程中响应更新,并在删除实例之前进行清理。

每个接口都有唯一的一个钩子方法,它们的名字是由接口名再加上 ng 前缀构成的。比如,OnInit 接口的钩子方法叫做 ngOnInit()。如果你在组件或指令类中实现了这个方法,Angular 就会在首次检查完组件或指令的输入属性后,紧接着调用它。

生命周期的顺序

钩子方法用途时机
ngOnChanges()当 Angular 设置或重新设置数据绑定的输入属性时响应。 该方法接受当前和上一属性值的 SimpleChanges 对象注意,这发生的非常频繁,所以你在这里执行的任何操作都会显著影响性能。 欲知详情,参阅本文档的使用变更检测钩子ngOnInit() 之前以及所绑定的一个或多个输入属性的值发生变化时都会调用。注意,如果你的组件没有输入,或者你使用它时没有提供任何输入,那么框架就不会调用 ngOnChanges()
ngOnInit()在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。 欲知详情,参阅本文档中的初始化组件或指令在第一轮 ngOnChanges() 完成之后调用,只调用一次
ngDoCheck()检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。 欲知详情和范例,参阅本文档中的自定义变更检测紧跟在每次执行变更检测时的 ngOnChanges() 和 首次执行变更检测时的 ngOnInit() 后调用。
ngAfterContentInit()当 Angular 把外部内容投影进组件视图或指令所在的视图之后调用。欲知详情和范例,参阅本文档中的响应内容中的变更第一次 ngDoCheck() 之后调用,只调用一次。
ngAfterContentChecked()每当 Angular 检查完被投影到组件或指令中的内容之后调用。欲知详情和范例,参阅本文档中的响应被投影内容的变更ngAfterContentInit() 和每次 ngDoCheck() 之后调用
ngAfterViewInit()当 Angular 初始化完组件视图及其子视图或包含该指令的视图之后调用。欲知详情和范例,参阅本文档中的响应视图变更第一次 ngAfterContentChecked() 之后调用,只调用一次。
ngAfterViewChecked()每当 Angular 做完组件视图和子视图或包含该指令的视图的变更检测之后调用。ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。
ngOnDestroy()每当 Angular 每次销毁指令/组件之前调用并清扫。 在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。 欲知详情,参阅本文档中的在实例销毁时进行清理在 Angular 销毁指令或组件之前立即调用。

组件之间的交互

通过输入型绑定把数据从父组件传到子组件

export class HeroChildComponent {  

@Input() hero!: Hero;

@Input('master') masterName = ''; // 括号中的master只是一个别名

}

<app-hero-child *[ngFor]="let hero of heroes"      
	[hero]="hero"
    [master]="master">    
</app-hero-child>

父组件监听子组件的事件

子组件暴露一个 EventEmitter 属性,当事件发生时,子组件利用该属性 emits(向上弹射)事件。父组件绑定到这个事件属性,并在事件发生时作出回应。

子组件的 EventEmitter 属性是一个输出属性,通常带有@Output 装饰器

import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'app-voter',
  template: `
    <h4>{{name}}</h4>
    <button (click)="vote(true)"  [disabled]="didVote">Agree</button>
    <button (click)="vote(false)" [disabled]="didVote">Disagree</button>
  `
})
export class VoterComponent {
  @Input()  name = '';
  @Output() voted = new EventEmitter<boolean>();
  didVote = false;

  vote(agreed: boolean) {
    this.voted.emit(agreed);
    this.didVote = true;
  }
}

点击按钮会触发 truefalse(布尔型有效载荷)的事件。

父组件 VoteTakerComponent 绑定了一个事件处理器(onVoted()),用来响应子组件的事件($event)并更新一个计数器。

import { Component } from '@angular/core';

@Component({
  selector: 'app-vote-taker',
  template: `
    <h2>Should mankind colonize the Universe?</h2>
    <h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3>
    <app-voter *ngFor="let voter of voters"
      [name]="voter"
      (voted)="onVoted($event)">
    </app-voter>
  `
})
export class VoteTakerComponent {
  agreed = 0;
  disagreed = 0;
  voters = ['Narco', 'Celeritas', 'Bombasto'];

  onVoted(agreed: boolean) {
    agreed ? this.agreed++ : this.disagreed++;
  }
}

父组件与子组件通过本地变量互动

父组件不能使用数据绑定来读取子组件的属性或调用子组件的方法。但可以在父组件模板里,新建一个本地变量来代表子组件,然后利用这个变量来读取子组件的属性和调用子组件的方法,如下例所示。

子组件 CountdownTimerComponent 进行倒计时,归零时发射一个导弹。startstop 方法负责控制时钟并在模板里显示倒计时的状态信息。

import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';

@Component({
  selector: 'app-countdown-parent-lv',
  template: `

  <h3>Countdown to Liftoff (via local variable)</h3>

  <button (click)="timer.start()">Start</button>
  <button (click)="timer.stop()">Stop</button>

  <div class="seconds">{{timer.seconds}}</div>

  <app-countdown-timer #timer></app-countdown-timer>
  `,
  styleUrls: ['../assets/demo.css']
})
export class CountdownLocalVarParentComponent { }

<app-countdown-timer #timer>

此处的#timer 便是父组件要使用的本地变量

父组件调用*@ViewChild()*

这个本地变量方法是个简单便利的方法。但是它也有局限性,因为父组件-子组件的连接必须全部在父组件的模板中进行。父组件本身的代码对子组件没有访问权。

如果父组件的需要读取子组件的属性值或调用子组件的方法,就不能使用本地变量方法。

当父组件需要这种访问时,可以把子组件作为 ViewChild,***注入***到父组件里面。

import { AfterViewInit, ViewChild } from '@angular/core';
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';

@Component({
  selector: 'app-countdown-parent-vc',
  template: `

  <h3>Countdown to Liftoff (via ViewChild)</h3>

  <button (click)="start()">Start</button>
  <button (click)="stop()">Stop</button>

  <div class="seconds">{{ seconds() }}</div>

  <app-countdown-timer></app-countdown-timer>
  `,
  styleUrls: ['../assets/demo.css']
})
export class CountdownViewChildParentComponent implements AfterViewInit {

  @ViewChild(CountdownTimerComponent)
  private timerComponent!: CountdownTimerComponent;

  seconds() { return 0; }

  ngAfterViewInit() {
    // Redefine `seconds()` to get from the `CountdownTimerComponent.seconds` ...
    // but wait a tick first to avoid one-time devMode
    // unidirectional-data-flow-violation error
    setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
  }

  start() { this.timerComponent.start(); }
  stop() { this.timerComponent.stop(); }
}

把子组件的视图插入到父组件类需要做一点额外的工作。

首先,你必须导入对装饰器 ViewChild 以及生命周期钩子 AfterViewInit 的引用。

接着,通过 @ViewChild 属性装饰器,将子组件 CountdownTimerComponent 注入到私有属性 timerComponent 里面。

组件元数据里就不再需要 #timer 本地变量了。而是把按钮绑定到父组件自己的 startstop 方法,使用父组件的 seconds 方法的插值来展示秒数变化。

这些方法可以直接访问被注入的计时器组件。

ngAfterViewInit() 生命周期钩子是非常重要的一步。被注入的计时器组件只有在 Angular 显示了父组件视图之后才能访问,所以它先把秒数显示为 0.

然后 Angular 会调用 ngAfterViewInit 生命周期钩子,但这时候再更新父组件视图的倒计时就已经太晚了。Angular 的单向数据流规则会阻止在同一个周期内更新父组件视图。应用在显示秒数之前会被迫再等一轮

使用 setTimeout() 来等下一轮,然后改写 seconds() 方法,这样它接下来就会从注入的这个计时器组件里获取秒数的值。

父子组件通过服务进行通讯

父组件和它的子组件共享同一个服务,利用该服务在组件家族内部实现双向通讯

父子组件之间共享数据

@Input()@Output() 为子组件提供了一种与其父组件通信的方法。 @Input() 允许父组件更新子组件中的数据。相反,@Output() 允许子组件向父组件发送数据。

配置子组件
import { Component, Input } from '@angular/core'; // First, import Input
export class ItemDetailComponent {
  @Input() item = ''; // decorate the property with @Input()
}
配置父组件
<app-item-detail [item]="currentItem"></app-item-detail>

export class AppComponent {
  currentItem = 'Television';
}

模板

文本插值

<label>Type something:
  <input #customerInput>{{customerInput.value}}
</label>

模板表达式不能引用全局命名空间中的任何东西,比如 windowdocument。它们也不能调用 console.logMath.max。 它们只能引用表达式上下文中的成员。

属性绑定

要绑定到元素的属性,请将其括在方括号 [] 内,该括号会将属性标为目标属性。目标属性就是你要对其进行赋值的 DOM 属性。例如,以下代码中的目标属性是 img 元素的 src 属性。

<img [src]="itemImageUrl">

方括号 [] 使 Angular 将等号的右侧看作动态表达式进行求值。如果不使用方括号,Angular 就会将右侧视为字符串字面量并将此属性设置为该静态值。

src/app.component.html

Angular 不允许带有 <script> 标记的 HTML,既不能用于插值也不能用于属性绑定,这样就会阻止运行 JavaScript。

事件绑定

要绑定到事件,请使用 Angular 的事件绑定语法。此语法由等号左侧括号内的目标事件名和右侧引号内的模板语句组成。在下面的示例中,目标事件名是 click ,模板语句是 onSave()

<button (click)="onSave()">Save</button>

双向数据绑定

Angular 的双向绑定语法是方括号和圆括号的组合 [()][] 进行属性绑定,() 进行事件绑定,如下所示。

<app-sizer [(size)]="fontSizePx"></app-sizer>

双向绑定语法是属性绑定和事件绑定的组合的简写形式。

导航

根据路由地址让根组件动态的挂载其他组件实现单页面应用

使用Angular cli来创建一个带有应用路由模块(AppRoutingModule)的基本Angular应用

ng new routing-app --routing --defaults
ng generate module xxx --routing

使用指令可以帮你生成xxx-routing-module.ts,并将其导入到对应的Module中

路由顺序

因为 Router 在匹配路由时使用“先到先得”策略,所以应该在不那么具体的路由前面放置更具体的路由。请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值