Angular 2 学习笔记

2023-12-13 20:41:01

Angular2 是一个基于?TypeScript?构建的开发平台。

Angular2是组件化、模块化的,我们开发一个Angular2的应用,也应该将系统设计成一个个组件,而且一个组件有可能包含多个子组件。就好比html是一个树形结构的DOM,一个Angular2应用也应该是一个树形结构的组件树。

Angular 2 应用主要由以下 几个部分组成:

  • 1、模块 (Modules):用于将应用程序分解为逻辑代码片段。 每段代码或模块都旨在执行单个任务。
  • 2、组件 (Components):可用于将模块组合在一起。
  • 3、模板 (Templates):用于定义Angular应用的视图。
  • 4、元数据 (Metadata):可用于向Angular类添加更多数据。
  • 5、数据绑定 (Data Binding)
  • 6、指令 (Directives)
  • 7、服务 (Services):用于创建可在整个应用中共享的组件。
  • 8、依赖注入 (Dependency Injection)
  • 9、路由(Route):建立URL路径和组件之间的对应关系,根据不同URL路径匹配出相应的组件并渲染。

工作流程:模板 (Templates)是由 Angular 扩展的 HTML 语法组成,组件 (Components)类用来管理这些模板,应用逻辑部分通过服务 (Services)来完成,然后在模块 (Modules)中打包服务(Services)与组件(Components),最后通过引导根模块 (Root Modules)来启动应用。?如果你的应用有几个Modules(模块)需要交互或跳转,就还需要定义路由。

一、模块 (Modules)

在Angular2中一个Module指的是使用@NgModule修饰的class。@NgModule利用一个元数据对象来告诉Angular如何去编译和运行代码。可以将组件、服务、指令、方法、管道等封装成一个模块,并且可以将它们的访问权限声明为公有,以使外部模块的组件可以访问和使用到它们。

ng2模块

模块是用来组织应用的,Angular2将许多常用功能都分配到一个个的模块中,如:

  • ApplicationModule:封装一些启动相关的工具
  • CommonModule:封装一些常用的内置指令和内置管道等
  • BrowserModule:封装在浏览器平台运行时的一些工具库,同时将CommonModule和ApplicationModule打包导出,所以通常在使用时引入BrowserModule就可以了
  • FormsModule和ReactiveFormsModule:封装表单相关的组件指令等
  • RouterModule:封装路由相关的组件指令等
  • HttpModule:封装网络请求相关的服务等

在使用模块前,需导入相关模块包:

  • @angular/core:存放核心代码,如变化监测机制、依赖注入机制、渲染等,核心功能的实现、装饰器(@Component、@Directive等)也会存放到这个模块。
  • @angular/common:存放一些常用的内置指令和内置管道等。
  • @angular/forms:存放表单相关的内置组件及内置指令等。
  • @angular/http:存放网络请求相关的服务等。
  • @angular/router:存放路由相关的组件和指令等。
  • @angular/platform-<x>:存放的是引导启动相关的工具。angular支持在多个平台下运行,不同的平台都有对应的启动工具,这些启动工具会被封装到不同的模块里,如浏览器的启动工具存放在@angular/platform-browser下,服务端渲染的启动工具存放在@angular/platform-server下。

Angular 模块是一个带有 @NgModule 装饰器的类,它接收一个用来描述模块属性的元数据对象

@NgModule的元数据属性

  • declarations:模块内部Components/Directives/Pipes的列表,声明一下这个模块内部成员
  • providers:指定应用程序的根级别需要使用的service。(Angular2中没有模块级别的service,所有在NgModule中声明的Provider都是注册在根级别的Dependency Injector中)
  • imports导入其他module,其它module暴露的出的Components、Directives、Pipes等可以在本module的组件中被使用。比如导入CommonModule后就可以使用NgIf、NgFor等指令。
  • exports:用来控制将哪些内部成员暴露给外部使用。导入一个module并不意味着会自动导入这个module内部导入的module所暴露出的公共成员。除非导入的这个module把它内部导入的module写到exports中。
  • bootstrap:通常是app启动的根组件一般只有一个component。bootstrap中的组件会自动被放入到entryComponents中。
  • entryCompoenents: 不会在模板中被引用到的组件。这个属性一般情况下只有ng自己使用,一般是bootstrap组件或者路由组件,ng会自动把bootstrap、路由组件放入其中。 除非不通过路由动态将component加入到dom中,否则不会用到这个属性。

一个最简单的根模块:

//app/app.module.ts 文件:


import { NgModule }      from '@angular/core'; //将NgModule,BrowserModule导入到本模块,供本模块使用。
import { BrowserModule } from '@angular/platform-browser';


@NgModule({
  imports:      [ BrowserModule ],
  providers:    [ Logger ],
  declarations: [ AppComponent ],
  exports:      [ AppComponent ],
  bootstrap:    [ AppComponent ]
})

export class AppModule { }//定义根模块类

每个 Angular 应用都至少有一个 NgModule 类,也就是根模块,它习惯上命名为 AppModule,并位于一个名叫 app.module.ts 的文件中。引导这个根模块就可以启动你的应用。

自定义模块根模块

职责区别主要有以下两点:

  1. 根模块的目的在于启动app,功能模块的目的在于扩展app
  2. 自定义模块可以根据需要暴露或隐藏具体的实现

随着应用的扩大,所有的事情都在一个模块中完成难免会变乱,我们就会想着把统分为多个模块,每个模块都只做各自的事情而互不干扰,用根模块来引导程序并管理所有子模块即通过路由定向以及为它们提供全局配置与服务实例。

  1. 根模块负责全局的路由。
  2. 核心模块负责全局服务,也可以定义一些只在根模块中使用的组件等,并只能由根模块引入一次,不再导出。
  3. 共享模块不做服务的提供,而是定义全局共享的组件等,以及帮助子模块导入系统模块,让子模块只需要导入此共享模块就够了。
  4. 子模块内部可以细分自己的子路由到具体的子组件,以及提供自己的服务等。
  5. 除了页面入口模块(即除了根模块外的具体业务模块)之外的其他子模块均考虑写成惰性加载的模块,以提升页面引导的速度减少性能浪费。
  6. 当需要一个比较通用的全局服务时,可以将其加入CoreModule,也可以再创建一个仅被根模块引入的特性模块。进一步的,甚至可以将此模块发布到npm,这就需要更强的编码能力和技术积累了。

模块的目录和目录结构

一般如下:

  1. 每个模块一个应该单独的文件夹
  2. 模块内功能相关或相近
  3. 每个模块最好都有单独的路由定义

二、组件 (Components)?/? 指令(Directive)

组件是一个可重用的单元,包含模版、样式,还有数据、事件等交互逻辑。

创建 Angular 组件的方法有三步:

  • 从 @angular/core 中引入 Component 修饰器
  • 建立一个普通的类,并用 @Component 修饰它
  • 在 @Component 中,设置 selector?自定义标签,以及 template?模板

@Component的元数据属性

  • animations?- 该组件的动画列表
  • changeDetection?- ?该组件使用的更改检测策略
  • encapsulation?- 此组件使用的样式封装策略
  • entryComponents?- 动态插入到此组件的视图中的组件列表
  • exportAs?- 组件实例在模板中导出的名称
  • host?- 类属性映射到事件,属性和属性的主机元素绑定
  • inputs?-将属性名列表列为数据绑定作为组件输入
  • interpolation?- 此组件模板中使用的自定义插值标记
  • moduleId?- 定义此组件的文件的ES / CommonJS模块ID
  • outputs?- 列出其他可以订阅的输出事件的类属性名称
  • providers?- 该组件及其子组件可用的提供程序列表
  • queries?- 配置可以注入组件的查询
  • selector?-css选择器,用于标识模板中的此组件
  • styleUrls?- 要应用于此组件视图的样式表的URL列表
  • styles?- 内联定义的样式应用于此组件的视图
  • template?-视图的内联定义模板
  • templateUrl?- url到包含视图模板的外部文件
  • viewProviders?- 此组件及其查看子项可用的提供程序列表

一个简单的组件:

import { Component } from '@angular/core';
@Component ({ 
   selector: 'my-app', 
   templateUrl: 'app/app.component.html' 
}) 
export class AppComponent { //类,它由属性和方法组成。
   appTitle: string = 'Welcome';
} 

使用@Component的Decorator(装饰器)定义一个名叫AppComponent 的 组件,组件内有一个变量。

在@Component中,定义了这个组件使用的模板、样式,和在它的html标签 ?。

在一个Angular2的应用中,组件是一个属性结构,就好像html的DOM树一样,每个Angular2应用都有一个根组件,然后它会有一个个的子组件。得到的是一个组件树。每个组件(除了根组件)都有一个父组件,每个组件定义中“selector”的值,对应父组件中的一个html标签。

Directive(指令)跟组件类似,工作原理也类似。它跟组件一样,也是定义一种可重用的结构,添加用户交互。实际上,在Angular2中,Component继承自Directive接口,并提供了模板相关的属性和功能。

指令作用在已有的DOM元素上,分两种:
结构指令能够添加、修改或删除DOM。

<button *ngIf="canEdit">编辑</button>

属性指令用来改变元素的外观或行为。

<span [ngStyle]="setStyles()">{{contact.name}}</span>

组件间通信

在Angular中,有多种方法可以实现父子组件通信。

以下是几种常用的方法:

  • ?输入属性(Input Properties)
  • ?输出属性(Output Properties)
  • ?服务(Services)
  • ?ViewChild与ContentChild

1. 输入属性(Input Properties)

输入属性是一种用于从父组件向子组件传递数据的方法。通过使用@Input()装饰器,我们可以在子组件中定义一个公共属性来接收来自父组件的数据。

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

@Component({
  selector: 'app-child',
  template: '<p>{{ message }}</p>'
})
export class ChildComponent {
  @Input() message: string;
}

在上述代码中,我们使用@Input()装饰器来定义了一个名为message的输入属性。在子组件的模板中,我们使用插值表达式{{ message }}来展示接收到的消息。

2. 输出属性(Output Properties)

输出属性允许子组件向父组件传递信息。通过使用事件触发器和@Output()装饰器,我们可以在子组件中定义一个事件,并在适当的时候将数据作为事件参数发送给父组件。

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

@Component({
  selector: 'app-child',
  template: '<button (click)="sendMessage()">Send Message</button>'
})
export class ChildComponent {
  @Output() messageEvent = new EventEmitter<string>();

  sendMessage() {
    this.messageEvent.emit('Hello from child component');
  }
}

在上述代码中,我们定义了一个名为messageEvent的输出属性,并使用EventEmitter来创建一个新的事件。在子组件中,当用户点击按钮时,我们通过调用sendMessage()方法并使用emit()方法来触发messageEvent事件,并将一个字符串作为参数传递给父组件。

3. 服务(Services)

服务是一种共享数据和状态的有效方式。通过创建一个共享的服务,我们可以在任何组件之间传递数据和共享状态。组件可以通过依赖注入服务,并使用服务提供的方法和属性进行通信。

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

@Injectable()
export class DataService {
  private message: string;

  setMessage(message: string) {
    this.message = message;
  }

  getMessage() {
    return this.message;
  }
}

在上述代码中,我们创建了一个名为DataService的服务,并在其中定义了一个私有的message属性和相应的设置和获取方法。通过在需要访问该数据的组件中注入DataService,我们可以在组件之间共享数据。

4. ViewChild与ContentChild

通过使用ViewChild和ContentChild装饰器,我们可以在父组件中获取对子组件的引用,并直接调用子组件的方法或访问其属性。这种方法适用于需要直接与子组件进行交互的情况。

import { Component, ViewChild } from '@angular/core';
import { ChildComponent } from './child.component';

@Component({
  selector: 'app-parent',
  template: `
    <app-child></app-child>
    <button (click)="callChildMethod()">Call Child Method</button>
  `
})
export class ParentComponent {
  @ViewChild(ChildComponent) childComponent: ChildComponent;

  callChildMethod() {
    this.childComponent.childMethod();
  }
}

在上述代码中,我们使用@ViewChild()装饰器来获取对ChildComponent的引用,并将其赋值给childComponent属性。然后,在父组件的模板中,我们使用一个按钮来触发callChildMethod()方法,该方法会调用子组件中的childMethod()方法。
?

三、元数据(Metadata)

元数据就是在定义模块、组件、服务的时候,Decorator(装饰器)方法里面的参数内容,例如一个AppComponent的元数据,就是 @Component?? 里面的参数,如下:

{
   selector : 'mylist',
   template : '<h2>菜鸟教程</h2>'
   directives : [ComponentDetails]
}

在Angular2中,Decorator(装饰器)被大量使用,当我们定义模板、组件、服务、指令时,都是使用Decorator来定义。顾名思义,Decorator(装饰器)就是在一个类上面添加一些额外属性或方法

