動(dòng)手寫(xiě)一個(gè)angular版本的Message組件

學(xué)習(xí)一個(gè)框架或庫(kù)的最好方法是看官方文檔,并著手去寫(xiě)例子。最近在利用空閑的時(shí)間學(xué)習(xí)angular,那今天就嘗試寫(xiě)一個(gè)message組件,并通過(guò)message服務(wù)動(dòng)態(tài)加載message組件。
我所參與的項(xiàng)目基本是用jquery完成的。之前,在項(xiàng)目中自己動(dòng)手寫(xiě)過(guò)一個(gè)簡(jiǎn)單的message插件,樣子如下圖。


success.jpg

那現(xiàn)在就使用angular(版本5.0.0)來(lái)實(shí)現(xiàn)message組件。

message組件

message組件要根據(jù)傳入的類型、消息和duration來(lái)顯示。創(chuàng)建三個(gè)文件:message.component.ts,message.component.html,message.component.css,代碼如下。

//message.component.ts
import {Component,Input,OnInit,ChangeDetectionStrategy} from '@angular/core';
import {
   trigger,
   state,
   style,
   transition,
   animate
 } from '@angular/animations';
const mapping={
   success:'glyphicon-ok-sign',
   warning:'glyphicon-exclamation-sign',
   error:'glyphicon-exclamation-sign',
   info:'glyphicon-ok-circle'
}
@Component({
   selector:'upc-ng-message',
   templateUrl:'./message.component.html',
   styleUrls:['./message.component.css'],
   changeDetection:ChangeDetectionStrategy.OnPush
})
export class MessageComponent implements OnInit{
   ngOnInit(): void {
       this.typeClass=['upc-message-' + this.msgType];
       this.typeIconClass=[mapping[this.msgType]];
   }
   @Input() msgType:'success' | 'info' | 'warning' | 'error'='info'

   @Input() payload:string = ''

   private typeClass
   private typeIconClass
}
<!--*message.component.html-->
<div class="upc-message">
        <div class="upc-message-content" [ngClass]="typeClass">
            <i class="glyphicon" [ngClass]="typeIconClass"></i>
            {{payload}}
        </div>
</div>
.upc-message {
    position: fixed;
    z-index: 1999;
    width: 100%;
    top: 36px;
    left: 0;
    pointer-events: none;
    padding: 8px;
    text-align: center;
  }
  .upc-message i {
      margin-right: 8px;
      font-size: 14px;
      top: 1px;
      position: relative;
  }
  .upc-message-success i {
      color: green;
  }
  .upc-message-warning i {
      color: yellow;
  }
  .upc-message-error i {
      color: red;
  }
  .upc-message-content {
      padding: 8px 16px;
      -ms-border-radius: 4px;
      border-radius: 4px;
      -webkit-box-shadow: 0 2px 8px #000000;
      -ms-box-shadow: 0 2px 8px #000000;
      box-shadow: 0 2px 8px #000000;
      box-shadow: 0 2px 8px rgba(0,0,0,.2);
      background: #fff;
      display: inline-block;
      pointer-events: all;
  }

ComponentLoader

通過(guò)官方文檔動(dòng)態(tài)組件一節(jié),可以了解動(dòng)態(tài)創(chuàng)建組件需要通過(guò)ComponentFactoryResolver來(lái)完成。使用ComponentFactoryResolver創(chuàng)建 ComponentFactory,再通過(guò)ComponentFactory的create方法創(chuàng)建組件??垂俜轿臋n中API的說(shuō)明,ComponentFactory的create方法至少需要一個(gè)injector參數(shù),而injector的創(chuàng)建在文檔中也有提到,其中參數(shù)providers為需要注入的類。再梳理下整個(gè)過(guò)程:

  1. 提供providers
  2. 創(chuàng)建Injector實(shí)例
  3. 創(chuàng)建ComponetFactory
  4. 使用ComponetFactory創(chuàng)建ComponentRef
//ComponentFactory的create方法
create(injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any, ngModule?: NgModuleRef<any>): ComponentRef<C>

//使用Injector的create創(chuàng)建injector實(shí)例
static create(providers: StaticProvider[], parent?: Injector): Injector

