• Angular复习笔记5-指令


     

    Angular复习笔记5-指令

    在Angular中,指令是一个重要的概念,它作用在特定的DOM元素上,可以扩展这个元素的功能,为元素增加新的行为。本质上,组件可以被理解为一种带有视图的指令。组件继承自指令,是指令的一个子类,通常被用来构造UI控件。

    指令的使用并不复杂,它与HTML元素属性的使用方式相似。不同的是,HTML语法标准为HTML元素预定义了特定的属性,浏览器遵循这一语法标准,实现了这些属性的内置行为。语法标准预定义的属性是有限的、不可扩展的,而Angular中的指令是可自定义的、可任意扩展的,这在一定程度上弥补了标准HTML元素属性功能的不足。

    指令分类

    在angular中指令分为三类:属性型指令,结构型指令和组件。

    属性型指令

    顾名思义,属性指令是以元素属性的形式来使用的指令。与HTML元素的内置属性不同,指令是Angular对HTML元素属性的扩展,浏览器本身不能识别这些指令,指令仅在Angular环境中才能被识别使用。属性指令通常被用来改变元素的外观和行为,如在第7章中介绍过的Angular内置指令NgStyle,它可以基于组件的状态来动态设置目标元素的样式。

    结构型指令

    结构指令可以用来改变DOM树的结构。结构指令可以根据模板表达式的值,增加或删除DOM元素,从而改变DOM的布局。结构指令与属性指令的使用方式相同,都是以元素属性的形式来使用的。两者的区别在于使用场景不同,属性指令用来改变元素的外观和行为,而结构指令用来改变DOM树的结构。
    以Angular内置的结构指令NgIf为例,使用NgIf指令需要为指令绑定一个表达式,当表达式值为true时,该DOM元素及其子元素被添加至DOM中;当表达式值为false时,元素从DOM中被移除。示例代码如下:

    上面的代码当condition的值为true时会显示p元素,反之p元素会从DOM中移除。

    组件

    组件继承自指令,它的代码结构与指令也是相似的,不同之处在于组件有一个自描述的模板,并且组件是用@Component来修饰,而指令需要一个宿主元素,用@Directive来描述。

    组件与指令的部分生命周期钩子函数相同:

    尽管组件与指令有相似的结构和一些相同的生命周期钩子方法,但是它们也有一些不同点。不同于属性指令和结构指令,组件不是以HTML元素属性的形式使用的,而是以自定义标签的形式使用的,原因在于组件带有模板。组件可作为对HTML元素的扩展,将自身的模板视图插入DOM中;而属性指令和结构指令是对HTML元素属性的扩展,其作用是扩展已有DOM元素的行为和样式,或者改变这些元素在DOM中的结构。

    自定义属性型指令

    一个属性指令需要一个控制器类,该控制器类使用@Directive装饰器来装饰。@Directive装饰器指定了用以标记指令所关联属性的选择器,控制器类则实现了指令所对应的特定行为。

    首先需要从@angular/core中引入Directive和ElementRef。前者包含了修饰指令的装饰器@Directive,后者通过指令的构造函数传入,代表指令修饰的DOM元素宿主。

    @Directive({
      selector: '[appFirst]'
    })
    export class FirstDirective implements OnInit {
      constructor(el: ElementRef) { }
      ngOnInit(): void {
    
      }
    
    }

    上面的代码便是创建了一个指令,我们需要在@Directive中定义这个指令的选择器,选择器的规则遵循CSS选择器标准,例如上面的选择器是[appFirst],表示该指令会以属性的方式附着在元素上面。

    Angular会为每一个匹配的DOM元素创建一个指令实例,同时将ElementRef作为参数注入到控制器构造函数。使用ElementRef服务,可以在代码中通过其nativeElement属性直接访问DOM元素,这样就可以通过DOM API设置元素的背景色:

    @Directive({
      selector: '[appFirst]'
    })
    export class FirstDirective implements OnInit {
      constructor(private el: ElementRef) { }
      ngOnInit(): void {
        console.log(this.el);
        this.el.nativeElement.style.backgroundColor = 'red';
      }
    
    }

    为指令绑定输入

    在上述的代码中指令为宿主设置的颜色是固定的,如果要想动态的变更颜色,有两个思路:

    第一,设置一个@Input输入属性,并将这个属性定义为set函数:

    @Directive({
      selector: '[appFirst]'
    })
    export class FirstDirective {
      @Input() set backgroundColor(color: string) {
        if (color) {
          this.el.nativeElement.style.backgroundColor = color;
        }
      }
      constructor(private el: ElementRef) { }
    
    }

    第二,指令实现onChanges接口,在接口中处理逻辑:

    @Directive({
      selector: '[appFirst]'
    })
    export class FirstDirective implements OnChanges {
      @Input() backgroundColor: string;
      ngOnChanges(changes: SimpleChanges): void {
        this.el.nativeElement.style.backgroundColor = this.backgroundColor;
      }
      constructor(private el: ElementRef) { }
    
    }

    然后在宿主中使用这个指令:

     <p appFirst [backgroundColor]="color" matLine>template</p>

    在宿主中需要定义一个color变量并且改变这个color变量的值,这里就不演示了。

    响应用户操作

    可以使用@HostListener来响应用户发出的事件。@HostListener指向使用指令的DOM元素,使得DOM元素的事件与指令关联起来。@HostListener是另一种动态设置的方案,相比较前面那种@Input set输入属性的方式还有生命周期钩子的方式,这种方式是从响应用户发出的事件的角度执行的。

    //.....其他代码....
    @HostListener('click')
    onClick(){
    //.....代码逻辑....
    }

    自定义结构型指令

    不同于属性型指令,属性型指令会在构造函数中传入一个ElementRef来表示它的宿主DOM元素,而结构型指令需要在构造器中传入两个参数,一个是TempleteRef,另一个是ViewContainerRef.这是因为被结构型指令修饰的DOM元素会被angular转译成一个<ng-temeplate>元素:

    <span *ngIf="condition">can you see it?</span>

    上面的代码会被angular转译为:

    <ng-temeplate [ngIf]="condition">
    <span>can you see it?</span>
    </ng-template>

    可以看出结构型指令会被转译成一个属性型的指令。

    所以,结构型指令需要传入一个TemeplateRef的服务来访问到这个组件模板(被转译成的ng-template)。至于另一个ViewContainerRef,这个服务是可以从DOM中创建或者删除一个TemplateRef所代表的模板元素,要取决于对结构型指令求值的boolean值(因为给结构型指令绑定的表达式必须是一个boolean类型的表达式)。

    下面代码简单定义了一个结构型的指令:

    @Directive({
      selector: '[appFirst]'
    })
    export class FirstDirective {
      constructor(templ: TemplateRef<any>, container: ViewContainerRef) { }
      @Input() condition: boolean;
    }

    看起来和属性型的指令差不多,不同之处在于构造函数传入的参数类型不同。使用上也和属性型指令差不多,需要注意的就是ViewContainerRef的两个方法,这两个方法都是用来操作指令中的TemplateRef所指向的模板的:

    createEmbeddedView():需要传入一个TemplateRef类型的参数,表示根据TemplateRef创建一个内嵌的视图模板。

    clear():将TemplateRef视图模板中删除。

  • 相关阅读:
    django admin site配置(二)
    MyEclipse中无法将SVN检出来的项目部署到tomcat中
    遍历目录树,清理编译目录
    axis2学习, ant 构建axis2 ws
    [置顶] 2013 Multi-University Training Contest 8
    Cocos2d-x 关于在iOS平台真机测试的一些注意
    SharePoint 2013的100个新功能之社交
    路由共享上网原理
    red ant
    nginx正向代理访问百度地图API
  • 原文地址:https://www.cnblogs.com/pangjianxin/p/10898771.html
Copyright © 2020-2023  润新知