Skip to content

Commit

Permalink
chore(modal): view container ref made optional for component loader
Browse files Browse the repository at this point in the history
  • Loading branch information
valorkin committed Jun 27, 2017
1 parent 91f06a9 commit 6e95929
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 140 deletions.
3 changes: 1 addition & 2 deletions demo/src/app/components/+modal/demos/service/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { BsModalRef } from 'ngx-bootstrap/modal/modal-options.class';
})
export class DemoModalServiceComponent {
public modalRef: BsModalRef;
constructor(private modalService: BsModalService, private element: ElementRef, private renderer: Renderer, private vcRef: ViewContainerRef) {
this.modalService = this.modalService.create(this.element, this.vcRef, this.renderer);
constructor(private modalService: BsModalService) {
}
public openModal(template: string | TemplateRef<any>) {
this.modalRef = this.modalService.show(template, {backdrop: 'static'});
Expand Down
3 changes: 1 addition & 2 deletions demo/src/app/components/+modal/modal-section.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@ let titleDoc = require('html-loader!markdown-loader!./docs/title.md');
<h2 routerLink="." fragment="service" id="service">Modal service</h2>
<p>Open a modal from service</p>
<p>To be able to open modals from service, inject BsModalService, ElementRef, ViewContainerRef and Renderer to your constructor. <br>
Then prepare modal service like this <code>this.modalService = this.modalService.create(this.element, this.vcRef, this.renderer)</code>. <br><br>
<p>To be able to open modals from service, inject BsModalService to your constructor. <br>
Finally, call <code>.show()</code> method of modal service. Pass a TemplateRef or a component as a first argument and config as a second (optionally). If you're passing a component, add it to <code>entryComponents</code> of your <code>NgModule</code><br><br>
<code>.show()</code> method returns an instance of BsModalRef class with <code>.hide()</code> method and <code>content</code> property where you'll find a component which you've passed to service <br>
If you passed a component to <code>.show()</code> you can get access to opened modal by injecting BsModalRef. See example for more info
Expand Down
11 changes: 9 additions & 2 deletions demo/src/ng-api-doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,13 @@ export const ngdoc: any = {
}
]
},
"BsModalFactory": {
"fileName": "src/modal/bs-modal-factory.service.ts",
"className": "BsModalFactory",
"description": "",
"methods": [],
"properties": []
},
"BsModalService": {
"fileName": "src/modal/bs-modal.service.ts",
"className": "BsModalService",
Expand Down Expand Up @@ -933,11 +940,11 @@ export const ngdoc: any = {
"args": [
{
"name": "content",
"type": "TemplateRef | any"
"type": "any"
},
{
"name": "config",
"type": "ModalOptions"
"type": "any"
}
],
"returnType": "BsModalRef"
Expand Down
88 changes: 47 additions & 41 deletions src/component-loader/component-loader.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// todo: merge events onShow, onShown, etc...
// todo: add global positioning configuration?
import {
ApplicationRef,
ComponentFactory,
ComponentFactoryResolver,
ComponentRef,
Expand Down Expand Up @@ -39,16 +40,9 @@ export class ComponentLoader<T> {

private _providers: Provider[] = [];
private _componentFactory: ComponentFactory<T>;
private _elementRef: ElementRef;
private _zoneSubscription: any;
private _contentRef: ContentRef;
private _innerComponent: ComponentRef<T>;
private _viewContainerRef: ViewContainerRef;
private _injector: Injector;
private _renderer: Renderer;
private _ngZone: NgZone;
private _componentFactoryResolver: ComponentFactoryResolver;
private _posService: PositioningService;

private _unregisterListenersFn: Function;

Expand Down Expand Up @@ -77,26 +71,16 @@ export class ComponentLoader<T> {
* Do not use this directly, it should be instanced via
* `ComponentLoadFactory.attach`
* @internal
* @param _viewContainerRef
* @param _elementRef
* @param _injector
* @param _renderer
* @param _componentFactoryResolver
* @param _ngZone
* @param _posService
*/
// tslint:disable-next-line
public constructor(_viewContainerRef: ViewContainerRef, _renderer: Renderer,
_elementRef: ElementRef,
_injector: Injector, _componentFactoryResolver: ComponentFactoryResolver,
_ngZone: NgZone, _posService: PositioningService) {
this._ngZone = _ngZone;
this._injector = _injector;
this._renderer = _renderer;
this._elementRef = _elementRef;
this._posService = _posService;
this._viewContainerRef = _viewContainerRef;
this._componentFactoryResolver = _componentFactoryResolver;
public constructor(private _viewContainerRef: ViewContainerRef,
private _renderer: Renderer,
private _elementRef: ElementRef,
private _injector: Injector,
private _componentFactoryResolver: ComponentFactoryResolver,
private _ngZone: NgZone,
private _applicationRef: ApplicationRef,
private _posService: PositioningService) {
}

public attach(compType: Type<T>): ComponentLoader<T> {
Expand All @@ -122,6 +106,7 @@ export class ComponentLoader<T> {
return this;
}

// todo: appendChild to element or document.querySelector(this.container)
public show(opts: { content?: string | TemplateRef<any>, [key: string]: any } = {}): ComponentRef<T> {
this._subscribePositioning();
this._innerComponent = null;
Expand All @@ -130,17 +115,30 @@ export class ComponentLoader<T> {
this.onBeforeShow.emit();
this._contentRef = this._getContentRef(opts.content);
const injector = ReflectiveInjector.resolveAndCreate(this._providers, this._injector);
this._componentRef = this._viewContainerRef
.createComponent(this._componentFactory, 0, injector, this._contentRef.nodes);

this._componentRef = this._componentFactory.create(injector, this._contentRef.nodes);
this._applicationRef.attachView(this._componentRef.hostView);
// this._componentRef = this._viewContainerRef
// .createComponent(this._componentFactory, 0, injector, this._contentRef.nodes);
this.instance = this._componentRef.instance;

Object.assign(this._componentRef.instance, opts);

if (this.container instanceof ElementRef) {
this.container.nativeElement
.appendChild(this._componentRef.location.nativeElement);
}

if (this.container === 'body' && typeof document !== 'undefined') {
document.querySelector(this.container as string)
.appendChild(this._componentRef.location.nativeElement);
}

if (!this.container && this._elementRef) {
this._elementRef.nativeElement.parentElement
.appendChild(this._componentRef.location.nativeElement);
}

// we need to manually invoke change detection since events registered
// via
// Renderer::listen() are not picked up by change detection with the
Expand All @@ -157,19 +155,25 @@ export class ComponentLoader<T> {
}

public hide(): ComponentLoader<T> {
if (this._componentRef) {
this.onBeforeHide.emit(this._componentRef.instance);
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._componentRef.hostView));
this._componentRef = null;

if (this._contentRef.viewRef && this._viewContainerRef.indexOf(this._contentRef.viewRef) !== -1) {
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._contentRef.viewRef));
this._contentRef = null;
}

this._componentRef = null;
this.onHidden.emit();
if (!this._componentRef) {
return this;
}

this.onBeforeHide.emit(this._componentRef.instance);

const componentEl = this._componentRef.location.nativeElement;
componentEl.parentNode.removeChild(componentEl);
this._componentRef.destroy();
// this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._componentRef.hostView));
//
// if (this._contentRef.viewRef && this._viewContainerRef.indexOf(this._contentRef.viewRef) !== -1) {
// this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._contentRef.viewRef));
// }

this._contentRef = null;
this._componentRef = null;

this.onHidden.emit();
return this;
}

Expand Down Expand Up @@ -246,13 +250,15 @@ export class ComponentLoader<T> {
this._zoneSubscription = null;
}

private _getContentRef(content: string | TemplateRef<any>): ContentRef {
private _getContentRef(content: string | TemplateRef<any> | any): ContentRef {
if (!content) {
return new ContentRef([]);
}

if (content instanceof TemplateRef) {
const viewRef = this._viewContainerRef.createEmbeddedView<TemplateRef<T>>(content);
const viewRef = content.createEmbeddedView({});
this._applicationRef.attachView(viewRef);
// const viewRef = this._viewContainerRef.createEmbeddedView<TemplateRef<T>>(content);
return new ContentRef([viewRef.rootNodes], viewRef);
}

Expand Down
22 changes: 9 additions & 13 deletions src/component-loader/component-loader.factory.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
import {
Injectable, NgZone, ViewContainerRef, ComponentFactoryResolver, Injector,
Renderer, ElementRef
Renderer, ElementRef, ApplicationRef
} from '@angular/core';
import { ComponentLoader } from './component-loader.class';
import { PositioningService } from '../positioning';

@Injectable()
export class ComponentLoaderFactory {
private _componentFactoryResolver: ComponentFactoryResolver;
private _ngZone: NgZone;
private _injector: Injector;
private _posService: PositioningService;

public constructor(componentFactoryResolver: ComponentFactoryResolver, ngZone: NgZone,
injector: Injector, posService: PositioningService) {
this._ngZone = ngZone;
this._injector = injector;
this._posService = posService;
this._componentFactoryResolver = componentFactoryResolver;
public constructor(private _componentFactoryResolver: ComponentFactoryResolver,
private _ngZone: NgZone,
private _injector: Injector,
private _posService: PositioningService,
private _applicationRef: ApplicationRef) {
}

/**
Expand All @@ -27,8 +22,9 @@ export class ComponentLoaderFactory {
* @param _renderer
* @returns {ComponentLoader}
*/
public createLoader<T>(_elementRef: ElementRef, _viewContainerRef: ViewContainerRef, _renderer: Renderer):ComponentLoader<T> {
public createLoader<T>(_elementRef: ElementRef, _viewContainerRef: ViewContainerRef, _renderer: Renderer): ComponentLoader<T> {
return new ComponentLoader<T>(_viewContainerRef, _renderer, _elementRef,
this._injector, this._componentFactoryResolver, this._ngZone, this._posService);
this._injector, this._componentFactoryResolver, this._ngZone, this._applicationRef,
this._posService);
}
}
9 changes: 0 additions & 9 deletions src/modal/bs-modal-factory.service.ts

This file was deleted.

Loading

0 comments on commit 6e95929

Please sign in to comment.