• Angular6 学习笔记——指令


    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址:

    https://www.angular.cn/guide/template-syntax

    http://www.ngfans.net/topic/12/post/2

    系列目录

    (1)组件详解之模板语法

    (2)组件详解之组件通讯

    (3)内容投影, ViewChild和ContentChild

    (4)指令

    (5)路由

    指令

     大家应该都知道,在html中存在一些附加在元素节点上的标记,例如属性,事件等等.它们能够改变元素的行为,甚至操作DOM,改变DOM元素,以及它的各级子节点.

     那么,在angular中也有这样的存在,那就是指令.

     在 Angular 中有三种类型的指令:

    1. 组件 — 组件的特殊存在,拥有模板

    2. 结构型指令 — 通过添加和移除 DOM 元素改变 DOM 布局的指令,常用的有:*ngIf,*ngFor,ngSwitch

    3. 属性型指令 — 改变元素、组件或其它指令的外观和行为的指令,常用的有:NgClass,NgStyle

    组件

     在查看源代码,我们会发现这样的画面

     所以说组件是继承指令的,只是它比较特殊,有模版

    同样,因为组件继承指令,在下面属性型和结构型指令的一系列操作,在组件中都是可以实现的

    但是因为指令的目的是为了复用,在组件中这样操作是达不到这个目的,所以强烈不推荐这样去操作.

    这里就不多做说明,有兴趣可以自己尝试一下

    属性型指令

    上面说道属性型指令是用来改变元素、组件或其它指令的外观和行为

    那么我们如何打造属于自己的属性型指令呢?

    首先,创建指令很像创建组件。

    • 导入 Directive 装饰器(而不再是 Component)。

    • 导入符号 Input、TemplateRef 和 ViewContainerRef,视指令类型与需求来选择

    • 给指令类添加装饰器。

    • 设置 CSS 属性选择器 ,以便在模板中标识出这个指令该应用于哪个元素。

    对于指令而言,至少需要一个带有@Directive装饰器的控制器类。该装饰器指定了一个用于标识属性的选择器。 控制器类实现了指令需要的指令行为。

    在我们存放指令的文件夹下(例如src/app/directives)下执行下面cli命令

    ng generate directive name(简写ng g d name)

    就可以快速得到src/app/directives/name.directive.ts和相应的测试文件src/app/directives/name.directive.spec.ts

    并且在根模块AppModule中声明这个指令类。

     根据命令快速创建一个名为highlight的指令。基本代码如下:

     1 import { BrowserModule } from '@angular/platform-browser';
     2 import { NgModule } from '@angular/core';
     3 
     4 import { AppRoutingModule } from './app-routing.module';
     5 import { AppComponent } from './app.component';
     6 import { HighlightDirective } from './directive/highlight.directive';
     7 
     8 @NgModule({
     9   declarations: [
    10     AppComponent,
    11     HighlightDirective,
    12   ],
    13   imports: [
    14     BrowserModule,
    15     AppRoutingModule
    16   ],
    17   providers: [],
    18   bootstrap: [AppComponent]
    19 })
    20 export class AppModule { }
     1 import { Directive } from '@angular/core';
     2 
     3 @Directive({
     4   selector: '[appHighlight]'
     5 })
     6 export class HighlightDirective {
     7 
     8   constructor() { }
     9 
    10 }

    Warnning

    和组件一样,这些指令也必须在Angular模块中进行声明

    指令所在的元素就是它的宿主元素

     下面以一个实例展示如何自定义属性型指令:highlight的功能如下

        1能够接收2个参数,其中一个为另外一个的默认值

        2监听宿主元素的鼠标进入和离开事件,在进入时宿主背景颜色为上述传入的值

    具体代码如下

    src/app/directives/highlight.directive.ts

     1 import { Directive, ElementRef, HostListener, Input } from '@angular/core';
     2 
     3 @Directive({
     4   //指定指令的属性型选择器
     5   selector: '[appHighlight]'
     6 })
     7 
     8 export class HighlightDirective {
     9   @Input('appHighlight') highlightColor: string;
    10   @Input() defaultColor: string;
    11 
    12   //构造函数中使用 ElementRef 来注入宿主 DOM 元素的引用
    13   constructor(private el: ElementRef) { }
    14 
    15   //监听宿主元素mousenter事件
    16   @HostListener('mouseenter') onMouseEnter() {
    17     this.highlight(this.highlightColor || this.defaultColor);
    18   }
    19 
    20   //监听宿主元素mousenter事件
    21   @HostListener('mouseleave') onMouseLeave() {
    22     this.highlight(null);
    23   }
    24 
    25   private highlight(color: string) {
    26     //ElementRef通过其 nativeElement 属性,提供直接访问宿主 DOM 元素的能力。
    27     this.el.nativeElement.style.backgroundColor = color;
    28   }
    29 }

    宿主元素用法

      1 <p appHighlight="red" defaultColor="black">宿主元素</p> 

    最终效果

    结构性指令

    结构性基本知识和上面的属性型指令差不多,但是它必须导入 Input、TemplateRef 和 ViewContainerRef,因为我们必须得有值来确定的是什么样的结构.

    在说道结构性指令的时候,例如*ngIf,*ngFor(ngSwitch其实也有,不过是在内部),都会看到前面有一个*号,它是用来干嘛的呢?

    其实这里星号是一个用来简化更复杂语法的“语法糖”,从内部实现来说:以*ngIf为例,Angular 把 *ngIf 属性翻译成一个 <ng-template> 元素 并用它来包裹宿主元素

    1 <ng-template [ngIf]="bool">
    2   <div class="name">{{hero.name}}</div>
    3 </ng-template>

    <ng-template>指令

    <ng-template>是一个 Angular 元素,用来渲染 HTML。 它永远不会直接显示出来。 事实上,在渲染视图之前,Angular 会把 <ng-template> 及其内容替换为一个注释。如果没有使用结构型指令,而仅仅把一些别的元素包装进 <ng-template> 中,那些元素就是不可见的。

    在下面的这个短语"Hip! Hip! Hooray!"中,中间的这个 "Hip!"不会显示就是如此。

    1 <p>Hip!</p>
    2 <ng-template>
    3   <p>Hip!</p>
    4 </ng-template>
    5 <p>Hooray!</p>

    但是结构型指令会让 <ng-template> 正常工作,在下面自定义结构型指令时就会看到这一点.

    <ng-container> 指令

    Angular 的 <ng-container> 是一个分组元素,但它不会污染样式或元素布局,因为 Angular 压根不会把它放进 DOM 中。

    下面是重新实现的条件化段落,这次使用 <ng-container>,都可以显示

    1 <p>
    2   I turned the corner
    3   <ng-container *ngIf="bool">
    4     and saw {{hero.name}}. I waved
    5   </ng-container>
    6   and continued on my way.
    7 </p>

    TemplateRef 和 ViewContainerRef

    像上面简单结构型指令例子,会从 Angular 生成的 <ng-template> 元素中创建一个内嵌的视图,并把这个视图插入到一个视图容器中.可以使用TemplateRef取得 <ng-template> 的内容,并通过ViewContainerRef来访问这个视图容器。可以把它们都注入到指令的构造函数中,作为该类的私有属性,详见下面自定义结构型指令

    自定义结构型属性

    我们来自定义一个名为appUnless的结构型指令,该指令的使用者会把一个 true/false 条件绑定到 [appUnless] 属性上。实现与*ngIf相反的功能

    具体代码如下

     1 import { Directive, TemplateRef, Input, ViewContainerRef } from '@angular/core';
     2 
     3 @Directive({
     4   selector: '[appUnless]'
     5 })
     6 export class UnlessDirective {
     7 
     8   private hasView: boolean = false;
     9 
    10   @Input() set appUnless(condition: boolean) {
    11     if (!condition && !this.hasView) {
    12       this.viewContainer.createEmbeddedView(this.templateref);
    13       this.hasView = true;
    14     }
    15     else if (condition && this.hasView) {
    16       this.viewContainer.clear();
    17       this.hasView = false;
    18     }
    19   }
    20 
    21   constructor(
    22     private templateref: TemplateRef<any>,
    23     private viewContainer: ViewContainerRef
    24   ) { }
    25 
    26 }
    <!-- ts里定义 condition: boolean = false;-->
    <p *appUnless="condition" class="unless a">
      (A) This paragraph is displayed because the condition is false.
    </p>
    
    <p *appUnless="!condition" class="unless b">
      (B) Although the condition is true,
      this paragraph is displayed because appUnless is set to false.
    </p>

    效果如下

     对于简单的段落,隐藏和移除之间的差异影响不大,但对于资源占用较多的组件是不一样的。 当隐藏掉一个元素时,组件的行为还在继续 —— 它仍然附加在它所属的 DOM 元素上, 它也仍在监听事件。Angular 会继续检查哪些能影响数据绑定的变更。 组件原本要做的那些事情仍在继续。所在在使用结构型指令操作时,我们需要明白这一点

    Warnning

    每个宿主元素上只能有一个结构型指令,你可能试过把 *ngFor 和 *ngIf 放在同一个宿主元素上,但 Angular 不允许。这是因为你在一个元素上只能放一个结构型指令。

    内置指令

    angular自带的有许多内置的指令,下面可以看见许多熟悉的身影:

    详情参见官网传送门,大家有兴趣就多去看看

    (终)

    文档信息


    感谢您的阅读,如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮。本文欢迎各位转载,但是转载文章之后必须在文章页面中给出作者和原文连接
  • 相关阅读:
    开发微博应用7构思草图
    微博应用研究【2】
    跟着Artech学习WCF扩展(4) 扩展MessageInspector
    ASP.NET开源MVC框架VICI 测试的便利性
    第一次踏出.net后花园(一)
    回忆被三层架构忽悠的日子,上当的同学自觉举手
    微博应用开发10
    开发微博应用【5】应用的使用频率
    微博应用研究(4)
    微博应用研究(3)
  • 原文地址:https://www.cnblogs.com/banluduxing/p/10402322.html
Copyright © 2020-2023  润新知