Angular官网学习笔记

Angular官网学习笔记

一、Angular概述

**什么是Angular:**一个基于TypeScript构建的开发平台包括:

  • 一个基于组件的框架,用于构建可伸缩的Web应用
  • 一组完美集成的库,涵盖各种功能,包括路由、表单管理、客户端-服务器通信等
  • 一套开发工具,帮助开发、构建、测试、更新代码

**优势:**让更新更容易,可以用最小的成本升级到最新的Angular版本。

1.1知识要点:
1.1.1 组件

组件是构成应用的砖块。 由三个部分组成:带有@Component()装饰器的TypeScript类、Html模板和样式文件。
@Component()装饰器的TypeScript类
@Component()装饰器会指定一下信息

  • 一个CSS选择器,用于定义如何在模板中使用组件。
  • 一个HTML模板,用于指示Angular如何渲染此组件
  • 一组可选的CSS样式,用于定义模板中HTML的外观
1.1.2 模板

每个组件都有一个HTML模板,用于声明该组件的渲染方式。使用内联或文件路径定义
Angular使用额外的语法扩展了HTML,文本插值(向组件中动态的插入动态值)支持属性绑定事件监听指令为模板添加额外功能

1.1.3 依赖注入

满足声明TypeScript类的依赖项,无需操心如何实例化。
指某个类执行其功能所需的服务或对象。依赖项注入(DI)是一种设计模式,此设计模式中类会从外部源请求依赖项而不是创建。

1.1.4 Angular CLI

常用CLI命令:
ng build:把Angular应用编译到一个输出目录中。
ng serve:构建你的应用并启动开发服务器,当有文件变化就重新构建
ng generate:基于原理图生成或修改某些文件
ng test:在指定的项目上运行单元测试。
ng e2e:构建一个Angular应用并启动开发服务器,然后运行端到端测试。

1.1.5 自带库

常用库:
Angular 路由器:高级的客户侧导航功能与基于Angular组件的路由机制。支持惰性加载、嵌套路由、自定义路径匹配规则等。
Angular 表单:同意的表单填报与验证体系。
Angular HttpClient:支持高级的客户端-服务器通讯。
Angular 动画:用于驱动基于应用状态的动画。
Angular PWA:用于构建渐进式Web应用(PWA)的工具,包括Service Worker和Web应用清单。
Angular 原理图:一些搭建脚手架、重构、升级的自动化工具。用于简化大规模应用的开发。

二、详解Angular功能

2.1 组件

创建组件

    ng g component componentName
2.1.1 组件生命周期

生命周期伴随着变更检测,Angular会检查数据绑定属性何时发生变化,并按需更新视图和组件实例。直至销毁组件实例并移除它渲染的模板
组件与指令的生命周期唯一的区别就是指令没有AfterContentInit和AfterContentChecked,因为指令没有投影,组件或指令发生变更检测时都会调用三个变更检测方法:ngDoCheck、ngAfterContentChecked、ngAfterViewChecked
生命周期顺序:
constructor():初始化类,主要用于注入依赖,在所有生命周期钩子之前执行,用于依赖注入或执行简单的数据初始化操作。
ngOnChanges():触发顺序在ngOnInit之前,当绑定有输入属性或输入属性有发生改变时触发,接收当前和上一个属性值的SimpleChanges对象,。
ngOnInit():顺序在ngOnChages()之后。第一次显示数据绑定和设置指令/组件的输入属性后触发,即使没触发ngOnChages()也会触发。只触发一次。
ngDoCheck():自定义的方法,用于检测和处理值的改变。
ngAfterContentInit():在组件内容初始化之后调用。
ngAfterContentChecked():组件每次检查内容时调用。
ngAfterViewInit():组件相应的视图初始化之后调用。
ngAfterViewCeched():组件每次检查视图时调用
ngOnDestory():指令销毁时调用,此处可以反订阅观察对象和分离事件处理器,防止内存泄漏。

