Angular4+杂感及知识点总结

前言

接触Angular还有两个月这样就要整整满一年了,从一个.net程序员变成了一个前端,在了解了前端的东西之后爱上了前端,怎么说呢,其实对于前端这种侧重数据展示的开发自己本身就喜欢,奈何没有引路人,各种原因之下最后进了.net的坑。好了,废话不多说,写下这篇博客其实也是对自己使用angular将近一年的总结,怕自己以后有些东西忘记,包含了环境安装,新建项目,组件,路由,指令,事件,管道,服务和代理等。

Angular及其它框架介绍

Angular是目前前端的三大框架,由谷歌进行开发维护,另外两个框架是React和Vue。React由Facebook 进行开发维护,Vue主要由国内开发者尤雨溪进行开发维护。

从时间上看,Angular最早版本已经是十年之前,2009年谷歌发布Angular1,2016年9月Angular2正式版发布,Angular3由于在开发过程中出现问题(Router模块)被放弃,直接奔向4,2017年3月Angular4正式版发布,之后还有5、6、7,改动都不是很大,2019年5月28日,Angular8正式发布。其实这些版本中只有1.x是最特别的,因为1.x与2以及之后的版本完全不一样,可以说基本没什么关系了,在语法上就不一样,以前在学校的时候就接触1.x,在我那时看来,1.x的语法简直晦涩难懂,学习成本比较高。React是2013年5月正式发布,2015年逐渐稳定,由于对React并不是很了解,所以就不过多介绍了。Vue最早是2014年2月在GitHub开源,但是1.0版是在2015年10月发布的,2016年8月发布2.0版,预计今年下半年发布3.0,据作者说,3.0将会进行重写。

在使用情况上,从国内来看,虽然Angular是最早发布的,但相对于其他的两个框架,现在Angular在国内的使用率并不高,甚至有些人断言Angular过时了,(嗯~~持这种观点的人我不想和他交朋友,因为我觉得其实Angualr挺好,就是国内生态不行,哈哈),使用率最高的是Vue,其次是React,造成这个的原因有一部分是因为Angular1到Angular2的跨度太大,导致开发人员学习成本的提高,迫使他们放弃,转向其他框架;另一部分原因我个人认为是在灵活性上以及上手难度上,相对于Angular来说,Vue十分的小巧与灵活,上手也比较快,由于Vue的小巧与灵活性,我更倾向于Vue是一个库或者模板引擎,当然,进行一个项目,前端上仅仅使用Vue是不够的,通常使用Vue还会使用其他的第三方东西,比如vue-router、vuex、axios,这些统称Vue全家桶。其实所谓的全家桶Angular中也有,但是Angular将他们都封装包含在一起了,使得这些东西成为Angular的一部分。就国外来说,其实Angular的使用率是极高的,远远将Vue甩在身后,当然,毕竟谷歌在国外的影响力可不是一点两点。虽然在国内Angular的状况有点惨,但不能否认的是,在构建大型项目上,Angular比Vue更为适合。叨叨了这么多,下面开始正题了。

技术及工具准备

  • 1、安装node.js
    关于node.js,node其实是服务端的东西,安装它其实主要是使用它的npm工具包,又引入了新东西npm,它是node package manager的缩写,是一个包管理工具,安装了node之后,我们可以通过npm命令下载需要的包。嗯~~为什么需要npm,因为现在网络上有太多太多的包以及工具,有时候我们需要的时候得一个一个的去找,找到了才能下载,这样会很麻烦和不方便,而npm则帮我们把这些包管理起来,需要的时候直接运行npm install 命令就可以下载需要的包到我们的项目中。
  • 2、安装Typescript
    打开cmd运行命令:npm install -g typescript
    因为angular是用TypeScript写的,所以得安装。TypeScript是微软开发的,它是JavaScript的超集,引入了类的概念,嗯~~这一点对后端开发人员很友好啊。既然说它是JavaScript的超集,那么就算不了解TypeScripe也可以开发的,因为一开始我就不了解的,只要JavaScript没问题就行。虽然说Angular是用TypeScript写的,但在浏览器运行前它在底层还是要被编译成JavaScript的,毕竟浏览器不认识。
  • 3、安装Angular-cli
    打开cmd运行命令:npm install -g @angular/cli
    要说Angular-cli就先说说cli,cli全称command-line interface,即命令行界面,说白了就是通过动手敲下一个个指令让计算机工作,嗯~~为什么要用这个,得记一个个命令多累人,为什么没有可视化工具进行操作,这个我也不知道,难道就只是为了节约计算机系统资源?希望有人告诉我。Angular-cli就是针对Angular项目构建的脚手架,对应的Vue也有Vue-cli,React没接触过不了解。在安装过程中可能会出现各种问题,反正基本上就是被墙的原因,实在不行就用淘宝镜像。
  • 4、开发工具。其实开发工具是个人喜好,有很多开发工具可以使用,但是我想推荐的是vscode,插件库很爽啊。关于被墙的解决方法和推荐插件点击这里

创建新项目

创建Angular新项目的运行命令:ng new 项目的名字
这个命令在命令窗口中或者vscode的终端中都可以,其中的ng代表的是Angular,不知道是从哪个版本开始,创建新项目会有以下两个询问,根据需要选择就可以了,网络良好的话创建不会有问题,如果被墙了那就创建失败。
创建项目

运行创建的项目

把项目文件丢到vscode中,然后在终端中执行命令:ng serve。其实运行npm start一样能启动项目,但是其实npm start最后还是调的ng serve

编译通过
在编译完成之后,在浏览器中访问4200端口,看见下面这个说明项目构建成功,正常运行
运行成功

一些编码风格设置

通过Angular-cli生成的项目,在编码风格上与自己的习惯不一致时,可以通过修改tslint.json文件中的配置,以下是我的一些习惯
风格

Angular项目目录结构介绍

这里从网上找了一张图,来源已经不清了
目录结构

ngModule

关于ngModule,中文官网上有这么一句话

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

这句话其实说的很明白了,就是NgModule 类是必须的,否则跑不起来。(关于引导,是在main.ts文件中,那是程序的入口,通常不需要改动这个文件里的东西)这个类接受一个元数据对象,这个对象中的属性是用来描述这个模块的,具体看下图
在这里插入图片描述

组件

组件,顾明思议一组元件,它是Angular中一个重要的概念,通过命令ng g component 组件名称来生成,见下图
组件
在上图中,@Component 装饰器会指出紧随其后的那个类是组件类,并指定元数据,换句话说就是,如果没有@Component这个装饰器的存在,这个TopComponentComponent类就是一个普通的类,那么它将无法控制任何html模板,只有被@Component 装饰的类才具有控制html模板的能力。
在定义生成组件之后,在需要的地方放置就能将组件展示到页面上,组件就是一个元件,一块砖,哪里需要哪里搬
在这里插入图片描述在这里插入图片描述
以上就是一个最基本的Angualr应用程序所应知道的基本知识点,下面说说一些开发中用到的东西。

组件间通讯

组件就是一个个小模块,正常情况下他们都是没有关系的,数据之间不互通,但是一个应用中不可能数据都只在一个个模块中,必须进行一些数据交互才能完成相应的功能,因此组件的通讯就变得很重要。组件间通讯的场景主要有:一、嵌套组件间通讯。分为父组件向子组件传递数据、子组件向父组件传递数据两种情况;二、非嵌套组件组件通讯(如果不是这次的回顾,我都不知道非嵌套组件间通讯的这种官方方式,如果早知道,现在项目中就没有那么多骚操作了…)。

  • 嵌套组件间通讯

1、父组件向子组件传递数据
步骤1:在子组件选择器标签上进行属性绑定,所绑定的值为父组件的某个属性,也就是你要传递给子组件的值

 <out #child [requestData]="sendToChild" (sendToParent)="getOutput($event)"></out>

上面的代码片段中[requestData]="sendToChild"就是进行进行的步骤1,requestData是子组件out的接收变量名,sendToChild是父组件中的一个属性,它的值如果发生变化,子组件所接收到的值也会立即变化。

步骤2:在组件的.ts文件中使用@Input()属性装饰器进行接收

@Input()
requestData:string;

