2016年12月30日 星期五

[ASP.NET Core X Angular] (11) - BlockUI by ComponentFactoryResolver

 Angular     ComponentFactoryResolver    BlockUI   Font-awesome 


Introduction


Since Angular2 doesn’t have
$compile equivalent, we could use ComponentFactoryResolver class to create the component factory and dynamically create a component.

We will make a sample, blockUI, which is a component that could be created in another component and destroyed at run-time.

Related articles


Environment

Angular 2.1.0


Implement


HTML and CSS

First, we should have the blockUI’s HTML and CSS.


blockUI.component.ts

import {Component, Input} from '@angular/core';

@Component({
    selector: 'block-ui',
    template:
    `<div class="in modal-backdrop blockui-overlay"></div>
    <div class="blockui-message-container">
    <div class="blockui-message" [ngClass]="blockuiMessageClass">
      {{state.message}} <i class="fa fa-cog fa-spin"></i>
    </div>
    </div>`,
    styleUrls: ['/app/component/blockUI/blockUI.component.css']
})


export class BlockUIComponent {
    @Input() private blockuiMessageClass;
    private state: any;

    constructor() {
        this.state = {message: 'Loading...'};
    }
}

blockUI.component.css

.blockui-overlay {
  background-color: white;
  cursor: wait;
}

.blockui-message-container {
  position: absolute;
  top: 35%;
  left: 0;
  right: 0;
  height: 0;
  text-align: center;
  z-index: 10001;
  cursor: wait;
}

.blockui-message {
  display: inline-block;
  text-align: left;
  background-color: #333;
  color: #f5f5f5;
  padding: 20px;
  border-radius: 4px;
  font-size: 20px;
  font-weight: bold;
  filter: alpha(opacity=100);
}

.modal-backdrop.in {
    filter: alpha(opacity=50);
    opacity: .5;
}

.modal-backdrop {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 1040;
    background-color: #000;
}



ComponentFactoryResolver

Let’s create a service for blockUI.

Notice that after Angular 2.2, there are some updates on ApplicationRef, and we could not use appRef['_rootComponents'][0]['_hostElement'].vcRef to get root component’s view anymore.


blockUI.service.ts (with Angular 2.1.0 or earlier version)

import {Injectable, ApplicationRef, ComponentRef, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import {BlockUIComponent} from './blockUI.component';

@Injectable()
export class BlockUIService {
    private blockUI: ComponentRef<BlockUIComponent>;

    constructor(
        private appRef: ApplicationRef,
        private componentFactoryResolver: ComponentFactoryResolver) { }

    public start() {

        let viewContainerRef: ViewContainerRef = this.appRef['_rootComponents'][0]['_hostElement'].vcRef;
        let factory = this.componentFactoryResolver.resolveComponentFactory(BlockUIComponent);
        this.blockUI = viewContainerRef.createComponent(factory);
    }


    public stop() {
        if (this.blockUI) {
            this.blockUI.destroy();
        }
    }
}



blockUI.service.ts (with latest Angular, 2.4.0 is tested fine)

In this version, we have to set the root component view from outside.
So we declare a PUBLIC ViewContainerRef variable.

import {Injectable, ApplicationRef, ElementRef, ComponentRef, ViewContainerRef, ComponentFactory,ComponentFactoryResolver } from '@angular/core';
import {BlockUIComponent} from './blockUI.component';

@Injectable()
export class BlockUIService {
    private blockUI: ComponentRef<BlockUIComponent>;
    public vRef: ViewContainerRef;

    constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

    public start() {
        let factory = this.componentFactoryResolver.resolveComponentFactory(BlockUIComponent);
        this.blockUI = this.vRef.createComponent(factory);
    }


    public stop() {
        if (this.blockUI) {
            this.blockUI.destroy();
        }
    }
}






A reference to an Angular application running on a page. We can use this class to get the root component. For example,




 

An instance of a Component created via a ComponentFactory.
In this sample, we need create a dynamic ComponentRef<BlockUIComponent> instance.


The View Container could be attached with one or more View.
We can insert the blockUI View to root component by function: createComponent

viewContainerRef.createComponent(factory);

and destroy the blockUI view by function: remove.



Use ComponentFactoryResolver class to create the component factory and the blockUI component.


Usage

Here is an example for using our blockUI.

App.module.ts

import { BlockUIComponent } from './components/blockUI/blockUI.component';
import { BlockUIService } from './components/blockUI/blockUI.service';

@NgModule({
    declarations: [
      //...
      BlockUIComponent
    ],
    imports: [
      //...
    ],
    entryComponents: [
          BlockUIComponent
    ],
    providers: [
        //...
        BlockUIService
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }


What is entryComponents?

It’s used for compiling the dynamic component with ViewContainerRef.createComponent(). Notice that the components registered in routes are also automatically added to entryComponents as well.



Any component


//For the old version of BlockUIService with Angular 2.1.0 or earlier
//constructor(
//  private blockUI: BlockUIService) {
//}

constructor(
    private viewContainerRef: ViewContainerRef,
    private blockUI: BlockUIService) {
        this.blockUI.vRef= this.viewContainerRef;
}

private getLaggers() {
    this.blockUI.start();
    return new Promise(
      resolve => {
          this.ypService.getLaggers().then(
            data => {
                this.laggers = data;
                resolve();
                this.blockUI.stop();
            }, err => { console.log(err); });

      });
}





Demo






Github









Reference










沒有留言:

張貼留言