1

响应视图的变更
当Angular再变更检测期间遍历视图树时,需要确保子组件中的某个变更不会尝试更改父组件中的属性,因为单向数据流的工作原理如此,这样的更改无法正常渲染。
如果需要做一个上述操作,必须触发一个新的变更检测周期。
举个栗子:

    // 子视图
    @Component({
  selector: 'app-child-view',
  template: `
    
    
  `
})
export class ChildViewComponent {
  hero = 'Magneta';
}

    //父组件template使用
    template: `
  
child view begins
child view ends
`
// 父组件的类文件 export class AfterViewComponent implements AfterViewChecked, AfterViewInit { private prevHero = ''; // Query for a VIEW child of type `ChildViewComponent` //注入子视图 @ViewChild(ChildViewComponent) viewChild!: ChildViewComponent; ngAfterViewInit() { // viewChild is set after the view has been initialized this.logIt('AfterViewInit'); this.doSomething(); } ngAfterViewChecked() { // viewChild is updated after the view has been checked if (this.prevHero === this.viewChild.hero) { this.logIt('AfterViewChecked (no change)'); } else { this.prevHero = this.viewChild.hero; this.logIt('AfterViewChecked'); this.doSomething(); } } private doSomething() { const c = this.viewChild.hero.length > 10 ? "That's a long name" : ''; if (c !== this.comment) { // Wait a tick because the component's view has already been checked this.logger.tick_then(() => this.comment = c); } } }

在视图合成完之后,就会触发AfterViewInit()AfterViewCheched()钩子,如果修改了这段代码,这个钩子就会立即修改该组件的数据绑定属性comment,测试会报错。
this.logger.tick_then(() => this.comment = c);会把日志的的更新工作推迟一个浏览器JS周期,也就会触发一个新的变更检测周期。

响应被投影内容的变更
内容投影时从组件外部导入HTML内容,并插入在组件模板中指定位置
投影内容会触发AfterContentInit()AfterContentChecked()
需要在父组件中导入

//子组件  标签时外来内容的占位符
template: `
  
projected content begins
projected content ends
`
//父组件 ` `

AfterContent和AfterView类似:不同在于子组件类型不同:

  • AfterView钩子关心的是ViewChildren,这些子组件的元素标签会出现在该组件的模板里面
  • AfterContent:钩子关心的是ContentChildren,这些子组件被Angular投影进该组件中。
    该组件的doSomething()会立即更新该组件的数据绑定属性comment,无需延迟更新以确保正确渲染。
2.2 视图封装

将组件的样式封装在组建的宿主元素中,避免影响其他应用程序。
分三种模式:
ViewEncapsulation.ShadowDom:组件的样式被包含在这个ShadowDom中,外部样式进不来,内部样式出不去
ViewEncapsulation.Emulated:样式局限在组件视图,全局样式可以进来,内部样式出不去
ViewEncapsulation.None:不使用视图封装,Angular会把CSS添加到全局样式中,全局样式可以进来,内部样式可以出去。
使用

@Component({
  selector: 'app-no-encapsulation',
  template: `
    

None

No encapsulation
`
, styles: ['h2, .none-message { color: red; }'], encapsulation: ViewEncapsulation.None, }) export class NoEncapsulationComponent { }
2.3 组件交互
2.3.1 通过输入型绑定 父传子

在子组件中添加@Input()装饰器

    // 子组件
    export class HeroChildComponent {
        @Input() hero!: Hero;
        //为子组件的属性名masterName指定一个别名master
        @Input('master') masterName = '';
    }
    //父组件
    <app-hero-child
      *ngFor="let hero of heroes"
      [hero]="hero"
      [master]="master">
    </app-hero-child>
    export class HeroParentComponent {
        heroes = HEROES;
        master = 'Master';
    }   
2.3.2 通过setter截听输入输入属性值变化

使用一个输入属性的setter,拦截父组件中值的变化