至此,只要父组件中sendToChild的值发生变化,子组件requestData的值也就会马上变化。

2、子组件向父组件传递数据
步骤1:先定义属性装饰器@Output()

@Output()
sendToParent:EventEmitter<string>=new EventEmitter();

关于EventEmitter类,我说不太清楚,没有专门去了解,就知道用来发射对象或属性给父组件的。要注意的是,这里的EventEmitter类是@angular/core里引入的,不能是其他模块里的,比如events里也有EventEmitter类,别搞错了。

步骤2:主动触发发射(当然也可以是代码逻辑发射)

<input type="text" placeholder="往父组件中传入的值" [(ngModel)]="toParent">
<button class="btn btn-primary" (click)="sendToPar()">确定</button>
toParent:string;
sendToPar(){
    this.sendToParent.emit(this.toParent);
}

通过emit方法将要发射的值发射出去。
上面是子组件进行了发射的相关步骤,下面是父组件的步骤

步骤3:要接收谁的值就去它的选择器上进行监听

<out #child [requestData]="sendToChild" (sendToParent)="getOutput($event)"></out>

(sendToParent)="getOutput($event)"所绑定的事件sendToParent必须是子组件中的EventEmitter实例。

requestData: string;
getOutput(event: string) {
    this.requestData = event;
}

3、父组件调用子组件的属性与方法
这种方式是通过@ViewChild来实现的,就像是new了一个子组件的实例一样。

步骤1:给子组件定义模板变量

  <out #child [requestData]="sendToChild" (sendToParent)="getOutput($event)"></out>

#child就是模板变量,在当前HTML页面中都可以使用,在.ts文件中我的理解就像document.getElementById()

步骤2:使用@ViewChild()属性装饰器注入到父组件中

@ViewChild("child")
childRecive: OutputComponent;

经过@ViewChild()装饰的属性childRecive将会成为OutputComponent的实例(不知道这么说对不对)。这里的OutputComponent是子组件out的类,如果使用模板变量的标签是普通HTML标签,则类型为:ElementRef。经过这个两个步骤,父组件就可以调用子组件中的方法和属性了。

关于模板变量的的另一种使用,官网上还有一种是父子组件通过本地变量互动,其实就是利用模板变量在当前HTML中任何地方都可以使用的特性,这里就不写了。

4、ng-content内容投射
组件间的交互官网上并没有提到这种方式,但我的觉得这也是一种交互吧,毕竟投射的内容可以直接使用父组件中的东西

步骤1:在子组件选择器标签中写HTML代码片段

<out #child [requestData]="sendToChild" (sendToParent)="getOutput($event)">
    <!-- 
      选择器之间是可以写HTML代码片段的,这些片段将会显示在这个组件(outcomponent)的模板中,
      前提之一是这个组件的模板中有调用了ng-content指令,一般的关系是从父组件到子组件,这里
      还可以进行插值绑定获取其他的数据的绑定等,所绑定的值是父组件的值
    -->
    <div class="header" style="color:red;">这是头:这个是在父组件中的写的代码显示到了子组件中</div>
    <div>这个是在父组件中的写的代码显示到了子组件中,这是父组件中的属性:
    	<span  style="color:red;">{{context}}</span>
    </div>
    <div class="footer" style="color:red;">这是脚:这个是在父组件中的写的代码显示到了子组件中</div>
</out>

步骤2:在子组件的HTML文件中使用ng-content标记投射点

<div style="border:1px solid darkslateblue;">
    <!-- 标记投影点,从父组件投影部分HTML代码片段过来在这个位置显示 -->
    <ng-content select=".header"></ng-content>
    <ng-content></ng-content>
    <ng-content select=".footer"></ng-content>
</div>

select属性支持css选择器(”#id”,”.class”等等)
结果如下:
在这里插入图片描述

  • 非嵌套组件组件通讯

对于非嵌套组件组件通讯,目前我所知道只有通过service了,通过service形象点说就是桥,或者容器,一方将数据放入这个容器,另一方需要的时候进行读取,实现如下:
步骤1:创建一个service,命令:ng g service 服务的名称
步骤2:创建好service之后,编写接收函数和读取函数

 message:string;
 sentMessage(message:string){
    this.message=message;
 }
 getMessage(){
    return this.message;
 }

步骤3:在需要的地方注入服务就可以使用了

constructor(
    private messageService:MessageService
){}
//要发送消息的组件调用服务
sent(){
    this.messageService.sentMessage("发送的消息");
}
//要获取消息的组件中调用服务
message:string;
getMessage(){
    this.message=this.messageService.getMessage();
}

注入了服务就可以使用服务里的属性和方法了,就不展示结果了,很简单的东西

路由

这里的路由指的是前端路由,关于路由的概念,我找不到一个明确的定义,总结网上解释最多的是

路由就是指随着浏览器地址栏的变化,展示相应的页面给用户

虽然我能理解这样的解释,也认同这种解释是对的,但我并不想接受这种解释,因为这不是我想要的解释,但是我也没有什么语言来准确解释。嗯~~我觉得我的这种思想很奇怪。对于前端路由,GitHub上有一个人总结的很到位,参考这里

那么谈谈我自己对于路由的理解。路由原本是属于后端的东西,用户地址栏输入地址或者在页面进行操作之后会向服务器发起请求,那么服务器则会对请求的URL进行解析,然后将数据返回给客户端。随着业务需求的复杂,服务端将会处理越来越多的请求,每一次请求都要返回相应的数据,这些数据中有些根本就不重要,但是仍然需要进行返回,尤其是需要进行整个页面刷新的,这样一来服务端所承担的压力会越来越大,那怎么来减小这里压力呢。

前端路由就这么应运而生了,它的诞生是借鉴与浏览器的导航模式,我们知道的是,当地址栏的URL不同将会请求到不同的页面,只要URL发生了变化就马上发起请求,刷新页面,那么这个在前面说了,会使得服务端的压力增大,但如果我的地址栏发生了变化但是不发起请求,我客户端根据地址栏变化的东西对应展示写好的东西,那么服务端不就没事了,就算需要数据只要返回给我一个赤裸的数据就够了,所以这就是前端路由的思想。现在开发中的前后端分离我们可以简单粗暴的这么认为,前端主要就是DOM,是衣服,后端是数据,是各种人。当前端渲染页面的时候就是指定展示某件衣服,那么后端就返回这件衣服对应的人,这个人是赤裸的,这样一结合就是一个体面的人。

上面说了只要地址栏发生了变化,但不发起请求,那么后端就没什么事了,但问题是地址栏URL发生了变化怎么让它不向服务器发起请求呢,解决方法就是监听浏览器的hash值变化,诶,问题又来了,什么是hash值呢。

hash在w3school上的定义是:

hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)。

这个定义说的很明白了,比如这个URLhttps://music.163.com/#/friend#/friend就是这个URL的锚部分,这个#号后面的friend就是hash值,这个值或者后面的值(如果有)不管怎么变化都不会引起页面的请求,没有请求就不会刷新页面了,但需要知道的是,虽然它不会发生请求,但是访问历史是改变的。

下表是关于路由的关键词汇

路由器部件 含义
Router(路由器) 为激活的 URL 显示应用组件。管理从一个组件到另一个组件的导航
RouterModule 一个独立的 NgModule,用于提供所需的服务提供商,以及用来在应用视图之间进行导航的指令。
Routes(路由数组) 定义了一个路由数组,每一个都会把一个 URL 路径映射到一个组件。
Route(路由) 定义路由器该如何根据 URL 模式(pattern)来导航到组件。大多数路由都由路径和组件类构成。
RouterOutlet(路由出口) 该指令()用来标记出路由器该在哪里显示视图。
RouterLink(路由链接) 这个指令把可点击的 HTML 元素绑定到某个路由。点击带有 routerLink 指令(绑定到字符串或链接参数数组)的元素时就会触发一次导航。
RouterLinkActive(活动路由链接) 当 HTML 元素上或元素内的routerLink变为激活或非激活状态时,该指令为这个 HTML 元素添加或移除 CSS 类。
ActivatedRoute(激活的路由) 为每个路由组件提供提供的一个服务,它包含特定于路由的信息,比如路由参数、静态数据、解析数据、全局查询参数和全局碎片(fragment)。
RouterState(路由器状态) 路由器的当前状态包含了一棵由程序中激活的路由构成的树。它包含一些用于遍历路由树的快捷方法。
Link parameters array(链接参数数组) 这个数组会被路由器解释成一个路由操作指南。你可以把一个RouterLink绑定到该数组,或者把它作为参数传给Router.navigate方法。
Routing component(路由组件) 一个带有RouterOutlet的 Angular 组件,它根据路由器的导航来显示相应的视图。

说了一堆概念的东西了,现在说说使用(这篇博客对应的demo我是事先写好了的),下图中涉及了路由的大部分知识,路由配置、传参方式、路由守卫等,这里只是配置,相对应的其他地方也要有相应的配置。
路由配置图:
在这里插入图片描述

关于路由传参,有三种方式

  • 第一种:URL传参。
    1、进行路由配置见上图。
    2、配置好之后在routerLink上配置、传参,如下
 <button class="btn btn-primary" routerLink="./router-child2/{{id}}/{{name}}">路径传值</button>

3、在对应的组件中接收参数。
在这里插入图片描述
4、最终结果:
点击传值后路径上将携带参数值http://localhost:4200/router/router-child2/123/张三
在这里插入图片描述

  • 第二种:路由配置传参
    1、在路由配置中添加data属性和值就可以,见路由配置图。
    2、配置routerLink
<button class="btn btn-primary" routerLink="./router-child3">配置传值</button>

3、在对应的组件接收参数

this.router.data.subscribe(pram=>{
      console.log(pram);
})

4、接收结果:
在这里插入图片描述

  • 第三种:查询参数传参
    查询参数传参不需要配置路由
    1、配置routerLink和queryParams
 <button class="btn btn-primary" routerLink="./router-child" [queryParams]='{id:id}'>查询参数传值</button>

2、在对应组件接收参数

this.router.queryParams.subscribe(params=>{
      this.id=params["id"];
})

3、最终结果:
通过查询参数传值时,URL会携带参数值:http://localhost:4200/router/router-child?id=123456
在这里插入图片描述
关于路由参数传参的三种方式介绍结束。在这之中要注意的地方有:

  • 关于routerLink的写法
    一种是写在a标签里的,一种是非a标签里的
    a标签中可以使用这种方式:[routerLink]="['./router-child',id,name]"
    非a标签中不能有中括号或者什么其他的符号,否则报错或者匹配失败:易出现的问题有:
    1、[routerLink]="[./router-child]"该方式直接导致报错,routerLink不需要加中括号
    2、routerLink="[./router-child,id]"该方式导致路径不对,找不到匹配路径,routerLink的值不需要加中括号,添加中括号时,中括号会转码加到路径中导致匹配失败

  • 关于子路由
    添加子路由时,routerLink的值需要在斜杠/的前面加.不加.时将直接到根路由寻找,如果匹配到相同的路径则显示相应的组件,匹配不到则匹配**路径 。

  • 上述例子中URL中没有#
    对于URL中没有#号,这是因为路由器通过两种 LocationStrategy 提供商来支持不同的URL风格
    一种是:PathLocationStrategy - 默认的策略,支持“HTML 5 pushState”风格。
    另一种是:HashLocationStrategy - 支持“hash URL”风格。
    RouterModule.forRoot() 函数把 LocationStrategy 设置成了 PathLocationStrategy,使其成为了默认策略。
    关于这个的原因,angular官网上已经解释的很清楚,详情可见官网中路由与导航部分的附录

路由导航补充:路由导航除了在配置routerLink,还可以通过跳转的方式来进行,见以下

<button class="btn btn-primary" (click)="navigateTO()">navigate跳转</button>
navigateTO(){
    this.router.navigate(["router/router-child3",{id:"xxxx",name:this.name}]);
}

路由的东西暂时就写这么多吧,还有路由守卫的,这里就不写了,就是在路由配置里加路由守卫属性,然后编写相关守卫类,返回布尔类型,交给提供商providers,根据true和false决定是否允许进入和离开路由

指令

关于指令,怎么解释这个词在angular中的定义呢,通俗点说就是一个命令,当你把这个命令绑在了一个标签上,那么它就会对这个标签进行一些操作,这个指令的背后就是一些操作代码,通过指令,我们可以在很多地方使用,但是操作代码就一套,省了很多事情。指令分为属性型指令和结构型指令。一般情况下angular的内置指令就足够我们使用了,有以下这些常见指令:

  • ngIf =====if判断,为真则显示该部分,为假则不显示
  • ngFor ====for循环,循环输出或显示
  • ngSwitch ==switch语句,符合条件则显示
  • ngClass ===设置css类
  • ngStyle ====设置样式

这几个指令是开发中经常用到的,也没什么难度,没什么好说的

对于Angualr,通过命令生成的相关组件、指令、管道等,都会自动在根组件注册相关类,但如果是自己手动写相关的装饰器和类等,则需要手动去根组件注册这个类。

  • 属性型指令

属性型指令用于改变一个 DOM 元素的外观或行为。这里演示一个扩展官网例子的。
步骤1:执行命令ng g directive 指令名称,生成的文件中可见@Directive()装饰器

@Directive({
  selector: '[appHighlightAndResetSize]'
})

步骤2:注入宿主 DOM 元素的引用
使用命令创建指令后生成的文件除了基本的@Directive()装饰器和类这些东西,空荡荡的宛如被掏空,此时要在构造函数中注入宿主 DOM 元素的引用

constructor(
    el:ElementRef
) { }

在注入宿主 DOM 元素的引用之后,如果不需要参数以及行为,则可以直接在构造函数的方法体中写相应的操作逻辑代码,下面的两个步骤也就不需要了。

步骤3:定义输入属性(可选,如果需要传入参数则需要此步骤,反之不要)

 @Input("appHighlightAndResetSize")
 colorAndSize:any;

步骤4:定义行为函数(可选,如果有行为事件则需要此步骤,反之不要)

 @HostListener("mouseenter")
  onMouseEnter(){
    console.log(this.colorAndSize);
    this.el.nativeElement.style.backgroundColor=this.colorAndSize.color;
    this.el.nativeElement.style.fontSize=this.colorAndSize.size+"px";
  }
  @HostListener("mouseleave")
  onMouseLeave(){
    this.el.nativeElement.style.backgroundColor=null;
    this.el.nativeElement.style.fontSize=null;
  }

这个@HostListener()装饰器是用来监听dom事件的,第一个参数是要监听的事件,第二个参数可选,是当该事件发生时传给处理方法的一组参数。
结果演示如下:
鼠标进入前:
在这里插入图片描述
鼠标进入后:
在这里插入图片描述

  • 结构型指令

对于自定义结构型指令,我不知道是怎样的需求才会需要这样,通常情况下ngIfngForngSwitch就够用了,但是对于学习来说,知道如何去自定义还是有必要的,由于不知道怎样的需求才会需要自定义结构型指令,因此这里直接拿官网的例子来了。

步骤1:执行命令ng g directive 指令名称,生成的文件中可见@Directive()装饰器。当然也可以手动导入相应的装饰器和相关的类,并最后注册到根组件

@Directive({
  selector: '[appUnless]'
})

步骤2:构造函数中注入模板类和视图容器类

constructor(
    private templateRef:TemplateRef<any>,
    private viewContainerRef:ViewContainerRef
) { }

步骤3:导入@Input()属性装饰器并编写逻辑代码

  private hasView: boolean = false;
  @Input()
  set appUnless(condition: boolean) {
    if (!condition && !this.hasView) {
      this.viewContainerRef.createEmbeddedView(this.templateRef);
      this.hasView = true;
    }
    else if (condition && this.hasView) {
      this.viewContainerRef.clear();
      this.hasView = false;
    }
  }

使用结果:
在这里插入图片描述
在这里插入图片描述
结构型指令相对来说比较难,而且一般来说没必要自定义结构型指令(仅我个人见解),自定义结构型指令两个重要的类TemplateRefViewContainerRefTemplateRef可以取得<ng-template>的内容,ViewContainerRef可以访问视图容器,通过这两个类就可以完成对页面DOM结构的更改,至于更深的对于DOM的相关操作我就不知道了

事件

事件本来想合到指令里写的,但是觉得不合适,还是分了出来,其实angualr事件我所了解的并不多,只知道有哪些常见事件,以及如何使用,但都不难。常见事件有:

  • ngClick =========点击事件
  • ngBlur ==========失焦事件
  • ngFocus ========获焦事件
  • ngKeyup ========按键抬起事件
  • ngMouseenter ====鼠标进入事件
  • ngMouseleave ====鼠标离开事假

这些常见事件也都很简单,每个事件都有一个对应的$event的事件对象,这个对象的具体取决于事件类型,使用的时候把它打印到控制台找自己需要的属性与方法就可以。

事件也可以自己自定义,这里就不写了,官网有个小例子。对于内置的事件具体有哪些,我个人是不清楚的,有时候就拿原生DOM事件去试,毕竟我找不到官网在哪里写有。

对于ngKeyup有一个事件过滤就是keyup.enter这个就是只在按了回车键之后才调用函数,对于angualr的事件过滤,我仅知道这一个,这一点上Vue似乎做的更好
使用方法:

<input #box (keyup.enter)="onEnter(box.value)">

管道

对于管道其实也是一个常用的功能,就是将数据格式化或过滤展示到界面,通常的需求就是,从数据库读取过来的数据有时候格式不是日常生活中所见到的格式,比如日期;还有一些比如性别,存入数据库时候可能用0和1表示男女,但读出来的不可能将0和1直接展示给用户,这就需要将读出来的数据进行处理。Angualr就提供了这种功能,通过管道将数据格式化展示,使用方法就是在数据的后面添加管道操作符|然后写上对应管道,比如格式化日期:{{ 要格式化的日期 | date:'yyyy-MM-dd' }},date就是日期管道,yyyy-MM-dd就是要格式化成样子,格式可以自己去定义。比较常用的内置管道有:

  • date =========格式化日期
  • upperCase ====转大写
  • lowerCase ====转小写
  • json =========格式化json,不好用

通常情况下内置的管道不符合我们个人开发的需要,很多时候需要自定义,这里演示一个性别的管道

步骤1:指令命令ng g pipe 管道名称
通过命令生成的文件中,初始代码为

@Pipe({
  name: 'gender'
})
export class GenderPipe implements PipeTransform {

  transform(value: any, args?: any): any {
    return null;
  }

}

@Pipe()装饰器标记相邻的这个类是一个管道,管道类实现了 PipeTransform 接口的 transform 方法,该方法接受一个输入值和一些可选参数,并返回转换后的值。

步骤2:在transform方法中编写处理逻辑

 if(value=="1"){
      return "男";
 }
 else if(value=="0"){
      return "女";
 }
 else{
      return "人妖";
 }

步骤3:
如果数据源为:

person = [
    {name: "张三",sex: "1"},
    {name: "小明",sex: "3"},
    {name: "小红",sex: "0"}
]

使用:

<li *ngFor="let item of person" >{{item.sex | gender}}</li>

结果过展示为:
在这里插入图片描述
关于管道需要注意的地方就是纯管道非纯管道的区别:
只有发生纯变才会调用该管道,这类管道称为纯管道,其余的管道成为非纯管道。 纯变指的是对基本数据类型(StringNumberBoolean)输入值的变更或者对对象引用ArrayFunctionObject)的更改。 只要数据发生改变,均会触发非纯管道,但不一定会触发纯管道,需要考察数据变化的情形是否为纯变化。
这里换句话说就是像数组、对象等数据的改变,只有当引用地址的改变才会调用管道,如果只是数组、对象中的某一个值改变是不会调用管道的。

服务与代理

服务其实在非嵌套组件间通讯已经使用过,只不过没有细说,关于服务,先扯一扯依赖注入吧,依赖注入一开始我一直不理解是怎么一回事,在网上查了很久,解释千篇一律,很少有自己见解的,有些还会扯到控制反转的,并不是说说的不对,而是不利于那些什么都不懂的人去理解。

