最近很多小伙伴都在问Angular自定义拖拽指令和angular拖拽组件这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展angular--自定义指令和模板、Angular4.x自定
最近很多小伙伴都在问Angular 自定义拖拽指令和angular 拖拽组件这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展angular -- 自定义指令和模板、Angular 4.x 自定义验证指令、angular – 创建自定义结构指令、angular 自定义指令等相关知识,下面开始了哦!
本文目录一览:Angular 自定义拖拽指令(angular 拖拽组件)
指令
组件是一种带模版的指令。指令是超级。
结构型指令(改变布局)和属性型指令(改变外观和行为)。
Renderer2和ElementRef
Angular不提倡直接操作DOM
对于DOM的操作应该通过Renderer2进行。
ElementRef是指向DOM元素的引用
拖拽指令实例
1、新建directive module
$ ng g m directive CREATE src/app/directive/directive.module.ts (193 bytes)
2, 在指令文件夹下的drag-drop文件夹里新建一个drag和一个drop
$ ng g d directive/drag-drop/drag $ ng g d directive/drag-drop/drop
3,改一下selector
selector: ''[appDrag]''改为 selector: ''[app-draggable]'' selector: ''[appDrop]''改为 selector: ''[app-droppable]''4,在SharedModule中导入DirectiveModule
import { NgModule } from "@angular/core"; import { CommonModule } from "@angular/common"; import { MaterialModule } from "../material/material.module"; import { ConfirmDialogComponent } from "./confirm-dialog/confirm-dialog.component"; import { DirectiveModule } from ''../directive/directive.module''; @NgModule({ imports: [CommonModule, MaterialModule, DirectiveModule], exports: [CommonModule, MaterialModule, DirectiveModule], declarations: [ConfirmDialogComponent], entryComponents: [ConfirmDialogComponent] }) export class SharedModule { }
5,把DirectiveModule中多余的CommonDodule删掉,然后把Drag和Drop两个指令导出
import { NgModule } from ''@angular/core''; import { DragDirective } from ''./drag-drop/drag.directive''; import { DropDirective } from ''./drag-drop/drop.directive''; @NgModule({ declarations: [DragDirective, DropDirective], exports: [DragDirective, DropDirective], }) export class DirectiveModule { }
6,drag指令
使用@HostListener监听dragstart事件和dragend事件。
使用ElementRef获取元素。
使用Renderer2修改样式。
draggedClass是一个输入型参数。所以selector 为selector: ''[app-draggable][draggedClass]''。
app-draggable设置为true就可以拖拽,为false不可拖拽。
private _isDraggble = false; set isDraggable(value){ this._isDraggble=value; } get isDraggable(){ return this._isDraggble; }
给set方法加上@Input(app-draggable)。 这样在调用app-draggable= "true"的时候会调用set方法。
import { Directive, HostListener, Host, ElementRef, Renderer2, Input } from ''@angular/core''; @Directive({ selector: ''[app-draggable][draggedClass]'' }) export class DragDirective { private _isDraggble = false; @Input(''app-draggable'') set isDraggable(value:boolean){ this._isDraggble=value; this.rd.setAttribute(this.el.nativeElement,''draggable'',`${value}`); } get isDraggable(){ return this._isDraggble; } @input() draggedClass:string; constructor(private el:ElementRef, private rd:Renderer2) { } @HostListener(''dragstart'', [''$event'']) ondragstart(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.rd.addClass(this.el.nativeElement, this.draggedClass);//往el上增加一个class } } @HostListener(''dragend'', [''$event'']) ondragend(ev:Event){ if(this.el.nativeElement===ev.target){ this.rd.removeClass(this.el.nativeElement, this.draggedClass); } } }
在task-item组件中使用
//红色dash虚线半透明
.drag-start { opacity: 0.5; border: #ff525b dashed 2px; }
<mat-list-item[@item]="widerPriority" [ngClass]="{ ''priority-normal'':item.priority===3, ''priority-important'':item.priority===2, ''priority-emergency'':item.priority===1 }" [app-draggable]="true" [draggedClass]=" ''drag-start'' " (click)="onItemClick()"> ...... </mat-list-item>
7,drop指令
drop要监听dragenter,dragover,dragleave,drop四个事件。
需要改变自己的css。即拖放区域的css。变暗。
import { Directive, HostListener, ElementRef, Renderer2, Input } from ''@angular/core''; @Directive({ selector: ''[app-droppable][dragenterClass]'' }) export class DropDirective { @input() dragenterClass:string; constructor(private el:ElementRef, private rd:Renderer2) { } @HostListener(''dragenter'', [''$event'']) ondragenter(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.rd.addClass(this.el.nativeElement, this.dragenterClass);//往el上增加一个class } } @HostListener(''dragover'', [''$event'']) onDragOver(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ } } @HostListener(''dragleave'', [''$event'']) onDragLeave(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.rd.removeClass(this.el.nativeElement, this.dragenterClass);//往el上增加一个class } } @HostListener(''drop'', [''$event'']) onDrop(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.rd.removeClass(this.el.nativeElement, this.dragenterClass);//往el上增加一个class } } }View Code
在task-home中使用drop指令
.drag-enter{ background-color: dimgray; }
<app-task-list *ngFor="let list of lists" app-droppable="true" [dragenterClass]=" ''drag-enter'' " > ...... </app-task-list>
至此,问题是不能多重拖拽(list-item和list都能拖拽,区分不开。)和携带数据。
8、解决多重拖拽和携带数据的问题
创建一个service
$ ng g s directive/drag-drop
在DirectiveModule中providers里声明一下
import { NgModule } from ''@angular/core''; import { DragDirective } from ''./drag-drop/drag.directive''; import { DropDirective } from ''./drag-drop/drop.directive''; import { DragDropService } from ''./drag-drop.service''; @NgModule({ declarations: [DragDirective, DropDirective], exports: [DragDirective, DropDirective], providers:[DragDropService] }) export class DirectiveModule { }
drag-drop.service.ts
import { Injectable } from ''@angular/core''; import { Observable,BehaviorSubject } from ''rxjs''; //数据结构 export interface DragData{ tag:string; //多重拖拽的话是哪一级拖拽,用户自己保证唯一性,不能重复 data:any; //传递的数据 } @Injectable({ providedIn: ''root'' }) export class DragDropService { //用BehaviorSubject总能记住上一次的值 private _dragData = new BehaviorSubject<DragData>(null); setDragData(data:DragData){ this._dragData.next(data); } getDragData():Observable<DragData>{ return this._dragData.asObservable(); } clearDragData(){ this._dragData.next(null); } constructor() { } }
9,更新drag
在drag的时候需要多定义一个属性dragTag,
constructor中注入新的DragDropService,
在开始拖拽的时候就给service set上数据,这里需要多定义一个dragData。import { Directive, HostListener, Host, ElementRef, Renderer2, Input } from ''@angular/core'';import { DragDropService } from ''../drag-drop.service'';
@Directive({ selector: ''[app-draggable][dragTag][dragData][draggedClass]'' }) export class DragDirective { private _isDraggble = false; @Input(''app-draggable'') set isDraggable(value:boolean){ this._isDraggble=value; this.rd.setAttribute(this.el.nativeElement,''draggable'',`${value}`); } get isDraggable(){ return this._isDraggble; } @input() draggedClass:string;
//多定义一个dragTag @input() dragTag:string;
//给DragDropservice传递的数据 @input() dragData:any constructor( private el:ElementRef, private rd:Renderer2,
//注入DragDropService private service:DragDropService) { } @HostListener(''dragstart'', [''$event'']) ondragstart(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.rd.addClass(this.el.nativeElement, this.draggedClass);//往el上增加一个class
//进入时候给service传递上数据
this.service.setDragData({tag:this.dragTag,data:this.dragData}); } } @HostListener(''dragend'', [''$event'']) ondragend(ev:Event){ if(this.el.nativeElement===ev.target){ this.rd.removeClass(this.el.nativeElement, this.draggedClass); } } }
10,更新drop
对于drop来讲,它的tags是一个数组,而不是字符串了。
因为拖放放的区域,可能会支持多个拖的区域。
所以放的时候原来的判断都有问题,首先需要判断这个拖拽是不是你能够接收的。
建立一个私有的data$,在constructor里订阅data。
drop指令还需要一个Output,因为需要什么时候drop。
import { Directive, HostListener, ElementRef, Renderer2, Input, Output, EventEmitter } from ''@angular/core''; import { DragDropService, DragData } from ''../drag-drop.service''; import { take } from ''rxjs/operators''; @Directive({ selector: ''[app-droppable][dropTags][dragenterClass]'' }) export class DropDirective { @Output() dropped = new EventEmitter<DragData>(); @input() dragenterClass:string; @input() dropTags:string[] = []; private data$; constructor( private el:ElementRef, private rd:Renderer2, private service:DragDropService) { this.data$ = this.service.getDragData().pipe( take(1)); } // @HostListener(''dragenter'', [''$event'']) // ondragenter(ev:Event){ // //判断drag元素是不是指令应用的元素发起的 // if(this.el.nativeElement===ev.target){ // this.rd.addClass(this.el.nativeElement, this.dragenterClass);//往el上增加一个class // } // } @HostListener(''dragenter'', [''$event'']) ondragenter(ev:Event){ //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.data$.subscribe(dragData=>{ if(this.dropTags.indexOf(dragData.tag)>-1){ this.rd.addClass(this.el.nativeElement, this.dragenterClass);//往el上增加一个class } }); } } // @HostListener(''dragover'', [''$event'']) // onDragOver(ev:Event){ // //判断drag元素是不是指令应用的元素发起的 // if(this.el.nativeElement===ev.target){ // } // } //dragover允许进行data transfer的一些特效 @HostListener(''dragover'', [''$event'']) onDragOver(ev:Event){ //需要支持多级拖拽,所以要防止事件冒泡 ev.preventDefault(); ev.stopPropagation(); //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.data$.subscribe(dragData=>{ if(this.dropTags.indexOf(dragData.tag)>-1){ this.rd.setProperty(ev,''dataTransfer.effectAllowed'',''all''); this.rd.setProperty(ev,''dataTransfer.fropEffect'',''move''); }else{ this.rd.setProperty(ev,''dataTransfer.effectAllowed'',''none''); this.rd.setProperty(ev,''dataTransfer.dropEffect'',''none''); } }); } } @HostListener(''dragleave'', [''$event'']) onDragLeave(ev:Event){ ev.preventDefault(); ev.stopPropagation(); //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.data$.subscribe(dragData=>{ if(this.dropTags.indexOf(dragData.tag)>-1){ this.rd.removeClass(this.el.nativeElement, this.dragenterClass);//往el上增加一个class } }); } } @HostListener(''drop'', [''$event'']) onDrop(ev:Event){ ev.preventDefault(); ev.stopPropagation(); //判断drag元素是不是指令应用的元素发起的 if(this.el.nativeElement===ev.target){ this.data$.subscribe(dragData => { if(this.dropTags.indexOf(dragData.tag)>-1){ this.rd.removeClass(this.el.nativeElement, this.dragenterClass);//往el上增加一个class this.dropped.emit(dragData);//drop的时候把dragData发射出去 this.service.clearDragData(); //drop的时候把data clear掉,否则会影响下一次拖拽 } }); } } }
11,改造模版调用
对于taskItem
<mat-list-item[@item]="widerPriority" [ngClass]="{ ''priority-normal'':item.priority===3, ''priority-important'':item.priority===2, ''priority-emergency'':item.priority===1 }" [app-draggable]= "true" [dragTag]= "''task-item''" [draggedClass]=" ''drag-start'' " [dragData]="item" (click)= "onItemClick()">
对于taskHome
既能drag又能drop
此外还要处理一个dropped事件
<div> <app-task-list *ngFor="let list of lists"app-droppable="true" [dropTags]="[''task-item'',''task-list'']" [dragenterClass]=" ''drag-enter'' " [app-draggable]="true" [dragTag]=" ''task-list'' " [draggedClass]=" ''drag-start'' " [dragData]="list" (dropped)="handleMove($event,list)" >
handleMove(srcData,List){ switch (srcData.tag) { case ''task-item'': console.log(''handling item''); break; case ''task-list'': console.log(''handling list''); default: break; } }
最终效果:
总结
以上是小编为你收集整理的Angular 自定义拖拽指令全部内容。
如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。
原文地址:https://www.cnblogs.com/starof/p/10662027.html
angular -- 自定义指令和模板
angular 可以自定义一些指令,来简化我们前端的工作量。
第一种:简单指令示例:
<h3>自定义指令</h3>
<sheng></sheng> <!-- 第一种展示方式 -->
JS示例:
var myApp = angular.module(''myApp'',[]);
myApp.directive("sheng",function(){
return {
template:"<h1>你好</h1>"
};
});
第二种:引入外部文件:
<h3>模板 - 引入外部文件</h3>
<div shi=""></div> <!-- 第二种展示方式 -->
JS示例:
myApp.directive("shi",function(){
return {
templateUrl:"page/template.html" // 使用这种必须要在服务器上测试
};
});
第三种:使用script标签模板:
<h3>模板 - script标签</h3>
<qu></qu>
<script id="qu" type="text/ng-template">script标签模板</script>
JS示例:
myApp.directive("qu",function(){
return {
templateUrl:"qu"
};
});
自定义指令的不同实现方式:
当以类名形式编写时,还必须在定义指令时添加restrict属性 (使用属性和标签的方式时不写也可正常显示)
restrict是“限制”的意思,属性值可以为E(element),A(attribute),C(class),当你希望实现方式只能是标签时,可以写restrict:“E”,restrict:“EAC”表示三种方式都可以,以此类推。
五、自定义模板内容
上面的模板内容都是固定的,实际开发中模板的内容都是由项目需要而生成的,如何让每一个模板的内容不一样呢? 使用 transclude
1. 在模板中的标签里添加 ng-transclude,(如果你需要某个标签里的内容自定义就添加ng-transclude属性,如果希望是固定的值就不需要加)
2. 添加属性 transclude:true
3. 在实现自定义指令时,把自定义的内容写在指令标签的里面即可。
下面是在做测试的时候,一个简单的示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript" src="./js/angular.min.js"></script>
</head>
<body ng-app="myApp" ng-controller="myAppController">
<h3>自定义指令</h3>
<sheng></sheng> <!-- 第一种展示方式 -->
<h3>模板 - 引入外部文件</h3>
<div shi=""></div> <!-- 第二种展示方式 -->
<h3>模板 - script标签</h3>
<qu></qu>
<script id="qu" type="text/ng-template">script标签模板</script>
</body>
<script type="text/javascript">
var myApp = angular.module(''myApp'',[]);
myApp.directive("sheng",function(){
return {
template:"<h1>你好</h1>"
};
});
myApp.directive("shi",function(){
return {
templateUrl:"page/template.html" // 使用这种必须要在服务器上测试
};
});
myApp.directive("qu",function(){
return {
templateUrl:"qu"
};
});
myApp.controller(''myAppController'',[''$scope'',function($scope){
}]);
</script>
</html>
Angular 4.x 自定义验证指令
表单是几乎每个 Web 应用程序的一部分。虽然 Angular 为我们提供了几个内置 validators (验证器),但在实际工作中为了满足项目需求,我们经常需要为应用添加一些自定义验证功能。接下来我们将着重介绍,如何自定义 validator 指令。
Built-in Validators
Angular 提供了一些内置的 validators,我们可以在 Template-Driven 或 Reactive 表单中使用它们。如果你对 Template-Driven 和 Reactive 表单还不了解的话,可以参考 Angular 4.x Forms 系列中 Template Driven Forms 和 Reactive Forms 这两篇文章。
在写本文时,Angular 支持的内建 validators 如下:
required - 设置表单控件值是非空的
email - 设置表单控件值的格式是 email
minlength - 设置表单控件值的最小长度
maxlength - 设置表单控件值的最大长度
pattern - 设置表单控件的值需匹配 pattern 对应的模式
在使用内建 validators 之前,我们需要根据使用的表单类型 (Template-Driven 或 Reactive),导入相应的模块,对于 Template-Driven
表单,我们需要导入 FormsModule
。具体示例如下:
import { NgModule } from ''@angular/core'';
import { BrowserModule } from ''@angular/platform-browser'';
import { FormsModule } from ''@angular/forms'';
@NgModule({
imports: [BrowserModule, FormsModule], // we add FormsModule here
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
一旦导入了 FormsModule
模块,我们就可以在应用中使用该模块提供的所有指令:
<form novalidate>
<input type="text" name="name" ngModel required>
<input type="text" name="street" ngModel minlength="3">
<input type="text" name="city" ngModel maxlength="10">
<input type="text" name="zip" ngModel pattern="[A-Za-z]{5}">
</form>
而对于 Reactive
表单,我们就需要导入 ReactiveFormsModule
模块:
import { ReactiveFormsModule } from ''@angular/forms'';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
...
})
export class AppModule {}
可以直接使用 FormControl
和 FormGroup
API 创建表单:
@Component()
class Cmp {
form: FormGroup;
ngOnInit() {
this.form = new FormGroup({
name: new FormControl('''', Validators.required)),
street: new FormControl('''', Validators.minLength(3)),
city: new FormControl('''', Validators.maxLength(10)),
zip: new FormControl('''', Validators.pattern(''[A-Za-z]{5}''))
});
}
}
也可以利用 FormBuilder
提供的 API,采用更便捷的方式创建表单:
@Component()
class Cmp {
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.form = this.fb.group({
name: ['''', Validators.required],
street: ['''', Validators.minLength(3)],
city: ['''', Validators.maxLength(10)],
zip: ['''', Validators.pattern(''[A-Za-z]{5}'')]
});
}
}
需要注意的是,我们还需要使用 [formGroup]
指令将表单模型与 DOM 中的表单对象关联起来,具体如下:
<form novalidate [formGroup]="form">
...
</form>
接下来我们来介绍一下如何自定义 validator 指令。
Building a custom validator directive
在实际开发前,我们先来介绍一下具体需求:我们有一个新增用户的表单页面,里面包含 4 个输入框,分为用于保存用户输入的 username
、email
、password
、confirmPassword
信息。具体的 UI 效果图如下:
Setup (基础设置)
1.定义 user 接口
export interface User {
username: string; // 必填,5-8个字符
email: string; // 必填,有效的email格式
password: string; // 必填,值要与confirmPassword值一样
confirmPassword: string; // 必填,值要与password值一样
}
2.导入 ReactiveFormsModule
app.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from ''@angular/core'';
import { BrowserModule } from ''@angular/platform-browser'';
import { ReactiveFormsModule } from ''@angular/forms'';
import { AppComponent } from ''./app.component'';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
3.初始化 AppComponent
app.component.html
<div>
<h3>Add User</h3>
<form novalidate (ngSubmit)="saveUser()" [formGroup]="user">
<div>
<label for="">Username</label>
<input type="text" formControlName="username">
<div*ngIf="user.get(''username'').invalid &&
user.get(''username'').touched">
Username is required (minimum 5 characters, maximum 8 characters).
</div>
<!--<pre *ngIf="user.get(''username'').errors">
{{ user.get(''username'').errors | json }}</pre>-->
</div>
<div>
<label for="">Email</label>
<input type="email" formControlName="email">
<div*ngIf="user.get(''email'').invalid && user.get(''email'').touched">
Email is required and format should be <i>24065****@qq.com</i>.
</div>
<!--<pre *ngIf="user.get(''email'').errors">
{{ user.get(''email'').errors | json }}</pre>-->
</div>
<div>
<label for="">Password</label>
<input type="password" formControlName="password">
<div*ngIf="user.get(''password'').invalid &&
user.get(''password'').touched">
Password is required
</div>
<!--<pre *ngIf="user.get(''password'').errors">
{{ user.get(''password'').errors | json }}</pre>-->
</div>
<div>
<label for="">Retype password</label>
<input type="password" formControlName="confirmPassword" validateEqual="password">
<div*ngIf="user.get(''confirmPassword'').invalid &&
user.get(''confirmPassword'').touched">
Password mismatch
</div>
<!--<pre *ngIf="user.get(''confirmPassword'').errors">
{{ user.get(''confirmPassword'').errors | json }}</pre>-->
</div>
<button type="submit"[disabled]="user.invalid">Submit</button>
</form>
</div>
app.component.ts
import { Component, OnInit } from ''@angular/core'';
import { FormGroup, FormBuilder, Validators } from ''@angular/forms'';
export interface User {
username: string; // 必填,5-8个字符
email: string; // 必填,有效的email格式
password: string; // 必填,值要与confirmPassword值一样
confirmPassword: string; // 必填,值要与password值一样
}
@Component({
moduleId: module.id,
selector: ''exe-app'',
templateUrl: ''app.component.html'',
styles: [`
.error {
border: 1px dashed red;
color: red;
padding: 4px;
}
.btn-default {
border: 1px solid;
background-color: #3845e2;
color: #fff;
}
.btn-default:disabled {
background-color: #aaa;
}
`]
})
export class AppComponent implements OnInit {
public user: FormGroup;
constructor(public fb: FormBuilder) { }
ngOnInit() {
this.user = this.fb.group({
username: ['''', [Validators.required, Validators.minLength(5),
Validators.maxLength(8)]],
email: ['''', [Validators.required, Validators.email]],
password: ['''', [Validators.required]],
confirmPassword: ['''', [Validators.required]]
});
}
saveUser(): void {
}
}
Custom confirm password validator
接下来我们来实现自定义 equal-validator
指令:
equal-validator.directive.ts
import { Directive, forwardRef, Attribute } from ''@angular/core'';
import { Validator, AbstractControl, NG_VALIDATORS } from ''@angular/forms'';
@Directive({
selector: ''[validateEqual][formControlName],[validateEqual][formControl],
[validateEqual][ngModel]'',
providers: [
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => EqualValidator),
multi: true }
]
})
export class EqualValidator implements Validator {
constructor(@Attribute(''validateEqual'') public validateEqual: string) { }
validate(c: AbstractControl): { [key: string]: any } {
// self value (e.g. retype password)
let v = c.value; // 获取应用该指令,控件上的值
// control value (e.g. password)
let e = c.root.get(this.validateEqual); // 获取进行值比对的控件
// value not equal
if (e && v !== e.value)
return {
validateEqual: false
}
return null;
}
}
上面的代码很长,我们来分解一下。
Directive declaration
@Directive({
selector: ''[validateEqual][formControlName],[validateEqual]
[formControl],[validateEqual][ngModel]'',
providers: [
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => EqualValidator),
multi: true }
]
})
首先,我们使用 @Directive
装饰器来定义指令。然后我们设置该指令的 Metadata 信息:
selector - 定义指令在 HTML 代码中匹配的方式
providers - 注册EqualValidator
其中 forwardRef 的作用,请参考 - Angular 2 Forward Reference
Class defintion
export class EqualValidator implements Validator {
constructor(@Attribute(''validateEqual'') public validateEqual: string) {}
validate(c: AbstractControl): { [key: string]: any } {}
}
我们的 EqualValidator
类必须实现 Validator
接口:
export interface Validator {
validate(c: AbstractControl): ValidationErrors|null;
registerOnValidatorChange?(fn: () => void): void;
}
该接口要求定义一个 validate()
方法,因此我们的 `EqualValidator
类中就需要实现 Validator
接口中定义的 validate
方法。此外在构造函数中,我们通过 @Attribute(''validateEqual'')
装饰器来获取 validateEqual 属性上设置的值。
Validate implementation
validate(c: AbstractControl): { [key: string]: any } {
// self value (e.g. retype password)
let v = c.value; // 获取应用该指令,控件上的值
// control value (e.g. password)
let e = c.root.get(this.validateEqual); // 获取进行值比对的控件
// value not equal
if (e && v !== e.value)
return { // 若不相等,返回验证失败信息
validateEqual: false
}
return null;
}
Use custom validator
要在我们的表单中使用自定义验证器,我们需要将其导入到我们的应用程序模块中。
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from ''@angular/core'';
import { BrowserModule } from ''@angular/platform-browser'';
import { ReactiveFormsModule } from ''@angular/forms'';
import { EqualValidator } from ''./equal-validator.directive'';
import { AppComponent } from ''./app.component'';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [AppComponent, EqualValidator],
bootstrap: [AppComponent]
})
export class AppModule { }
以上代码成功运行后,我们来验证一下刚实现的功能:
友情提示:演示需要先把密码框的类型设置为text
步骤一
步骤二
看起来一切很顺利,但请继续看下图:
什么情况,password 输入框的值已经变成 12345 了,还能验证通过。为什么会出现这个问题呢?因为我们的只在 confirmPassword 输入框中应用 validateEqual
指令。所以 password 输入框的值发生变化时,是不会触发验证的。接下来我们来看一下如何修复这个问题。
Solution
我们将重用我们的 validateEqual 验证器并添加一个 reverse
属性 。
<div>
<label for="">Password</label>
<input type="text" formControlName="password" validateEqual="confirmPassword"
reverse="true">
<div*ngIf="user.get(''password'').invalid &&
user.get(''password'').touched">
Password is required
</div>
<!--<pre *ngIf="user.get(''password'').errors">
{{ user.get(''password'').errors | json }}</pre>-->
</div>
<div>
<label for="">Retype password</label>
<input type="text" formControlName="confirmPassword" validateEqual="password">
<div*ngIf="user.get(''confirmPassword'').invalid &&
user.get(''confirmPassword'').touched">
Password mismatch
</div>
<!--<pre *ngIf="user.get(''confirmPassword'').errors">
{{ user.get(''confirmPassword'').errors | json }}</pre>-->
</div>
若未设置
reverse
属性或属性值为 false,实现的功能跟前面的一样。若
reverse
的值设置为 true,我们仍然会执行相同的验证,但错误信息不是添加到当前控件,而是添加到目标控件上。
在上面的示例中,我们设置 password 输入框的 reverse 属性为 true,即 reverse="true"
。当 password 输入框的值与 confirmPassword 输入框的值不相等时,我们将把错误信息添加到 confirmPassword 控件上。具体实现如下:
equal-validator.directive.ts
import { Directive, forwardRef, Attribute } from ''@angular/core'';
import { Validator, AbstractControl, NG_VALIDATORS } from ''@angular/forms'';
@Directive({
selector: ''[validateEqual][formControlName],[validateEqual][formControl],
[validateEqual][ngModel]'',
providers: [
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => EqualValidator),
multi: true }
]
})
export class EqualValidator implements Validator {
constructor(@Attribute(''validateEqual'') public validateEqual: string,
@Attribute(''reverse'') public reverse: string) { }
private get isReverse() {
if (!this.reverse) return false;
return this.reverse === ''true'';
}
validate(c: AbstractControl): { [key: string]: any } {
// self value
let v = c.value;
// control vlaue
let e = c.root.get(this.validateEqual);
// value not equal
// 未设置reverse的值或值为false
if (e && v !== e.value && !this.isReverse) {
return {
validateEqual: false
}
}
// value equal and reverse
// 若值相等且reverse的值为true,则删除validateEqual异常信息
if (e && v === e.value && this.isReverse) {
delete e.errors[''validateEqual''];
if (!Object.keys(e.errors).length) e.setErrors(null);
}
// value not equal and reverse
// 若值不相等且reverse的值为true,则把异常信息添加到比对的目标控件上
if (e && v !== e.value && this.isReverse) {
e.setErrors({ validateEqual: false });
}
return null;
}
}
以上代码运行后,成功解决了我们的问题。其实解决该问题还有其它的方案,我们可以基于 password
和 confirmPassword
来创建 FormGroup
对象,然后添加自定义验证来实现上述的功能。详细的信息,请参考 - Angular 4.x 基于AbstractControl自定义表单验证。
参考资源
scotch.io - How to Implement a Custom Validator Directive (Confirm Password) in Angular 2
Thoughtram.io - CUSTOM VALIDATORS IN ANGULAR
stack overflow - Angular 2 form validating for repeat password
angular – 创建自定义结构指令
说明:我有一对字符串数组的元素让我们调用数组标题的n个元素的第一个元素和第二个描述
这是https://ibb.co/i23Dxa的结果
(数组来自后端,我通过模拟数组文件模拟它)
现在,如果我调用我需要创建的结构指令,我希望它显示我将在该调用下编写的内容
例:
<ng-template aulos-configuration-item-toolbar> <button (click)="click()">just click</button> </ng-template>
这应该只渲染我在标签之间写的按钮,现在,我有这个:
import { Directive,Input,TemplateRef,ViewContainerRef } from "@angular/core"; @Directive({ selector: "[aulos-configuration-item-toolbar]",}) export class AulosConfigurationItemToolbar { public templateRef: TemplateRef<any>; constructor(templateRef: TemplateRef<any>) { this.templateRef = templateRef; } }
有人可以更好地解释我的进展方式吗?非常感谢.
解决方法
组件看起来像这样……
export class GridOptionsComponent { } @Directive({ selector: 'grid-options' }) @Component({ selector: 'my-grid',moduleId: module.id,templateUrl: './grid.component.html' }) export class GridComponent { .... .... }
grid.component.html看起来像
<div> <div> some more html elements,components </div> <ng-content select="grid-options"></ng-content> </div>
并且应该在另一个组件中使用
<my-grid> <grid-options> <button >Add</button> <button >View</button> </grid-options> </my-grid>
angular 自定义指令
一、 id选择器
1、 文件 app.hightlight.directive.component.ts :
import { Directive, ElementRef, Input } from ''@angular/core''; @Directive({ selector: ''#appHightLight'', }) export class AppHightLightDirective { constructor(private el: ElementRef) { el.nativeElement.style.background = ''red''; } }
效果:
二: 类选择器:
import { Directive, ElementRef, Input } from ''@angular/core''; @Directive({ selector: ''.appHightLight'', }) export class AppHightLightDirective { constructor(private el: ElementRef) { el.nativeElement.style.background = ''red''; } }
html 文件中:
<div> 自定义样式 </div>
三: 属性选择器
import { Directive, ElementRef, Input } from ''@angular/core''; @Directive({ selector: ''[appHightLight]'', }) export class AppHightLightDirective { constructor(private el: ElementRef) { el.nativeElement.style.background = ''red''; } }
html文件:
<div appHightLight> 自定义样式 </div>
三:根据传入的值改变样式:
文件 app.hightlight.directive.component.ts :
import { Directive, ElementRef, HostListener, Input, OnInit } from ''@angular/core''; @Directive({ selector: ''[hightlight]'', }) export class AppHightLightDirective { @input() hightlight: any; constructor(private el: ElementRef) { alert(this.hightlight) console.log("constructor:" + this.hightlight) if (this.hightlight == null) { el.nativeElement.style.background = ''red''; } else { el.nativeElement.style.background = "pink"; } } ngOnInit(): void { //Called after the constructor, initializing input properties, and the first call to ngOnChanges. //Add ''implements OnInit'' to the class. alert("init:" + this.hightlight) console.log("init:" + this.hightlight) if (this.hightlight == null) { this.el.nativeElement.style.background = ''green''; } else { this.el.nativeElement.style.background = this.hightlight; } } }
html传入的值:
<div [hightlight]=''"pink"''> 自定义样式 </div>
总结
以上是小编为你收集整理的angular 自定义指令全部内容。
如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。
原文地址:https://www.cnblogs.com/z360519549/p/16122960.html
关于Angular 自定义拖拽指令和angular 拖拽组件的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于angular -- 自定义指令和模板、Angular 4.x 自定义验证指令、angular – 创建自定义结构指令、angular 自定义指令等相关内容,可以在本站寻找。
本文标签: