壮士且慢~这里有本Angular修炼秘籍送你!

Posted Think体验设计

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了壮士且慢~这里有本Angular修炼秘籍送你!相关的知识,希望对你有一定的参考价值。


今年1月份开始接触angular的新版本Angular5,之前一直是用的angular1.x。开始了angular5的踩坑之路,话不多说进入正题吧。


 初识 angular5 


学习angualr5的文章很多,推荐博文(复制链接到浏览器打开)

https://segmentfault.com/a/1190000008754631


 初学问题 


1. 打包之后的文件运行问题


直接打开index.html 页面无法打开,控制台会报如下的错误:

是因为angular是基于npm环境搭建的,打包后的文件需要在模拟服务器上打开。

解决方案:可以全局安装npm的http-server模块。构建本地web模拟服务器。

安装方法:在cmd窗口执行命令:npm install http-server –g 

使用方法:在index所在文件夹执行cmd窗口命令:hs –p 端口号 –o  即可打开运行。


2. 多级路由嵌套及懒加载问题


如果项目当中页面嵌套过多的情况,会出现路由嵌套的情况。


多级路由嵌套两种写法:

1)直接进行路由嵌套如下:

(左右/上下滑动可查看全部代码)

{
   path: 'dashboard', component: DashboardComponent, canDeactivate: [CanLeaveDetail],
   children: [
     { path: 'partA', component: DashboardPartAComponent },
     { path: 'partB', component: DashboardPartBComponent },
     { path: 'partC', component: DashboardPartCComponent }
   ]
}

2)懒加载模式:

主路由:(左右/上下滑动可查看全部代码)

{
   path: 'lifeCycle',
   canActivate: [AuthService],
   loadChildren: './LifecycleHooks/lifeCycleHooks.module#LifeCycleHooksModule'
}


子路由:(左右/上下滑动可查看全部代码)

{
   path: '', // 使用loadChildren方式此处路径为空
   component: LifeCycleHooksComponent,
   children: [
     { path: '', redirectTo:'componentLC', pathMatch: 'full'},
    { path: 'componentLC', component:ComponentLCComponent }
   ]
}


子路由全部用loadChildren加载,到这个路由的时候在加载路由下的模块及组件。

注意:子路由中的第一个path一定要为空。


 开发组件期间遇到的问题 


1. 使用组件的时候直接加在组件上的样式和样式类无效:


解决方案:给组件父容器上加样式或样式类,代码如下:

(左右/上下滑动可查看全部代码)

<div class="wrapper-content">
   <h1>基本用法</h1>
   <p-progressbar [animate]="animate" [value]="value" [maxValue]="maxValue">
</p-progressbar>
</div>


2. angular样式封装


组件内部定义的样式类不影响组件外部。

采用的策略是:angular在解析组件内部写的样式会加上_ngcontent-属性。这样不会全局污染。

打开控制台,查看DOM接口,在head标签中的style标签中会看到如下样式:

壮士且慢~这里有本Angular修炼秘籍送你!


查看宿主元素也就是自定义的组件标签DOM,如下图所示会加上_nghost-属性。

壮士且慢~这里有本Angular修炼秘籍送你!


导致的问题是组件内部定义的样式类在宿主元素上不生效。

为解决上述为题angualr提供了三种样式封装策略。需要引入ViewEncapsulation。

(左右/上下滑动可查看全部代码)

import { ViewEncapsulation } from'@angular/core'


具体方案:在元数据上加encapsulation属性。有下面三个值:

  • ViewEncapsulation. Emulated(仿真)

这是默认选项,通过Angular来模拟类似Shadow DOM的行为。不会影响全局样式。

  • ViewEncapsulation. Native(原生)

原先浏览器ShadowDOM行为。

  • ViewEncapsulation.None(无)

angular无任何封装行为,会全局污染。


3. 如何给一个DOM节点上使用2个ng-class


在angular1.x上只能有一个,并且只有一个生效。

Angular5中有个替代方案如下:

[class.className]=”expression”可以写多个。