為了代碼的復(fù)用,這里創(chuàng)建通用的loader類來(lái)完成組件的動(dòng)態(tài)創(chuàng)建。其中,attch方法用于初始化ComponetFactory(參數(shù)為組件類型);to方法用于標(biāo)識(shí)組件的父容器;provider方法用于初始化可注入的類;create方法用于創(chuàng)建組件并手動(dòng)變更檢測(cè);remove方法用于移除組件。

import {
    ComponentFactoryResolver,
    ComponentFactory,
    ComponentRef,
    Type,
    Injector,
    Provider,
    ElementRef
} from '@angular/core';
export class ComponentLoader<T>{
    constructor(private _cfr: ComponentFactoryResolver,
        private _injector: Injector) {
    }
    private _componentFactory: ComponentFactory<T>
    attch(componentType: Type<T>): ComponentLoader<T> {
        this._componentFactory = this._cfr.resolveComponentFactory<T>(componentType);
        return this;
    }
    private _parent: Element
    to(parent: string | ElementRef): ComponentLoader<T> {
        if (parent instanceof ElementRef) {
            this._parent = parent.nativeElement;
        } else {
            this._parent = document.querySelector(parent);
        }

        return this;
    }
    private _providers: Provider[] = [];
    provider(provider: Provider) {
        this._providers.push(provider);
    }
    create(opts: {}): ComponentRef<T> {
        const injector = Injector.create(this._providers as any[], this._injector);
        const componentRef = this._componentFactory.create(injector);
        Object.assign(componentRef.instance, opts);
        if (this._parent) {
            this._parent.appendChild(componentRef.location.nativeElement);
        }
        componentRef.changeDetectorRef.markForCheck();
        componentRef.changeDetectorRef.detectChanges();
        return componentRef;
    }
    remove(ref:ComponentRef<T>){
        if(this._parent){
            this._parent.removeChild(ref.location.nativeElement)
        }
        ref=null;
    }
}

同時(shí),為了便于loader的創(chuàng)建,再創(chuàng)建LoaderFactory類,代碼如下:

import {
    ComponentFactoryResolver,
    Injector,
    Injectable,
    ElementRef
} from '@angular/core';
import { ComponentLoader } from './component-loader.class';

@Injectable()
export class ComponentLoaderFactory {
    constructor(private _injector: Injector,
        private _cfr: ComponentFactoryResolver) {

    }

    create<T>(): ComponentLoader<T> {
        return new ComponentLoader(this._cfr, this._injector);
    }
}

message service

message service提供顯示message的API,代碼如下:

import {Injectable,Injector} from '@angular/core';
import { ComponentLoaderFactory } from '../component-loader/component-loader.factory';
import {MessageComponent} from './message.component';
import {ComponentLoader} from '../component-loader/component-loader.class';

@Injectable()
export class MessageService{
    constructor(private _clf:ComponentLoaderFactory,private _injector:Injector){
        this.loader=this._clf.create<MessageComponent>();
    }
    private loader:ComponentLoader<MessageComponent>
    private createMessage(t,c,duration=2000){
        this.loader.attch(MessageComponent).to('body');
        const opts = {
            msgType: t,
            payload:c
        };
        const ref = this.loader.create(opts);
        ref.changeDetectorRef.markForCheck();
        ref.changeDetectorRef.detectChanges();
        let self=this;
        let st = setTimeout(() => {
            self.loader.remove(ref);
            st = null;
        }, duration);
    }
    public info(payload,duration?) {
        this.createMessage('info',payload,duration);
    }
    public success(payload,duration?) {
        this.createMessage('success',payload,duration);
    }
    public error(payload,duration?) {
        this.createMessage('error',payload,duration);
    }
    public warning(payload,duration?) {
        this.createMessage('warning',payload,duration);
    }
}

message.module

最后,增加message.module.ts。記得要把動(dòng)態(tài)創(chuàng)建的組件添加到entryComponents數(shù)組中。

import {NgModule} from '@angular/core';
import { CommonModule } from '@angular/common';
import {MessageComponent} from './message.component';
import {MessageService} from './message.service';
import {ComponentLoaderFactory} from '../component-loader/component-loader.factory';

@NgModule({
   imports:[CommonModule],
   declarations:[MessageComponent],
   providers:[MessageService,ComponentLoaderFactory],
   entryComponents:[MessageComponent],
   exports:[MessageComponent]
})
export class MessageModule{
}

使用方法

注入MessageService,調(diào)用API使用Message組件。

this._msgService.success('成功了!');
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容