<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      13_自定義指令實踐

      自定義指令實踐:擴展HTML能力

      指令是Angular中與組件并列的核心特性,用于擴展HTML元素的行為和外觀。與組件不同,指令沒有模板,而是通過裝飾器和類方法實現(xiàn)對DOM的操作。本章將從基礎到高級,系統(tǒng)講解自定義指令的開發(fā)實踐,包括屬性指令、結構指令的實現(xiàn)技巧,以及指令與組件、服務的協(xié)同工作方式。

      1. 指令基礎:類型與核心概念

      Angular指令分為三類:組件指令(帶模板的特殊指令)、屬性指令(修改元素屬性或樣式)和結構指令(修改DOM結構)。本章聚焦于自定義屬性指令和結構指令的開發(fā)。

      1.1 指令元數(shù)據(jù)與選擇器

      自定義指令通過@Directive裝飾器定義,核心元數(shù)據(jù)包括selector(選擇器)和standalone(獨立模式):

      import { Directive } from '@angular/core';
      
      // 基本指令結構
      @Directive({
        selector: '[appHighlight]', // 屬性選擇器:以[]包裹
        standalone: true // 獨立指令(無需模塊)
      })
      export class HighlightDirective {
        // 指令邏輯
      }
      

      選擇器規(guī)則

      • 屬性選擇器:[屬性名](如[appHighlight]),匹配帶該屬性的元素
      • 元素選擇器:元素名(如app-my-directive),匹配特定元素
      • 類選擇器:.類名(如.special),匹配帶該類的元素
      • 組合選擇器:input[appNumber](匹配帶appNumber屬性的input元素)

      1.2 指令與組件的區(qū)別

      特性 組件 指令
      模板 必須有(templatetemplateUrl 無模板
      用途 構建UI視圖,擁有自己的DOM結構 擴展現(xiàn)有元素的行為或樣式
      選擇器 通常為元素選擇器(如app-component 通常為屬性選擇器(如[appDirective]
      生命周期 完整的組件生命周期 ngAfterContentInit等內容投影相關鉤子外的大部分生命周期

      2. 屬性指令:增強元素行為

      屬性指令用于修改DOM元素的外觀或行為(如樣式、事件監(jiān)聽、屬性值等),是最常用的自定義指令類型。

      2.1 基礎樣式修改指令

      實現(xiàn)一個高亮指令,根據(jù)條件修改元素背景色:

      // highlight.directive.ts
      import { Directive, ElementRef, Input, OnInit } from '@angular/core';
      
      @Directive({
        selector: '[appHighlight]',
        standalone: true
      })
      export class HighlightDirective implements OnInit {
        // 輸入屬性:高亮顏色(默認黃色)
        @Input() highlightColor = 'yellow';
        
        // 輸入屬性:是否激活(默認true)
        @Input() appHighlight = true; // 與選擇器同名,支持<元素 [appHighlight]="false">語法
        
        // ElementRef:獲取宿主元素的DOM引用
        constructor(private el: ElementRef) {}
        
        ngOnInit() {
          // 初始化時設置樣式
          this.updateHighlight();
        }
        
        // 更新高亮狀態(tài)
        private updateHighlight() {
          if (this.appHighlight) {
            // 通過nativeElement訪問DOM元素
            this.el.nativeElement.style.backgroundColor = this.highlightColor;
          } else {
            this.el.nativeElement.style.backgroundColor = '';
          }
        }
        
        // 監(jiān)聽輸入屬性變化,動態(tài)更新樣式
        ngOnChanges() {
          this.updateHighlight();
        }
      }
      

      使用指令:

      // 使用指令的組件
      import { Component } from '@angular/core';
      import { HighlightDirective } from './highlight.directive';
      
      @Component({
        selector: 'app-example',
        standalone: true,
        imports: [HighlightDirective], // 導入指令
        template: `
          <!-- 基礎用法 -->
          <p appHighlight>默認高亮(黃色)</p>
          
          <!-- 自定義顏色 -->
          <p appHighlight [highlightColor]="'lightblue'">淺藍色高亮</p>
          
          <!-- 動態(tài)控制是否激活 -->
          <p [appHighlight]="isActive" [highlightColor]="'pink'">
            可切換的高亮(點擊按鈕切換)
          </p>
          <button (click)="isActive = !isActive">切換高亮</button>
        `
      })
      export class ExampleComponent {
        isActive = true;
      }
      

      2.2 事件處理指令

      實現(xiàn)一個防抖指令,限制高頻事件(如輸入、滾動)的觸發(fā)頻率:

      // debounce.directive.ts
      import { Directive, HostListener, Input, Output, EventEmitter } from '@angular/core';
      import { Subject, debounceTime, takeUntil, skip } from 'rxjs';
      import { DestroyRef, inject } from '@angular/core';
      
      @Directive({
        selector: '[appDebounce]',
        standalone: true
      })
      export class DebounceDirective {
        // 防抖時間(默認300ms)
        @Input() debounceTime = 300;
        
        // 輸出防抖后的事件
        @Output() debounced = new EventEmitter<any>();
        
        // 用于防抖的Subject
        private eventSubject = new Subject<any>();
        
        // 自動銷毀訂閱(Angular 16+)
        private destroyRef = inject(DestroyRef);
        
        constructor() {
          // 防抖處理
          const subscription = this.eventSubject
            .pipe(
              debounceTime(this.debounceTime),
              skip(1) // 跳過初始值
            )
            .subscribe(value => {
              this.debounced.emit(value);
            });
            
          // 組件銷毀時自動取消訂閱
          this.destroyRef.onDestroy(() => {
            subscription.unsubscribe();
          });
        }
        
        // 監(jiān)聽宿主元素的input事件
        @HostListener('input', ['$event.target.value'])
        onInput(value: string) {
          this.eventSubject.next(value);
        }
        
        // 支持其他事件(如scroll)
        @HostListener('scroll', ['$event'])
        onScroll(event: Event) {
          this.eventSubject.next(event);
        }
      }
      

      使用防抖指令:

      <!-- 輸入框防抖 -->
      <input 
        type="text" 
        appDebounce 
        [debounceTime]="500"
        (debounced)="handleSearch($event)"
        placeholder="搜索(500ms防抖)"
      >
      
      <!-- 滾動防抖 -->
      <div 
        appDebounce 
        [debounceTime]="100"
        (debounced)="handleScroll($event)"
        style="height: 200px; overflow: auto; border: 1px solid #ccc;"
      >
        <!-- 長內容 -->
        <p *ngFor="let i of [1,2,3,4,5,6,7,8,9,10]">滾動內容 {{i}}</p>
      </div>
      

      2.3 帶依賴注入的指令

      指令可以注入服務,實現(xiàn)更復雜的功能(如權限控制、日志記錄):

      // permission.directive.ts(權限控制指令)
      import { Directive, Input, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';
      import { AuthService } from './auth.service';
      
      @Directive({
        selector: '[appPermission]',
        standalone: true
      })
      export class PermissionDirective {
        // 需要的權限
        @Input() appPermission!: string;
        
        constructor(
          private authService: AuthService, // 注入權限服務
          private templateRef: TemplateRef<any>, // 指令所在的模板
          private viewContainer: ViewContainerRef // 視圖容器
        ) {}
        
        ngOnInit() {
          // 檢查是否有權限
          if (this.authService.hasPermission(this.appPermission)) {
            // 有權限:渲染模板
            this.viewContainer.createEmbeddedView(this.templateRef);
          } else {
            // 無權限:清空視圖
            this.viewContainer.clear();
          }
        }
      }
      

      權限服務:

      // auth.service.ts
      import { Injectable } from '@angular/core';
      
      @Injectable({ providedIn: 'root' })
      export class AuthService {
        // 模擬權限檢查
        hasPermission(permission: string): boolean {
          const userPermissions = ['read', 'edit']; // 當前用戶擁有的權限
          return userPermissions.includes(permission);
        }
      }
      

      使用權限指令:

      <!-- 有權限才顯示的內容 -->
      <button *appPermission="'edit'">編輯(需要edit權限)</button>
      
      <!-- 無權限則不顯示 -->
      <button *appPermission="'delete'">刪除(需要delete權限)</button>
      

      3. 結構指令:操縱DOM結構

      結構指令通過*語法糖修改DOM結構(如條件渲染、循環(huán)渲染),核心是通過TemplateRefViewContainerRef操作模板。

      3.1 基礎條件渲染指令

      實現(xiàn)一個*appIf指令,類似*ngIf但支持自定義加載狀態(tài):

      // if.directive.ts
      import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
      
      @Directive({
        selector: '[appIf]',
        standalone: true
      })
      export class IfDirective {
        // 存儲當前條件
        private currentCondition: boolean = false;
        
        constructor(
          private templateRef: TemplateRef<any>,
          private viewContainer: ViewContainerRef
        ) {}
        
        // 輸入屬性:支持<元素 *appIf="condition; else elseTemplate">語法
        @Input() set appIf(condition: boolean) {
          this.currentCondition = condition;
          this.updateView();
        }
        
        // 輸入屬性:else模板
        @Input() appIfElse?: TemplateRef<any>;
        
        // 更新視圖
        private updateView() {
          // 清空現(xiàn)有視圖
          this.viewContainer.clear();
          
          if (this.currentCondition) {
            // 條件為true:渲染主模板
            this.viewContainer.createEmbeddedView(this.templateRef);
          } else if (this.appIfElse) {
            // 條件為false且有else模板:渲染else模板
            this.viewContainer.createEmbeddedView(this.appIfElse);
          }
        }
      }
      

      使用*appIf指令:

      <!-- 基礎用法 -->
      <p *appIf="showContent">條件為true時顯示</p>
      
      <!-- 帶else模板 -->
      <div *appIf="hasData; else loading">
        數(shù)據(jù)加載完成:{{ data }}
      </div>
      
      <ng-template #loading>
        <p>加載中...</p>
      </ng-template>
      
      <button (click)="showContent = !showContent">切換顯示</button>
      

      3.2 循環(huán)渲染指令

      實現(xiàn)一個*appFor指令,類似*ngFor但支持索引和空狀態(tài):

      // for.directive.ts
      import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
      
      @Directive({
        selector: '[appFor]',
        standalone: true
      })
      export class ForDirective {
        // 存儲當前數(shù)據(jù)
        private items: any[] = [];
        
        constructor(
          private templateRef: TemplateRef<any>,
          private viewContainer: ViewContainerRef
        ) {}
        
        // 解析輸入:*appFor="let item of items; let i = index; empty emptyTemplate"
        @Input() set appForOf(items: any[]) {
          this.items = items || [];
          this.updateView();
        }
        
        // 空狀態(tài)模板
        @Input() appForEmpty?: TemplateRef<any>;
        
        // 更新視圖
        private updateView() {
          this.viewContainer.clear();
          
          if (this.items.length === 0 && this.appForEmpty) {
            // 數(shù)據(jù)為空且有empty模板:渲染空狀態(tài)
            this.viewContainer.createEmbeddedView(this.appForEmpty);
          } else {
            // 渲染列表項
            this.items.forEach((item, index) => {
              this.viewContainer.createEmbeddedView(this.templateRef, {
                // 暴露變量給模板:$implicit(默認變量)、index(索引)
                $implicit: item,
                index: index
              });
            });
          }
        }
      }
      

      使用*appFor指令:

      <!-- 基礎用法 -->
      <ul>
        <li *appFor="let user of users; let i = index">
          {{ i + 1 }}. {{ user.name }}
        </li>
      </ul>
      
      <!-- 帶空狀態(tài) -->
      <div *appFor="let item of products; empty noProducts">
        <p>{{ item.name }}</p>
      </div>
      
      <ng-template #noProducts>
        <p>暫無產品數(shù)據(jù)</p>
      </ng-template>
      

      4. 高級指令技巧

      4.1 指令與信號(Signals)結合

      Angular 16+引入的信號(Signals)可與指令結合,實現(xiàn)響應式狀態(tài)管理:

      // tooltip.directive.ts(信號版 tooltip 指令)
      import { Directive, Input, ElementRef, HostListener, signal } from '@angular/core';
      
      @Directive({
        selector: '[appTooltip]',
        standalone: true
      })
      export class TooltipDirective {
        // 信號:控制tooltip顯示狀態(tài)
        private isVisible = signal(false);
        
        // 輸入:tooltip文本
        @Input() appTooltip!: string;
        
        // 輸入:位置(上/下/左/右)
        @Input() tooltipPosition: 'top' | 'bottom' | 'left' | 'right' = 'top';
        
        // tooltip元素
        private tooltipElement: HTMLElement;
        
        constructor(private el: ElementRef) {
          // 創(chuàng)建tooltip DOM元素
          this.tooltipElement = document.createElement('div');
          this.tooltipElement.className = 'app-tooltip';
          this.tooltipElement.style.position = 'absolute';
          this.tooltipElement.style.display = 'none';
          document.body.appendChild(this.tooltipElement);
          
          // 監(jiān)聽信號變化,更新顯示狀態(tài)
          this.isVisible.subscribe(visible => {
            this.tooltipElement.style.display = visible ? 'block' : 'none';
            if (visible) {
              this.positionTooltip();
            }
          });
        }
        
        // 定位tooltip
        private positionTooltip() {
          const hostRect = this.el.nativeElement.getBoundingClientRect();
          const tooltipRect = this.tooltipElement.getBoundingClientRect();
          
          switch (this.tooltipPosition) {
            case 'top':
              this.tooltipElement.style.top = `${hostRect.top - tooltipRect.height - 5}px`;
              this.tooltipElement.style.left = `${hostRect.left + hostRect.width / 2 - tooltipRect.width / 2}px`;
              break;
            // 其他位置邏輯...
          }
        }
        
        // 鼠標進入:顯示tooltip
        @HostListener('mouseenter') onMouseEnter() {
          this.tooltipElement.textContent = this.appTooltip;
          this.isVisible.set(true);
        }
        
        // 鼠標離開:隱藏tooltip
        @HostListener('mouseleave') onMouseLeave() {
          this.isVisible.set(false);
        }
        
        // 清理:移除tooltip元素
        ngOnDestroy() {
          document.body.removeChild(this.tooltipElement);
        }
      }
      

      4.2 指令組合與優(yōu)先級

      多個指令可應用于同一元素,通過priority控制執(zhí)行順序(數(shù)值越大優(yōu)先級越高):

      // 高優(yōu)先級指令
      @Directive({
        selector: '[appHighPriority]',
        standalone: true,
        priority: 100 // 優(yōu)先級高
      })
      export class HighPriorityDirective {
        constructor() {
          console.log('高優(yōu)先級指令初始化');
        }
      }
      
      // 低優(yōu)先級指令
      @Directive({
        selector: '[appLowPriority]',
        standalone: true,
        priority: 10 // 優(yōu)先級低
      })
      export class LowPriorityDirective {
        constructor() {
          console.log('低優(yōu)先級指令初始化');
        }
      }
      

      應用多個指令:

      <!-- 輸出順序:高優(yōu)先級 → 低優(yōu)先級 -->
      <div appHighPriority appLowPriority></div>
      

      4.3 宿主綁定與類/樣式操作

      通過@HostBinding直接綁定宿主元素的屬性、類或樣式:

      // active.directive.ts
      import { Directive, HostBinding, HostListener } from '@angular/core';
      
      @Directive({
        selector: '[appActive]',
        standalone: true
      })
      export class ActiveDirective {
        // 綁定宿主元素的class.active
        @HostBinding('class.active') isActive = false;
        
        // 綁定宿主元素的style.cursor
        @HostBinding('style.cursor') cursor = 'pointer';
        
        // 鼠標點擊切換激活狀態(tài)
        @HostListener('click') onClick() {
          this.isActive = !this.isActive;
        }
        
        // 鼠標懸停效果
        @HostListener('mouseenter') onMouseEnter() {
          this.cursor = 'pointer';
        }
        
        @HostListener('mouseleave') onMouseLeave() {
          this.cursor = 'default';
        }
      }
      

      使用效果:

      <!-- 點擊切換active類,鼠標懸停改變光標 -->
      <div appActive>點擊激活</div>
      

      5. 指令最佳實踐

      5.1 命名規(guī)范

      • 指令選擇器前綴:使用項目特定前綴(如app)避免沖突
      • 功能命名:選擇器名稱應明確表示指令功能(如appHighlightappDebounce
      • 輸入屬性:與指令同名的輸入屬性用于主開關(如[appPermission]="'edit'"

      5.2 性能優(yōu)化

      • 避免頻繁DOM操作:通過防抖、節(jié)流減少高頻事件處理
      • 清理資源:在ngOnDestroy中移除事件監(jiān)聽、定時器等
      • 使用DestroyRef:Angular 16+推薦使用DestroyRef管理訂閱生命周期

      5.3 適用場景

      • 通用UI增強:如高亮、tooltip、拖拽等
      • 行為封裝:如防抖、節(jié)流、權限控制等
      • 跨組件復用邏輯:不適宜用組件實現(xiàn)的共享行為

      5.4 避免過度使用

      • 復雜UI邏輯優(yōu)先使用組件
      • 單一職責:一個指令只做一件事
      • 避免嵌套結構指令:多個結構指令應用于同一元素可能導致不可預期的行為

      總結

      自定義指令是Angular擴展HTML能力的強大工具,通過屬性指令可以增強元素行為和樣式,通過結構指令可以操縱DOM結構。本章從基礎概念出發(fā),通過實例講解了指令的開發(fā)流程,包括選擇器定義、宿主元素交互、依賴注入集成等核心技術。

      高級部分介紹了指令與信號結合、指令組合、宿主綁定等技巧,幫助開發(fā)者構建更靈活、高效的指令。遵循最佳實踐,合理使用指令可以顯著提升代碼復用性和應用質量,尤其在開發(fā)通用組件庫或處理跨組件共享行為時發(fā)揮重要作用。

      在實際開發(fā)中,應根據(jù)需求選擇合適的指令類型,平衡靈活性與性能,避免過度設計,讓指令真正成為組件的有力補充。

      posted @ 2025-09-21 18:19  S&L·chuck  閱讀(10)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲成人动漫在线| 国产老女人免费观看黄A∨片| 亚洲成熟女人av在线观看| 日韩精品人妻中文字幕| 亚洲色av天天天天天天| 蜜桃av亚洲精品一区二区| 国产精品亚洲中文字幕| 老司机精品成人无码AV| 2020国产成人精品视频| 亚洲第一精品一二三区| 成人免费无遮挡无码黄漫视频| 国产av一区二区三区精品| 亚洲精品无码久久一线| 久久人妻av无码中文专区| 欧美精品国产综合久久| 国产精品理论片在线观看| 国产农村激情免费专区| 国产蜜臀精品一区二区三区| 国产午夜福利片在线观看| 色婷婷婷丁香亚洲综合| 国产AV福利第一精品| 国产香蕉久久精品综合网| 久久精品亚洲中文无东京热| 色悠久久网国产精品99| 国产午夜福利视频在线| 这里只有精品在线播放| 亚洲成在人线av无码| 深夜av免费在线观看| 黑人猛精品一区二区三区| 亚洲午夜亚洲精品国产成人| 成年女性特黄午夜视频免费看| 伊人久久大香线蕉成人| 人妻少妇偷人无码视频| 亚洲精品国产精品乱码不| 日本妇人成熟免费| 国产精品高清国产三级囯产AV| 精品久久人人做爽综合| 亚洲第一福利网站在线观看| 色吊a中文字幕一二三区| 新版天堂资源中文8在线| 18禁无遮拦无码国产在线播放|