我理解的依赖注入是:
在没有依赖注入的时候,A要完成某个功能需要B、C、D的帮助,那么A就需要亲自一个一个的去叫B、C、D帮忙,在面对对象编程语言中,就是需要new B、C、D对象,那么问题来了,A直接与B、C、D产生了关系,这在软件工程中就是提高了耦合。而有依赖注入的时候,有一个人力资源公司把B、C、D都收了,A要完成功能的时候直接去找人力资源公司说我需要B、C、D就够了,最后人力资源公司给A提供B、C、D,至于人力资源公司怎么找的B、C、D不关A的事。这样就降低软件的耦合。

Angular的依赖注入有不少东西,我并不能完全的理解,对于依赖注入的好处,我所能理解的就是降低耦合,降低了内存占用(因为如果使用每new一次就要占用一个内存空间),其他的我就不知道有什么好处了。但是话说回来,依赖注入是一个框架所拥有的东西,一个框架的目的就是统一生产形式,规范生产过程,如果将一个使用了框架的代码交给你,你直接知道从哪里看起,但是如果给你一个没有生产规范的代码给你,你会感觉无从下手,甚至会被各种依赖之间绕晕。

在Angular中使用ng g service 服务名称命令之后生成的初始代码为:

@Injectable({
  providedIn: 'root'
})
export class HttpService {

  constructor() { }
  
}

@Injectable()装饰器是每个 Angular 服务定义中的基本要素,它把与之相邻的类标记为可供注入的服务,在@Injectable()中有一个providedIn的元数据选项,它的值为root,表明这个类在顶层提供服务,这么定义之后就不需要将它添加到根模块的提供者中。这个元数据选项的值不一定非得为root,可以根据自己的需要修改,如果指定这个服务提供给哪个具体的类,那么需要将这个服务类注册到根模块的提供者providers中。

这里演示一个简单的请求后端数据的服务,要了解更过参考这里代理到后端服务器部分。
步骤1:必须先在根模块中导入HttpClientModule模块,否则在其它地方无法正确引包

imports: [
    BrowserModule,
    // 导入HttpClientModule需要在BrowserModule模块的后面
    HttpClientModule,
],

步骤2:把 HttpClient 注入到应用类中

@Injectable({
  providedIn: 'root'
})
export class HttpService {

  constructor(
    private httpService:HttpClient
  ) { }
  
}

这一步之后还不能获得服务器数据,因为没有配置代理,会产生跨域的问题,因此要使用HttpClient的相关API请求数据,需要先配置好代理

步骤3:配置代理

  1. 在项目的 src/ 目录下创建一个 proxy.conf.json 文件,和 package.json 放在同一目录下
  2. 往这个新的代理配置文件中添加如下内容:
{
  "/api": {
    "target": "http://localhost:3000",
    "secure": false
  }
}
  1. angular.json 中为 serve 目标添加 proxyConfig 选项:
"architect": {
  "serve": {
    "builder": "@angular-devkit/build-angular:dev-server",
    "options": {
      "browserTarget": "AngularTest:build",
      
      "proxyConfig": "src/proxy.conf.json"
      
    },

也可以在package.json的命令后面直接以参数的形式配置

"scripts": {
    "ng": "ng",
    
    "start": "ng serve --proxy-config proxy.config.json --port 4300",
    
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },

经过以上配置之后就可以使用HttpClient相关的API了,比如get请求

 getServe():Observable<any>{
    return this.httpService.get("api/");
  }

HttpClient的很多API返回的都是Observable流,关于Observable就不解释了,太多东西了。
我自己搭了一个node服务器对请求进行了响应,返回了一个测试数据
在这里插入图片描述

写在最后

这篇博客是我的第一篇正式的博客,不知不觉写了一万多字的总结与感悟,估计没有人从上到下看到这里,虽然这些都可以从官网中找得到指南,但是经过自己亲自写出来是有自己感悟的。除去实习,我正式工作还不满一年,没有经历过前端的逐步发展,很多东西都只是自己在学习与在项目中了解的,有些东西不知道说的准不准确。如果有能看到这里的看官发现说的不对地方,望不吝赐教。

猜你喜欢

转载自blog.csdn.net/moqiuqin/article/details/91294718
今日推荐