Angular 开发问题记录

679 阅读3分钟

在使用了ngIf的标签内的button元素上添加disabled属性[disabled]="boolean"报错:

Can't bind to 'disabled' since it isn't a known property of 'a'.

原因:

_The disabled attribute is another peculiar example. A button's disabled property is false by default so the button is enabled. When you add the disabled attribute, its presence alone initializes the button's disabled property to true so the button is disabled.
_Adding and removing the disabled attribute disables and enables the button. The value of the attribute is irrelevant, which is why you cannot enable a button by writing <button disabled="false">Still Disabled</button>.

参考 stackoverflow 上的问答找到解决方法:

使用 [class.btn-lg]="someValue"

如:[attr.disabled]="boolean ? true : null"

注意:somevalue 只能是 string 不能是 boolean

@ViewChild 和 @ViewChildren 的使用

@ViewChild 的使用场景是:在父组件中需要实例化子组件,调用子组件中的方法或获取子组件中的数据;

使用示例:

child.ts

import ...
...
someFun() {
  console.log("我是子组件的方法");
}
...

parent.ts

import { ..., ViewChild} from  '@angular/core';
...
@ViewChild('child1') child1;       //可以声明多个子组件
...
useChildFun() {
  this.child1.someFun();             //将会打印出 “我是子组件的方法”
}
...

parent.html

<app-child  #child></app-child>

@ViewChildren 的使用场景是:在父组件中需要实例化一组子组件,遍历调用子组件中的方法或遍历获取子组件中的数据;具体场景:父组件中存在一个子组件列表,其中每个子组件都是动态添加的,具有不确定性;需要调用每个子组件中的方法进行数据处理等操作;

使用示例:

child.ts

import ...
...
someFun() {
  console.log("我是子组件的方法");
}
...

parent.ts

import { ..., ViewChildren} from  '@angular/core';
...
@ViewChildren('childList') childList;       //可以声明多个子组件列表
...
useChildFun() {
  this.childList._result.map(item => {
    item.someFun()                          //将会打印出 “我是子组件的方法”
  })
}
...

parent.html

<app-child  #child></app-child>

关于`ExpressionChangedAfterItHasBeenCheckedError`报错原因及解决方案

  • 原因:angular的渲染流程

Angular 程序其实是一个组件树,在变更检测期间,Angular 会按照以下顺序检查每一个组件(注:这个列表称为列表 1):

  • 更新所有子组件/指令的绑定属性
  • 调用所有子组件/指令的三个生命周期钩子:ngOnInitOnChangesngDoCheck
  • 更新当前组件的 DOM
  • 为子组件执行变更检测(注:在子组件上重复上面三个步骤,依次递归下去)
  • 为所有子组件/指令调用当前组件的ngAfterViewInit生命周期钩子

在每一次操作后,Angular 会记下执行当前操作所需要的值,并存放在组件视图的oldValues属性里(注:Angular Compiler 会把每一个组件编译为对应的 view class,即组件视图类)。在所有组件的检查更新操作完成后,Angular 并不是马上接着执行上面列表中的操作,而是会开始下一次 digest cycle ,即 Angular 会把来自上一次 digest cycle 的值与当前值比较(注:这个列表称为列表 2):

  • 检查已经传给子组件用来更新其属性的值,是否与当前将要传入的值相同
  • 检查已经传给当前组件用来更新 DOM 值,是否与当前将要传入的值相同
  • 针对每一个子组件执行相同的检查(注:就是如果子组件还有子组件,子组件会继续执行上面两步的操作,依次递归下去。)

所以我的代码开始时的渲染是没问题,问题出在后面的变更检测。

  • 解决方案:异步更新

不管是第一次变化监测还是第二次的验证监测都是同步执行的。这意味着如果我们在代码中异步更新属性的值,那么在第二次验证循环运行时这些属性是不会被改变的,那么也就不会报错了。

setTimeout(() => {
   ......;
});

实测有效。

知乎上有一篇文章讲的很详细:《传送门》。

  • angular from setValidators 手动添加验证规则

使用场景:当需要对表单的部分输入项(都是必填项)进行分类展示时的校验工作

// 添加
this.form.controls["firstName"].setValidators([Validators.minLength(0), Validators.maxLength(11)]);

this.validateForm.get('name').setValidators([Validators.required]);

// 清除
this.validateForm.get('name').clearValidators();

数据发生变化页面却没有更新的解决方法:

使用angular自带的API来刷新数据

constructor(private pageRef:ChangeDetectorRef) {}

fun() {
    ...
    <!--添加代码-->
        this.pageRef.markForCheck();        this.pageRef.detectChanges();    <!--添加代码-->
    ...
}