//  子组件
@Input()
  get name(): string { return this._name; }
  set name(name: string) {
    this._name = (name && name.trim()) || '';
  }
  private _name = '';

父组件与上述一致

2.3.3 通过ngOnChanges()来截听输入属性值变化

当需要监听多个、交互式输入属性时可以使用该方法

  @Input() major = 0;
  @Input() minor = 0;
  changeLog: string[] = [];

  ngOnChanges(changes: SimpleChanges) {
    const log: string[] = [];
    for (const propName in changes) {
      const changedProp = changes[propName];
      const to = JSON.stringify(changedProp.currentValue);
      if (changedProp.isFirstChange()) {
        log.push(`Initial value of ${propName} set to ${to}`);
      } else {
        const from = JSON.stringify(changedProp.previousValue);
        log.push(`${propName} changed from ${from} to ${to}`);
      }
    }
    this.changeLog.push(log.join(', '));
  }

父组件不变

2.3.4 父组件监听子组件事件

子组件暴露一个EventEmitter属性,当事件发生,子组件利用该属性emits(向上弹射)事件,父组件绑定到这个事件属性,并在事件发生时做出相应。
使用@Output()装饰器

    // 子组件
    @Input()  name = '';
    @Output() voted = new EventEmitter<boolean>();
    didVote = false;

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

    // 父组件
    template:`
      //事件属性
    `
    export class VoteTakerComponent {
    agreed = 0;
    disagreed = 0;
    voters = ['Dr IQ', 'Celeritas', 'Bombasto'];

    onVoted(agreed: boolean) {
        if (agreed) {
        this.agreed++;
        } else {
        this.disagreed++;
        }
    }
2.3.5 父子组件通过本地变量互动

在父组件模板中新建一个本地变量待变子组件,利用这个变量来读取子组件的属性和调用子组件的方法。

// 父组件
 template: `
    

Countdown to Liftoff (via local variable)

{{timer.seconds}}
//新建变量 timer
`

只可以在父组件的模板中使用该局部变量

2.3.6 通过@ViewChild()来互动

当父组件的类需要依赖于子组件类,此时不能使用本地变量方法,此时可以将子组件作为ViewChild,注入到父组件中。

// 父组件
@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里面。

2.3.7 父子组件通过服务通讯

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

// 服务
@Injectable()
export class MissionService {
// 通过RxJs来实现双向通讯

  // Observable string sources
  private missionAnnouncedSource = new Subject<string>();
  private missionConfirmedSource = new Subject<string>();

  // Observable string streams
  missionAnnounced$ = this.missionAnnouncedSource.asObservable();
  missionConfirmed$ = this.missionConfirmedSource.asObservable();

  // Service message commands
  announceMission(mission: string) {
    this.missionAnnouncedSource.next(mission);
  }

  confirmMission(astronaut: string) {
    this.missionConfirmedSource.next(astronaut);
  }
}
// 父组件
//注入服务
constructor(private missionService: MissionService) {
  //接收子组件的传值
    missionService.missionConfirmed$.subscribe(
      astronaut => {
        this.history.push(`${astronaut} confirmed the mission`);
      });
  }

  announce() {
    const mission = this.missions[this.nextMission++];
    //向子组件传值
    this.missionService.announceMission(mission);
    this.history.push(`Mission "${mission}" announced`);
    if (this.nextMission >= this.missions.length) { this.nextMission = 0; }
  }

子组件通过构造函数注入该服务,因为是子组件所以获取到的也是父组件的这个服务实例。
subscript变量在子组件ngOnDestory()钩子函数内被销毁(调用unsubscribe()),防止内存泄漏的保护措施,在父组件中不需要因为服务与父组件的生命周期相同。

2.4 组件样式

特殊的选择器::host、:host-context
:host
每个组件都会关联一个与组件选择器相匹配的元素,该元素被称为宿主元素,模板会渲染到其中。:host伪类选择器可用于创建针对宿主元素自身的样式,而不是针对宿主内部的哪些元素。
host-context
在当前组件宿主元素的祖先节点上查找CSS类,直到文档的根节点为止。只能与其他选择器组合使用。

2.5 父子指令及组件间共享数据

父组件和一个或多个子组件共享数据,使用@Input()@Output()
@Input():允许父组件更新子组件中的数据
@Output():允许子组件向父组件发送数据。

2.6 内容投影
2.6.1 单插槽内容投影

创建一个组件,在其中投影一个组件。
在组件模板中添加,,元素希望投影的内容出现在其中。
栗子:

// 子组件
  template: `
    

Single-slot content projection

`
// 父组件 <app-zippy-basic> <p>Is content projection cool?</p> </app-zippy-basic>

元素是一个占位符,它不会创建真正的DOM元素。的自定义属性被忽略。

2.6.2 多插槽内容投影

一个组件可以具有多个插槽,每一个插槽可以指定一个CSS选择器,选择器可以指定哪些内容放入该插槽。
必须指定投影出现的位置,通过使用和select属性来完成此任务。
栗子

// 子组件
    template: `
    

Multi-slot content projection

Default: Question:
`
// 父组件 <app-zippy-multislot> <p question> Is content projection cool? </p> <p>Let's learn about content projection!</p> </app-zippy-multislot>

question属性的将内容投影到带有select=[question] 属性的 元素。
如果组件包含不带select属性的元素,则该实例将接受所有与其他元素都不匹配的投影组件。

2.6.3 有条件的内容投影

后补

2.7 动态组件

当组件数量不多时可以使用ngif来判断component
栗子:

<ng-container *ngIf="radioValue === 'A'">
  <app-component-a></app-component-a>
</ng-container>
<ng-container *ngIf="radioValue === 'B'">
  <app-component-b></app-component-b>
</ng-container>
<ng-container *ngIf="radioValue === 'C'">
  <app-component-c></app-component-c>
</ng-container>

当选项很多时,或者选项通过后端决定等复杂情况下,再用ngif实现会很复杂,代码不容易维护此时需要动态组件,动态的方式来生成或删除component
可以通过ViewContainerRef可以将一个或多个视图附着到组件中的容器,也只有它才可以加载组件。不过可以将新的组件作为其兄弟(节点)的DOM元素(容器),是兄弟不是父子。
步骤:

  1. 创建一个指令 ng g d directiveName
@Directive({
  selector: '[appDynamic]'
})
export class DynamicDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}
  1. 创建容器
    通过单选框来选择动态加载组件
    锚点设置在ng-template上,
    appDynamic(使用指令)