举个例子,根组件AppComponent,在定义它的时候,通过 ??@Component?? 才能把它定义成一个Angular的组件。然后我们在这个元数据里面,设置了这个组件对应的selector,模板和样式。

这样Angular框架在解析这个类的时候,就会按照组件的规则去解析并初始化。

当在一个页面里面遇到这个selector设置的标签时,就会初始化这个组件,渲染模板生成html显示到对应的标签里面,并应用样式。

四、服务(Services)

在Angular2中,服务是一个很宽泛的定义,任何的类都可以被定义成服务,这个类中可以包含一些业务方法,可以包含环境配置变量。Angular2并没有对服务的定义做任何的规则限制。

一个简单的服务:

export class loggerServices {
  log(msg: any)   { console.log(msg); }
  error(msg: any) { console.error(msg); }
  warn(msg: any)  { console.warn(msg); }
}

我们只需要定义一个class,并把它export就可以。但是,一般我们都是结合依赖注入来使用服务。

五、依赖注入 (Dependency Injection)

在NG2中,依赖注入 (Dependency Injection)主要是用于管理service实例的注入。

在上面讲的service中,我们创建了一个loggerServices?,在传统的用法中,我们都是在需要用它的地方,手动创建这个类的实例,然后通过实例调用它的方法或属性。

Angular它借用了java等语言中某些容器库的概念,将所有service实例的创建都由容器来完成,当一个service需要引用另一个service的时候,不是自己创建service实例,而是从容器中获取那个service的实例。

要使用依赖注入的功能,首先我们的service必须由一个装饰器 ??@Injectable?? 来定义:

@Injectable?? 
export class loggerServices {
...
}

然后,在Component中需要加一个providers,也就是服务的创建者:

import { Component } from '@angular/core';
@Component ({ 
   selector: 'my-app', 
   templateUrl: 'app/app.component.html' ,
   providers: [ loggerServices ]//注入loggerServices ,当前组件及子组件共用一个loggerServices 实例
}) 
export class AppComponent { //类,它由属性和方法组成。
   appTitle: string = 'Welcome';

  constructor(private theService: loggerServices ) { }//Angular 能通过查看构造函数的参数类型,来得知组件需要哪些服务。
} 

这样,这个服务就可以在AppComponent?中自动注入了。它的构造函数中有一个参数theService,类型是loggerServices,Angular在创建这个Component的时候,会从容器里面查找loggerServices类的实例,如果有就用这个实例去初始化AppComponent?对象;如果没有就先新建一个,再初始化。这个过程,就是Angular的依赖注入。

有关依赖注入,需要注意的一点就是依赖注入的作用范围。Angular2的依赖注入是一个树形的结构,就好像组件树一样。

在上面的例子中,我们在 ?AppComponent 的 providers?? 里面设置了 ??loggerServices ,? 也就是说,在AppComponent 这个节点,以及它所有的子节点的组件上,loggerServices 类的实例是共用的,它们都共享一个服务实例。但是,在这个AppComponent 的父组件里,它如果也想注入loggerServices 来使用的话,就没有办法从容器中获得,除非在它的父组件中的providers中也添加了这个服务。

六、数据绑定(Data binding)

Angular2的数据更新检测是在每个组件上有一个检测器。这样,就算应用中有再多绑定的变量,当有一个数据修改后,也只是对应的那个组件的检测器被触发,来检查它以及它所有的子组件的数据修改。

数据绑定的语法有四种形式:

1、插值 (单向): 在 HTML 标签中显示组件值。

<h3>
{{title}}
<img src="{{ImageUrl}}">
</h3>

2、属性绑定(单向): 把元素的属性设置为组件中属性的值。

<img [src]="userImageUrl">

3、事件绑定(单向): 在组件方法名被点击时触发。

<button (click)="onSave()">保存</button>

4、双向绑定: 使用Angular里的NgModel指令可以更便捷的进行双向绑定。

<input [value]="currentUser.firstName"
       (input)="currentUser.firstName=$event.target.value" >

双向绑定就是用户在页面上修改这个值时,这个值就会直接反馈到组件中。同样,如果在组件中通过某种方式修改了这个值,页面上,也会显示最新的值。

对于上面的 ?[]? ?()???? 两种类型的绑定,可以理解成’输入’和’输出’。 ??

