构建自定义指令
介绍
指令是 Angular 提供的一个非常重要的功能。即使是 Angular 组件实际上也是具有自己模板的高阶指令。
在本指南中,我们将研究各种类型的指令,然后学习如何根据应用程序的自定义要求构建一个指令。
指令类型
Angular 有两种主要类型的指令:
- 属性
- 结构
属性指令
属性指令应用于元素的属性。因此,它们有助于通过更新某些属性来操纵我们的 DOM,但不会创建或销毁元素。因此,它们也可以称为 DOM 友好型指令。它们只会更改所附加的 DOM 元素。它们主要使用数据绑定或事件绑定。
它们在以下场景中很有用:
- 有条件地将样式或类应用于某些元素
以下为示例:
<div [style.color]="'green'">Very excited to learn about building Custom Directives !!</div>
- 有条件地显示或隐藏元素
以下为示例:
<div [hidden]="showHideEl">Very excited to learn about building Custom Directives !!</div>
- 根据任何属性动态改变任何组件的行为
结构型指令
另一方面,结构型指令可以根据某些输入创建、删除或重新创建 DOM 元素。因此,它们通常不是 DOM 友好的。
让我们来谈谈“hidden”属性指令的作用。它将元素保留在 DOM 中,但仅将其对用户隐藏,而结构指令(例如 *ngIf)会从 DOM 中删除元素。
其他常用的结构指令是 *ngFor 和 *ngSwitch,它们可用于路由编程任务。
自定义指令
我们的应用中有很多用例,其中我们有自定义需求,并且必须根据需求创建自定义指令。这就是我们需要创建自定义指令的地方。
Angular 有一些基本的 API 可以帮助我们创建自定义指令。让我们首先看看如何创建自定义属性指令。
自定义属性指令
假设我们有一个需求,即我们希望任何元素都以特定的方式设置样式(例如,使用背景高亮颜色和一些文本/前景色)。我们可以在多个地方有这样的文本,因此我们可以为相同的文本创建自定义指令并在我们的应用程序中重复使用它。因此,本质上,我们希望能够按如下方式使用我们的指令:
<div class="para float-left" myHighlight>Some text to be highlighted !</div>
要创建指令,我们可以使用 Angular CLI 运行以下命令:
ng generate directive myHighlight
上述命令将自动更新 app.module.ts 中的条目。但是,如果我们手动创建指令,则需要在我们的 AppModule 中更新它,如下所示:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MyHighlightDirective } from './myhighlight.directive';
@NgModule({
imports: [ BrowserModule ],
declarations: [
AppComponent,
MyHighlightDirective
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
上述内容会将我们的新指令告知 Angular,这样它就知道在遇到模板中的选择器时要调用哪个类。
We'll now start with the decorator. So, any directive class has to be annotated by using a @Directive decorator.
Let us create a class called "MyHighlightDirective" and then use the @Directive decorator to associate our class with the attribute "myHighlight", as shown below:
import { Directive } from '@angular/core';
//...
//...
//...
@Directive({
selector:"[myHighlight]"
})
export class MyHighlightDirective { }
As you can see, we marked the class using the @Directive decorator and have to import the same from @angular/core.
Also, the code shown above is quite similar to how we write a component. One of the differences we can see is that our selector is wrapped inside []. This is because our selector attribute internally uses the same CSS matching rules to match any directive or component and map it to any HTML element.
Thus, if we have to select any particular element via CSS, we just write the name of the element like say div {background-color: 'green'}. And this is why, in the selector for the component in the @Component directive, we just specify the name of the component.
If we update the selector in our directive as below:
import { Directive } from '@angular/core';
//...
//...
//...
@Directive({
selector:".myHighlight"
})
export class MyHighlightDirective { }
With the above definition, our directive would be associated with any element which has a class "myHighlight" defined like:
<div class="para float-left myHighlight">Some text to be highlighted !</div>
For now, let us associate our directive to an element with the attribute "myHighlight".
Once we have our decorator added, the next step would be to add a constructor to our directive, as shown below:
import { Directive } from '@angular/core';
import { ElementRef } from '@angular/core';
//...
//...
//...
@Directive({
selector:"[myHighlight]"
})
export class MyHighlightDirective {
constructor(private elRef: ElementRef) {}
}
With the above code, we are telling Angular to inject an instance of ElementRef into its constructor, whenever the directive is created. This is actually via dependency injection.
ElementRef is used to get direct access to the DOM element on which our directive attribute is attached to.
Let's say we now want to change the background color of this element to green and the foreground color to blue. To do that, we'll write the following code:
el.nativeElement.style.backgroundColor = "green";
el.nativeElement.style.color = "blue";
ElementRef is actually a wrapper for our actual DOM element and we access the DOM element using the property nativeElement on it.
We'll write the above code inside our ngOnInit method. So this is how our directive class would now look like;
import { Directive } from '@angular/core';
import { ElementRef } from '@angular/core';
//...
//...
//...
@Directive({
selector:"[myHighlight]"
})
export class MyHighlightDirective {
constructor(private elRef: ElementRef) {}
ngOnInit() {
this.elRef.nativeElement.style.backgroundColor = "green";
this.elRef.nativeElement.style.color = "blue";
}
}
The only issue with the above style of using directives is that this would assume that our app would always run in a browser. However, that might not be the case always. We can have our app running in a different environment like on a native mobile device. Thus, Angular has provided with an API which is platform independent and we can set properties on our elements using Renderer.
This is how our class would get updated to if we use the Renderer helper:
import { Directive } from '@angular/core';
import { Renderer } from '@angular/core';
//...
//...
//...
@Directive({
selector:"[myHighlight]"
})
export class MyHighlightDirective {
constructor(private elRef: ElementRef, private renderer: Renderer) {}
ngOnInit() {
this.renderer.setStyle(this.elRef.nativeElement, 'background-color', 'green');
this.renderer.setStyle(this.elRef.nativeElement, 'color', 'blue');
}
}
The "setStyle" method has the following signature:
setStyle(element: any, style: string, value: any, flags?: RendererStyleFlags2): void
Thus, we are now updating our DOM element's style (background and foreground color) via the "Renderer" and not directly accessing our element, which is a good practice.
我们现在将硬编码的背景色和前景色应用于 DOM 元素。假设我们希望它是可配置的,这意味着我们的 DOM 元素可以说它需要背景色和前景色是特定的。因此,我们希望能够说:
<div class="para float-left" myHighlight [backgroundColor]="'black'" [foregroundColor]="'white'">Some text to be highlighted !</div>
上述内容可以借助 @Input 装饰器实现。我们还将使用 @HostBinding 装饰器来更新元素上的样式。让我们更新我们的类,展示 @Input 和 @HostBinding 的使用:
import { Directive } from '@angular/core';
import { Input } from '@angular/core';
import { HostBinding } from '@angular/core';
//...
//...
//...
@Directive({
selector:"[myHighlight]"
})
export class MyHighlightDirective {
@Input() backgroundColor:string = 'green';
@Input() foregroundColor:string = 'blue';
@HostBinding('style.backgroundColor') bgColor:string;
@HostBinding('style.color') color:string;
constructor() {}
ngOnInit() {
this.bgColor = this.backgroundColor;
this.color = this.foregroundColor;
}
}
如果您看到上述代码,我们现在有背景色和前景色的默认值(即绿色和蓝色)。但是,如果通过数据绑定将这些值传递给指令,我们将使用这些值来设置元素的样式。
我们还可以给指令附加事件监听器。比如,当用户将鼠标悬停在文本上时,我们希望文本具有不同的背景和前景色。因此,我们希望能够将其传递给指令:
<div class="para float-left" myHighlight
[backgroundColor]="'black'"
[foregroundColor]="'white'"
[hoverBackgroundColor]="'yellow'"
[hoverForegroundColor]="'red'">Some text to be highlighted !</div>
为了做到这一点,我们需要使用@HostListener 装饰器,如下所示:
import { Directive } from '@angular/core';
import { Input } from '@angular/core';
import { HostBinding } from '@angular/core';
import { HostListener } from '@angular/core';
//...
//...
//...
@Directive({
selector:"[myHighlight]"
})
export class MyHighlightDirective {
@Input() backgroundColor:string = <span class="hl
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~