目录
模板
组件中的模板有两种表现形式。
内联模板=>在@Component装饰器中通过template指定使用`符号(ES6中的提出的模板字符串)来进行字符串组装,注意不是'单引号。
模板文件=>在@Component装饰器中可以通过templateUrl指定,地址为对应的html文件的相对路径地址。
模板语法混淆点梳理
模板语法说白了就是怎么绑定数据,要理解绑定数据首先需要搞懂两个概念,什么是Html的 attribute?什么是DOM的property?在传统的赋值的方式中我们需要通过操作DOM来获取Html元素的attribute然后给它赋值,在Angular中我们绑定数据赋值其实绑定的不是attribute而是绑定的property,换句话说attribute其实不会变是固定的,而你设置的具体值是动态的是需要变的。比如<a [attribute]="变量">,我们关注的点是在变量上而不是attribute上。这也是为什么在MVVP模式下要弃用JQuery的原因,要尽量避免直接操作DOM元素,绑定对于数据来说已经够用了,除非你要一些比较酷炫的效果,比如基于canvas/svg搞一些事情的时候,操作DOM还是必不可免的。
上面这段话解释有人反映不太懂,理论不说了就来点直白的吧
我的html中有这么个input,当我用直接用DOM的property赋值的时候,尽管页面上显示的值已经变了,但它的属性值依然不会变,依然是渲染时被初始化的值。
Attribute绑定
是不是很乱,上面刚说完不是attribute,就又来了个attribute绑定,其实这是特例,CSS类绑定与样式绑定都属于Attribute绑定,Attribute绑定是比较特殊的场景,应用于DOM的property不存在的时候,比如td标签的colspan。
[(ngModel )]双向绑定
标准的双向绑定代表,其实是个语法糖,以下是演变过程。
@Component({
selector: 'test'
template:`
<div>
<button (click)="change()" value="change">
</div>
`
})
export class demo{
@Input() name: string;
@Output() nameChange: new EventEmitter<string>();
change(name){
this.name = name;
this.nameChange.emit(this.name)
}
}
以上点击button触发change进而触发nameChange
-------------------------------------------------------------------------
<test [name]="currentName" (nameChange)="currentName=$event"></test>
以上引用test组件,为其传入name属性值为currentName,同时接受nameChange事件并把接受到的参数赋值给currentName
-------------------------------------------------------------------------
<test ([name])="currentName"></test>
以上为Angualr提供的语法糖写法
以上只是推演的过程用于理解而已,针对input中的[()]其实是依靠ngModel指令,一直对外Angular隐藏了这些细节,所以如果需要使用input标签内的双向绑定,一定要引入FormsModule模块不然不会生效,因为ngModel执行不生效。
生命周期方法(钩子方法)
生命周期方法指的是组件的创建到销毁这段时间内的,暴露出来关键时刻的方法。什么时候会触发那?在Angular中组件新建,更新,销毁时都会触发。对于销毁是组件要从DOM中移除之前会销毁。关于方法的执行顺序如下。
指令
说明
什么是指令?其实就是一个呗@Directive修饰器所修饰的类,被挂载到元素的标签内,类似于标签的属性。
指令有什么用,什么时候用指令?其实指令是用来修改DOM的样式和行为,或者是用来操作DOM元素,避免咱们在写代码的时候直接调用DOM Api来直接操作DOM。
指令都有什么类型?常见的指令分三类属性指令,结构指令,和最通用的组件指令(ng-template)。
自定义属性指令
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@Input() defaultColor: string;
@Input('appHighlight') highlightColor: string;
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || this.defaultColor || 'red');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
---------------------------------------------------------------------------------
<p [appHighlight]="color" defaultColor="violet">
结构指令
*ngIf,*ngFor,ngSwitch揭秘,*是一个语法糖,本质是ng-template,*ngIf => <ng-template [ngIf]="xxx"></ng-template>。
ng-container,是一个分组元素,不会被真正渲染到DOM中,在Angualr中的作用类似于JS中的花括号,它不会破坏DOM的样式和布局,对于一些场景下需要一个外层的包装(没有合适的宿主元素)来执行一些指令的时候很有用。
<select [(ngModel) = "xxx"]
<ng-container *ngFor="let item of heroes>
<option [ngValue]="item">{{item}}</option>
</ng-container>
</select>
自定义结构指令
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({ selector: '[appUnless]'})
export class UnlessDirective {
private hasView = false;
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) { }
@Input() set appUnless(condition: boolean) {
if (!condition && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (condition && this.hasView) {
this.viewContainer.clear();
this.hasView = false;
}
}
}
---------------------------------------------------------------------------
<p *appUnless="condition">Show this sentence unless the condition is true.</p>
<ng-template [appUnless]="condition">
<p class="code unless">
(A) <ng-template [appUnless]="condition">
</p>
</ng-template>
管道
自定义管道
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
transform(value: number, exponent: string): number {
let exp = parseFloat(exponent);
return Math.pow(value, isNaN(exp) ? 1 : exp);
}
}