七、路由

Angular2中建立路由的5个步骤:

安装@angular/router

首先第一件事,我们需要安装 Angular Router。你可以通过运行以下命令进行安装.

npm i --save @angular/router

以上命令执行后,将会自动下载 @angular/router 模块到 node_modules 文件夹中。

1、路由配置

Angular2中路由要解决的是URL与页面的对应关系。在Angular2中页面是由组件组成的(Angular2中的根模块对应的根组件就表示了整个应用,应用也可以说成只有一个组件,也就是一个页面,这就是单页面应用),所以路由解决的是URL与组件的对应关系

这个需要一个变量,它用来表示url与组件的对应关系,Angular2中把这个数据叫作“路由配置”。

注意:path中不能用斜线/开头。

import { Routes } from '@angular/router';

import { component_help } from './help/componentA';//自定义Component 
import { component_index } from './home-page/componentB';//自定义Component 



export const routes: Routes = [
    {
        path: '', //  empty path匹配各级路由的默认路径。
        component: component_index
    },
    {
    path: '**',// **代表该路由是一个通配符路径。如果当前URL无法匹配上我们配置过的任何一个路由中的路径,路由器就会匹配上这一个。当需要显示404页面或者重定向到其它路由时,该特性非常有用。
      component: component_help
    },
]

2、在根模块中创建根路由模块。

根路由模块包含了路由所需的使用服务,它以路由配置为参数,调用RouterModule.forRoot() app.module.ts

// 在根模块中导入路由配置
import { rootRouterConfig } from './app.routes'; // 注意没有.ts


// 需要调用RouterModule.forRoot()方法
import { RouterModule } from '@angular/router';


/* 
创建路由模块

根路由模块默认提供的路由策略为PathLocationStrategy(另外一个是HashLocationStrategy)。
PathLocationStrategy路由策略需要一个base路径,设置base路径有2种方式,最简单的是在index.html中设置<base>
路由策略:pathLocationStrategy ->const rootRouterModule: ModuleWithProviders = RouterModule.forRoot(rootRouterConfig); 
路由策略:HashLocationStrategy-> const rootRouterModule: ModuleWithProviders = RouterModule.forRoot(rootRouterConfig, {useHash: true});  
*/
const rootRouterModule: ModuleWithProviders = RouterModule.forRoot(rootRouterConfig);



@NgModule({ 
  declarations: [],
  imports: [rootRouterModule], // 导入路由模块
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

3、添加组件存放的区域,使用RouterOutlet指令

这个指令就是在页面上预留一个区域,当url改变时匹配路由配置中的path,匹配成功后就将与path对应的component加载到这个区域里。

一般情况下,这个指令是放在根组件中。 app.conponent.html

<div class="container">
    <router-outlet></router-outlet>
</div>

到目前为止,一个简单的路由就基本配置完成了,只要在url中输入正确的path,就能显示对应的组件。

4、路由跳转

路由跳转就是为了不要手动改变url,最常见的就是点击事件改变url,实现路由跳转,显示组件。

路由跳转有两种方式:指令跳转和代码跳转。

指令跳转指的是使用RouterlLink指令,该指令接收一个链接参数数组,当事件被触发时,数组中的所有元素与路由配置中的path接收的数组中的元素进行一一对比,全都相等时得以匹配。

RouterLink有一个好的方法,可以指定routerLinkActive = “className”,也就是当RouterLink被激活时可以给相应的html元素添加clas类。神奇地是,routerLinkActive可以作用于父级元素。

<div id="navbar" class="collapse navbar-collapse">
    <ul class="nav navbar-nav">
        <li><a [routerLink]="['/help']" routerLinkActive="active">allPeople</a></li>
        <li><a [routerLink]="['/first-come']" routerLinkActive="active">firstCome</a></li>
        <li><a [routerLink]="['/last-leave']" routerLinkActive="active">lastLeave</a></li>
        <li><a [routerLink]="['/form']" routerLinkActive="active">newPerson</a></li>
        <li><a [routerLink]="['/bugManage']" routerLinkActive="active">bugManage</a></li>
    </ul>
</div>

需要特别注意的是:RouterLink指令仅响应click事件,要想响应其他(比如延迟显示)事件,就要用到代码跳转。

文章来源:https://blog.csdn.net/qianqianyixiao1/article/details/134970668
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。