左右/上下滑动可查看全部代码

<div class="p-progress-bar" [class.p-no-animation]="!hasAnimation"[ngClass]="progressClass">
</div>

 

4. 如何实现接口的双向数据绑定(Two-WayData Binding)


双向数据绑定原理图:

壮士且慢~这里有本Angular修炼秘籍送你!

双向绑定是由两个单向绑定组成:

  • 模型 -> 视图数据绑定

  • 视图 -> 模型事件绑定


Angular 2.x及以上版本中 [] 实现了模型到视图的数据绑定,() 实现了视图到模型的事件绑

定。两个结合在一起 [()] 就实现了双向绑定。就好比[(ngModel)]。


1)在angular1.x中可以按照下面的方式实现接口的数据绑定,通过‘=’修饰符表示这个数据双向绑定

如下代码,定义接口的时候使用‘=’

scope: {
 format:  ‘=?’
}

 

2)在angular2.x以上的版本实现方式有下面两种


第一种,Input和output装饰器结合使用

实现方案:

I.定义一个Input输入属性,定义一个Output输出的事件接口,名称之间形式如下:

(左右/上下滑动可查看全部代码)

@Input() stated : boolean;
@Output() statedChange = new EventEmitter<boolean>();

II.在内部修改stated的时候把这个值,把这个值通知到外部。

private toggleSwitch(): void {
  this.stated = !this.stated;
  this.statedChange.emit(this.stated);
}

III.使用的时候形式如下

(左右/上下滑动可查看全部代码)

<p-switch name="state" [(stated)]="stated"></p-switch>

原理如下:

[(stated)] 可以拆分成两部分stated和statedChange,[stated]用于绑定输入属性,(statedChange)用于绑定输出属性。当 Angular 在解析模板时,遇到 [(stated)] 形式的绑定语法,它会期待这个指令中会存在一个名为 stated 的输入属性和一个名为 statedChange 的输出属性。


第二种,创建类表单组件

https://segmentfault.com/a/1190000009070500


把这个组件当中最重要的接口定义成双向数据绑定,如下方式使用:

双向数据绑定的值用[(ngModel)]=”xxx”绑定实现。

使用形式如下(就好比原生的textarea标签)

(左右/上下滑动可查看全部代码)

<p-spinner name="state" [(ngModel)]="value"></p-spinner >

5. 测试用例定义的[id]变量,在组件的ngOnInit()生命周期钩子拿不到


解决方法:在ngAfterViewInit()生命周期钩子可以拿到。

用户写宿主id三种方式:
1)给HTML属性id赋字符串

<!-- 赋字符串 -->
<input id='mycheckbox' />
<!-- 赋变量 -->
<input id='{{myId}}'/>

2)给HTML属性id赋变量

<input [attr.id]='mycheckboxid'/>


3)给DOM属性id赋变量

<input [id]='mycheckboxid'/>


组件开发读写宿主id的三种方法:
1)读HTML属性id
(左右/上下滑动可查看全部代码)

constructor(@Attribute('id') public hostId ) {}


2)写HTML属性id 

@HostBinding('attr.id') hostId: string;

写DOM属性id

@HostBinding('id') hostId: string;


3)读写DOM属性id(左右/上下滑动可查看全部代码)

constructor(private hostRef: ElementRef ) { }
ngAfterViewInit() {  
   const id =  this.hostRef.nativeElement.id
}


6. 函数作为入参时候,函数尽量写成箭头函数形式


组件开发过程过需要修改或者读取组件属性或者方法时候,一定要写成箭头函数形式,不然会出现this指向问题。

比较常见的2种应用。

1)定时器:(左右/上下滑动可查看全部代码)

let timer = setInterval(() => {
  startDist += motion.step * dir;
  topy+= motion.step * dir;
  if (Math.abs(startDist - startPoint)>= motion.distance) {
    clearInterval(timer);
    timer = null;
  }
}, 1);


2)事件监听:(左右/上下滑动可查看全部代码)

