GVKun编程网logo

Angular 自定义拖拽指令(angular 拖拽组件)

12

最近很多小伙伴都在问Angular自定义拖拽指令和angular拖拽组件这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展angular--自定义指令和模板、Angular4.x自定

最近很多小伙伴都在问Angular 自定义拖拽指令angular 拖拽组件这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展angular -- 自定义指令和模板、Angular 4.x 自定义验证指令、angular – 创建自定义结构指令、angular 自定义指令等相关知识,下面开始了哦!

本文目录一览:

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 -- 自定义指令和模板

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 自定义验证指令

Angular 4.x 自定义验证指令

表单是几乎每个 Web 应用程序的一部分。虽然 Angular 为我们提供了几个内置 validators (验证器),但在实际工作中为了满足项目需求,我们经常需要为应用添加一些自定义验证功能。接下来我们将着重介绍,如何自定义 validator 指令。

Built-in Validators

Angular 提供了一些内置的 validators,我们可以在 Template-DrivenReactive 表单中使用它们。如果你对 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 {}

可以直接使用 FormControlFormGroup 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 个输入框,分为用于保存用户输入的 usernameemailpasswordconfirmPassword 信息。具体的 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;
    }
}

以上代码运行后,成功解决了我们的问题。其实解决该问题还有其它的方案,我们可以基于 passwordconfirmPassword 来创建 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 – 创建自定义结构指令

angular – 创建自定义结构指令

我一周前开始学习Angular2和最新的angular4,现在我需要创建一个自定义结构指令来显示其中的内容.

说明:我有一对字符串数组的元素让我们调用数组标题的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 自定义指令

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 自定义指令等相关内容,可以在本站寻找。

本文标签: