Angular2 Directive 学习笔记-基础篇

在Angular2中有三种类型的指令(Directive)
1. 组件 — 拥有模板的指令。
2. 结构型指令 - 通过添加和移除DOM元素来改变DOM结构的指令。例如:NgFor,NgIf …
3. 属性型指令 - 改变元素显示和行为的指令。例如:NgStyle …

Tips: 关于组件型的指令介绍会在专门的文章里面细致的介绍,毕竟Angular2中组件(Component)是如此的重要。

从创建一个简单属性型指令开始

在Angular2中,属性型指令的创建至少需要一个带有@Directive装饰器修饰的控制器类。@Directive装饰器指定了一个选择器名称,用于指出与此指令相关联的属性的名字。
控制器类实现了指令所具备的行为能力。接下来,开始创建一个简单的属性型指令,该指令的功能是,单鼠标在其修饰的元素上悬停时,改变其修饰元素的背景颜色。
创建一个highlight.directive.ts文件,其代码结构如下:

import { Directive,ElementRef,Renderer } from '@angular/core';

@Directive({
    selector: '[prefixHightlight]'
})
export class HighlightDirective {
    constructor(elem: ElementRef,renderer: Renderer) {
        renderer.setElementStyle(el.nativeElement,'backgroundColor','red');
    }
}

代码注解:

  1. 首先使用 import 语句从Angular Core库中导入我们需要使用的一些功能模块。
    使用 Directive 模块提供的 @Directive 修饰器,对 HighlightDirective 类进行功能修饰;
    在 HighlightDirective 类的构造函数中注入 ElementRef 和 Renderer 模块的实例(这里涉及到的DI-依赖注入关系之后也会有详细的学习笔记介绍)。
    注入 ElementRef 的目的在于让指令可以引用到真实的 DOM 元素。ElementRef这个类可以用来在宿主标签内注入其它标签的应用,这些应用并不仅仅局限于 DOM 元素。ElementRef这个类可以用来在宿主标签内注入其它标签的应用,这些应用并不仅仅局限于 DOM 元素。
    Renderer 可以让我们在 Hightlight 类里面的逻辑代码能够正确的渲染 DOM 元素的样式。
  2. 在倒入需要使用的功能模块后,我们使用@Directive装饰器以‘配置对象’参数的形式,对指令的元数据进行说明。
    属性型指令的@Directive装饰器需要一个css选择器来指定selector的值,以便Angular编译器从模版中识别出关联到这个指令的HTML。例如:代码中的‘[]’对应的就是css选择器中的属性选择器。
  3. 紧接着在@Directive元数据后面,我们声明并导出里指令的控制器类 HighlightDirective,HighlightDirective类包含了prefixHightlight指令的工作逻辑。别忘记了导出指令的控制器,
    这样才能够让我们定义的指令被别的指令(组件)访问。
  4. 需要注意的是:属性型指令的 selector 必须要对应 CSS 的属性选择器,同时命名需要遵循驼峰式命名方式
Tips: 在Angualr的最佳实践中推荐,
1. 我们应该为我们定义的每一个指令,组件,服务都添加上前缀。这样做的好处在于:
   确保我们自己声明定义的这些指令,组件,服务不会与标准的HTML属性冲突,也降低与第三方指令,组件,服务冲突的可能性。   
2. 指令的名称应该具备一定的自解释性,这样方便我们通过指令的名称就能大概知道指令的用途。

Angular会为每一个被指令匹配上的元素创建一个该指令对应的控制器类的实例。并自动注入该控制器类依赖的别的类的实例。比如上面的代码中,Angualr会选择合适的时机,为我们注入 ElementRef 和 Renderer 的实例。
Angular具体怎么实现的依赖注入之后的学习笔记会在一起学习,如果使用过Spring等Java框架的小伙伴一定对DI不会陌生的。ElementRef 是一个服务,它赋予了我们直接访问 DOM 元素的能力。
通过 ElementRef 的 nativeElement 属性和 Renderer 服务的组合使用,我们便实现了我们需要的指令能力。

如何使用属性型指令呢?

这个~~~对于Angualr2内置的指令的使用相信大家的已经用的不能在溜溜。我们这里学习一下如何在别的指令,组件中使用我们定义的组件的方法。
要使用我们刚才定义的指令,我们需要创建一个模板,并把这个指令作为一个属性应用到一个DOM元素上,也就是我们需要为我们定一个这个指令找到一个宿主元素。
我们先来定一个组件的模板,并取名字叫做:app.component.html

<h1>Angualr2 Directive Study</h1>
<article prefixHightlight>hover me</article>

模板定义完成后,我们开始定义我们的组件,将组件的文件名称定义为:app.component.ts,其代码结构如下:

import { Component,OnInit } from '@angualr/core';

@Component({
    selector: 'prefix-app',templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit {
    constructor() {}

    ngOnInit(): void {
        console.log(`AppComponent has inited : ${ Date.Now() }`);
    }
}

接着我们声明一个Angular的模块,并在这个Angular模块中显示的声明我们自己定义的指令,以便Angualr在解析模板时,能够正确的识别我们自己定一个指令。
我们将模块的名称定义为:app.module.ts,其代码结构如下:

import { NgModule } from '@angular/core';
import { browserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { HighlightDirective } from './highlight.directive';

@NgModule({
  imports: [ browserModule ],declarations: [
    AppComponent,HighlightDirective
  ],bootstrap: [ AppComponent ]
})
export class AppModule { 

}

一定要记住在 @NgModule 的 declarations 数组中显示的声明我们定义的指令。否则,我们运行我们的应用时,浏览器会无情的跟我们抛出下面的错误提示:

EXCEPTION: Template parse errors:
Can’t bind to ‘prefixHighlight’ since it isn’t a kNown property of ‘article’.

简单的来总结一下自定义的属性型指令的运行原理:
Angular在编译模板时,检测到DOM元素上我们正在尝试绑定到某些属性 ,但Angualr并不能认识这些属性(非内置的属性指令)。
Angular就会尝试在我们声明的 declarations 元数据数组中查找这个指令属性。 我们把 HighlightDirective 在元数据的 declarations 数组中进行了声明,
这样一来 Angular 在发现这个指令的导入信息后,接着就会去检查对应的导入语句,从而找到 highlight.directive.ts 中导出的类,进而服务宿主元素对应的行为能力。

实现预定的目标

如果你动手实践了上面已经完成的代码,你会惊讶的发现,我们并没有实现我们最初设想的功能,我们仅仅是对使用了prefixHighlight指令的元素简单粗暴的设置了其背景颜色,
并没有实现预期的鼠标悬停在宿主元素上时才变换背景颜色的功能。
为了现实我们预期的目标,我们需要我们定义的指令能够感知到鼠标事件,并能够在这些事件触发时能够进行特定的行为。
下面我们就开始来改造我们的指令,找到highlight.directive.ts文件,并开始动手修改其代码结构如下:

import { Directive,Renderer,HostListener } from '@angular/core';

@Directive({
    selector: '[prefixHightlight]'
})
export class HighlightDirective {

    private _domElem: ElementRef;
    private _renderer: Renderer;

    constructor(elem: ElementRef,renderer: Renderer) {
        this._domElem = elem.nativeElement
        this._render = renderer;
        //renderer.setElementStyle(elem.nativeElement,'backgroundColor','red');
    }

    @HostListener('mouseenter')
    onMouseEnter() {
        this._render.setElementStyle(this._domElem,'red');
    }

    @HostListener('mouseleave')
    onMouseLeave() {
        this._render.setElementStyle(this.domElem,null);
    }
}

代码解释:

我们新导入了 HostListener 类,并使用了这个类的 @HostListener 装饰器。这个装饰器引用了我们定义的属性型指令的宿主元素,在我们的学习笔记中就是 \

如何封装功能强大的指令--让指令可做的事情更多

通过上面的修改,我们已经让我们定义的 prefixHighlight 指令具备了我们预期的功能。
但是我们还可以让它更加灵活和强大。接下来就让我们一起来让我们定义指令具备更丰富的能力能够做更多的事情,让它真正的实现‘器大活好’的目标。

我们需要的是灵活可配置的指令

现在我们实现的指令中高亮颜色是在指令中硬编码进去的,并没有弹性。 我们期望我们的指令是灵活的,这个颜色能够通过绑定从外部设置。例如:

<article [pefixHighlight]="yellow">hover me</article>

还是老规矩,先上代码,之后在解释。接着修改highlight.directive.ts文件,修改后其代码结构如下:

import { Directive,HostListener,Input } from '@angular/core';

@Directive({
    selector: '[prefixHightlight]'
})
export class HighlightDirective {

    private _domElem: ElementRef;
    private _renderer: Renderer;

    private _defaultColor = 'red';

    constructor(elem: ElementRef,'red');
    }

    @Input('prefixHighlight') highlightColor: string;

    @HostListener('mouseenter')
    onMouseEnter() {
        this._render.setElementStyle(this._domElem,this.highlightColor || this._defaultColor);
    }

    @HostListener('mouseleave')
    onMouseLeave() {
        this._render.setElementStyle(this._domElem,null);
    }
}

代码解释:

在上面的代码中,我们从 Angaulr Core 中新导入了 Input 模块,这个模块提供一个 @Input 装饰器,通过这个装饰器,使得我们的指令,能够接受外部的输入值,根据不同的输入值表现出不同的能力。
上面的代码中,我们定义了一个新的属性 highlightColor 并使用了 @Input 装饰器对其进行了装饰,这样一来 @Input 会把相关元数据添加到了类上,让 highlightColor 能被以 myHighlight 为别名进行绑定。
被 @Input 装饰器修饰的属性也被称为‘输入属性’。

通过上面的修改,我们基本实现了一个属性型指令的定义和使用,也简单粗暴的实现了一些指令输入的功能,由于时间关系(其实是学习程度的问题),这里先不做更深入的介绍,等我打完怪回来继续完善…
这篇学习笔记的结尾总感觉有点儿那什么,哎,算了。凑活看吧!以后打完怪在接着完善进阶篇,这样才有美剧的风格~~~~哈哈哈哈哈哈哈哈~~~~~

彩蛋吗???

额外的知识点:

上面的代码解释中提到了输入属性,这里就简单的介绍一下什么叫做输入属性吧。这样一来可以让这篇没有结尾的学习笔记看起来并没有哪吗突兀啦!
PS:关于 @Input @Output 的详细学习会在后面的进阶篇里面和大家一起学习

指令的 输入(Input)属性

我们在上面的最后一次修改中添加的 highlightColor 属性是 HighlightDirective 指令的一个 input 属性。
为了更好的理解为什么需要输入属性这个东西,我们需要先理解一下,
Angular在绑定的绑定的目标 之间的一个巧妙但重要的区别。
关于目标的一个简单定义:

如果属性出现在了模板表达式等号 (=) 的右侧,它就是一个。如果它出现在了方括号 ([ ]) 中,并且出现在等号 (=) 的左侧,它就是一个目标**

就像在绑定到 HighlightDirective 的 myHighlight 属性时所做的那样。

<p [prefixHighlight]="color">hover me</p>

[prefixHighlight]=”color” 中的 ‘color’ 就是绑定源属性不需要特别声明
[prefixHighlight]=”color” 中的 ‘prefixHighlight’ 就是绑定目标。 必须把它定义为一个 Input 属性,否则,Angular 就会拒绝这次绑定,并给出一个明确的错误。

当前只需要记住,通过@Input装饰的输入声明可以确保指令的消费者只能绑定到公开的 API 中的属性,而不是其它的属性。有效的保护了指令或组件的封装性。

Angular2 Directive 学习笔记-基础篇的更多相关文章

  1. HTML5 input新增type属性color颜色拾取器的实例代码

    type 属性规定 input 元素的类型。本文较详细的给大家介绍了HTML5 input新增type属性color颜色拾取器的实例代码,感兴趣的朋友跟随脚本之家小编一起看看吧

  2. 移动HTML5前端框架—MUI的使用

    这篇文章主要介绍了移动HTML5前端框架—MUI的使用的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  3. 使用placeholder属性设置input文本框的提示信息

    这篇文章主要介绍了使用placeholder属性设置input文本框的提示信息,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下

  4. Bootstrap File Input文件上传组件

    这篇文章主要介绍了Bootstrap File Input文件上传组件,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. HTML5中input输入框默认提示文字向左向右移动的示例代码

    这篇文章主要介绍了HTML5中input输入框默认提示文字向左向右移动,本文通过实例代码给大家介绍的非常详细对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. swift 正则表达式运用实例选自《swifter 100个swift开发必备tip 》

  7. Swift 2.0关键字guard

    viewmode=list前言:当一项新的技术出来的时候,第一参考自然是文档。文档链接guard语句guard语句的作用是:当某些条件不满足的情况下,跳出作用域举个例子:写个函数,保证输入小于10在playground输入如下可以看到输出上述方法和使用if一样但是使用guard有一个好处如果不使用return,break,continue,throw跳出当前作用域,编译器会报错所以,对那些对条件要求十分严格的地方,guard是不二之选。另外,guard也可以使用可选绑定也就是guardlet的格式例如如何

  8. Swift 柯里化(currying)和反柯里化(uncurrying)

    //DemoofcurryingfuncaddTwoNums(a:Int)(num:Int)->Int{returna+num}letaddToFour=addTwoNums(4)letresult=addToFour(num:6)print("result:\(result)")funcgreaterThan(comparor:Int)(input:Int)->Bool{returninput>

  9. swift – 上下文类型“AnyObject”不能与字典文字一起使用?

    我正在尝试将Objective-C示例转换为Swift2,但我遇到一个小问题。原来的Objective-C片段:我认为Swift代码应该是:结果错误是:在这种情况下,如何将Objective-C转换成Swift?因此,声明数组更具体在Swift3中用于JSON集合类型或字典/数组仅包含值类型使用

  10. swift – 我可以指定generic是值类型吗?

    我知道我们可以通过使用AnyObject来基本上指定我们的泛型是任何引用类型:但是有没有办法指定我们的泛型应该只是值类型,不允许引用类型?

随机推荐

  1. Angular2 innerHtml删除样式

    我正在使用innerHtml并在我的cms中设置html,响应似乎没问题,如果我这样打印:{{poi.content}}它给了我正确的内容:``但是当我使用[innerHtml]=“poi.content”时,它会给我这个html:当我使用[innerHtml]时,有谁知道为什么它会剥离我的样式Angular2清理动态添加的HTML,样式,……

  2. 为Angular根组件/模块指定@Input()参数

    我有3个根组件,由根AppModule引导.你如何为其中一个组件指定@input()参数?也不由AppModalComponent获取:它是未定义的.据我所知,你不能将@input()传递给bootstraped组件.但您可以使用其他方法来做到这一点–将值作为属性传递.index.html:app.component.ts:

  3. angular-ui-bootstrap – 如何为angular ui-bootstrap tabs指令指定href参数

    我正在使用角度ui-bootstrap库,但我不知道如何为每个选项卡指定自定义href.在角度ui-bootstrap文档中,指定了一个可选参数select(),但我不知道如何使用它来自定义每个选项卡的链接另一种重新定义问题的方法是如何使用带有角度ui-bootstrap选项卡的路由我希望现在还不算太晚,但我今天遇到了同样的问题.你可以通过以下方式实现:1)在控制器中定义选项卡href:2)声明一个函数来改变控制器中的散列:3)使用以下标记:我不确定这是否是最好的方法,我很乐意听取别人的意见.

  4. 离子框架 – 标签内部的ng-click不起作用

    >为什么标签标签内的按钮不起作用?>但是标签外的按钮(登陆)工作正常,为什么?>请帮我解决这个问题.我需要在点击时做出回复按钮workingdemo解决方案就是不要为物品使用标签.而只是使用divHTML

  5. Angular 2:将值传递给路由数据解析

    我正在尝试编写一个DataResolver服务,允许Angular2路由器在初始化组件之前预加载数据.解析器需要调用不同的API端点来获取适合于正在加载的路由的数据.我正在构建一个通用解析器,而不是为我的许多组件中的每个组件设置一个解析器.因此,我想在路由定义中传递指向正确端点的自定义输入.例如,考虑以下路线:app.routes.ts在第一个实例中,解析器需要调用/path/to/resourc

  6. angularjs – 解释ngModel管道,解析器,格式化程序,viewChangeListeners和$watchers的顺序

    换句话说:如果在模型更新之前触发了“ng-change”,我可以理解,但是我很难理解在更新模型之后以及在完成填充更改之前触发函数绑定属性.如果您读到这里:祝贺并感谢您的耐心等待!

  7. 角度5模板形式检测形式有效性状态的变化

    为了拥有一个可以监听其包含的表单的有效性状态的变化的组件并执行某些组件的方法,是reactiveforms的方法吗?

  8. Angular 2 CSV文件下载

    我在springboot应用程序中有我的后端,从那里我返回一个.csv文件WheniamhittingtheURLinbrowsercsvfileisgettingdownloaded.现在我试图从我的角度2应用程序中点击此URL,代码是这样的:零件:服务:我正在下载文件,但它像ActuallyitshouldbeBook.csv请指导我缺少的东西.有一种解决方法,但您需要创建一个页面上的元

  9. angularjs – Angular UI-Grid:过滤后如何获取总项数

    提前致谢:)你应该避免使用jQuery并与API进行交互.首先需要在网格创建事件中保存对API的引用.您应该已经知道总行数.您可以使用以下命令获取可见/已过滤行数:要么您可以使用以下命令获取所选行的数量:

  10. angularjs – 迁移gulp进程以包含typescript

    或者我应该使用tsc作为我的主要构建工具,让它解决依赖关系,创建映射文件并制作捆绑包?

返回
顶部