document.addEventListener('mousemove', this.mouseMoveHandlerFn);
private mouseMoveHandlerFn = ($event) => {
 const option = this.config.options;
}


7. 组件通讯之子获取父的实例


应用场景:组件嵌套,子组件需要用到父组件的属性和方法。

组件嵌套形式如下面代码:

父组件的模板代码如下,父组件嵌套了子组件app-child。

(左右/上下滑动可查看全部代码)

<div class="container">
   <div *ngFor="let item in items">
     <app-child [item]="item"></app-child>
  </div>
</div>

方案:在constructor(构造函数)中使用@Inject()依赖注入父组件。

如下面代码:

export class TiBtnItemComponent {
 constructor(
   @Inject(TiBtnRadioCompontent) private btnRadio: TiBtnRadioCompontent
 ) {}
}

并且上面的方式是单例的(父组件下不管有几个子组件,父组件只实例化一次),但是这种方式只能注入一个组件实例。

在实际开发中,例如选择按钮组有多选按钮和单选按钮,这个时候就需要注入多个父组件。用上面同样的方式同时注入2个就会报如下错误:

壮士且慢~这里有本Angular修炼秘籍送你!


原因如下:

当组件申请一个依赖时,Angular从该组件本身的注入器开始,沿着依赖注入器的树往上找,直到找到第一个符合要求的提供商。如果找不到就会抛出一个错误。

 

解决方案:

在@Inject()装饰前加@Optional(),如下面代码:

(左右/上下滑动可查看全部代码)

export class TiBtnItemComponent {
 constructor(
  @Optional()@Inject(TiBtnRadioCompontent) private btnRadio: TiBtnRadioCompontent,
    @Optional() @Inject(TiBtnCheckboxCompontent) private btnRadio:TiBtnCheckboxCompontent    
 ) {}
}


解决原因:

当Angular找不到依赖时,@Optional装饰器会告诉Angular继续执行,Angualr会把此注入参数设置为null(而不是默认的抛出错误的行为)。


8. 组件生命周期总结

壮士且慢~这里有本Angular修炼秘籍送你!


如上图,为组件初始化各个生命周期执行顺序。其中紫色的生命周期只在组件初始化执行一次,绿色的会执行多次。


1)ngOnChanges频率较低,仅监听用户对@Input变量的改变。注意:监听不到组件开发者在代码中对变量更改。可以考虑使用@Input set访问器方式,监听变量,所有改变都可以监听到。


2)ngDoCheck,触发频率超高。angular自身监视[ngclass]数组变化,是放在这个生命周期。使用了IterableDiffers机制。其他什么情况适合放在这里,暂时不太清楚。


3)ngAfterContentChecked,ngAfterViewChecked相对频率低。我们目前代码监听Dom属性变化,暂时放在这里了。不知道有没有更好办法监听Dom属性变化。


注意:如果在ngAfterViewChecked里面,改变了模板中使用的变量,此变量不一定能及时更新到试图。需要强制刷新this.changeDetectorRef.detectChanges();


4)想监听数组和对象属性变化,angular内部使用了IterableDiffers和KeyValueDiffer。参考[ngClass]源码。


— END —


壮士且慢~这里有本Angular修炼秘籍送你!


壮士且慢~这里有本Angular修炼秘籍送你!有什么想对作者说的吗?快来留言吧!壮士且慢~这里有本Angular修炼秘籍送你!


相关链接:




THINK体验设计

微信ID:hw-Think

长按二维码关注有惊喜

壮士且慢~这里有本Angular修炼秘籍送你!


点击“阅读原文”,进入华为云官网 ! 

以上是关于壮士且慢~这里有本Angular修炼秘籍送你!的主要内容,如果未能解决你的问题,请参考以下文章

JAVA 修炼秘籍第八章:《String类》

每天3分钟操作系统修炼秘籍(25):进程调度算法图解说明

每天3分钟操作系统修炼秘籍:并行的假象和分时系统

软件测试薪资过万?送你10条秘籍附图

做软件测试,想薪资过万? 送你10条秘籍!附图

《程序员修炼手册》心得