template:
`

  
  
  


`
class类:
   /*所有的组件列表*/
   componentList = {
    componentA: ComponentAComponent,
    componentB: ComponentBComponent,
    componentC: ComponentCComponent,
  }
 /*利用viewChild获取模板元素*/
  @ViewChild(DynamicDirective) componentHost!:DynamicDirective;
  radioValue: string = 'A';

  radioValueChange() {
      /** 获取到模板元素,因为用viewChild获取dom时定义的undefined 所以需要手动as定义一下类型*/
      const viewContainerRef = this.componentHost.viewContainerRef;
      /*将模板上的元素清空*/
      viewContainerRef.clear();
      /*创建组件*/
      viewContainerRef.createComponent(this.getComponent(this.radioValue));
    }

  getComponent(radioValue: string): any {
    if (radioValue === 'A') {
      return this.componentList['componentA']
    } else if (radioValue === 'B') {
      return this.componentList['componentB']
    } else {
      return this.componentList['componentC']
    }
  }
2.8 Angular元素(Element)

Angular元素就是打包成自定义元素的Angular组件

3 模板

Angular使用额外的特性扩展了模板中的HTML语法,Angular模板只是UI的一个片段,因此不包含等元素。并且不支持模板中使用

你可能感兴趣的:(#,Angular,前端,angular.js,学习)