Reputation: 1508
I have logical problem. I need component which will be imported inside an service and parallel inside that component I need to have that service. The error below:
Circular dependency: dist\services\modal.service.js -> dist\components\modal\modal.component.js -> dist\services\modal.service.js
Which is the best way of solving this case. My best solution is to use third service which will inject those two files somehow. Reason to have component inside the service is to use it within other component.
service.ts
import { ComponentFactoryResolver, EmbeddedViewRef, ApplicationRef, ComponentRef, Injectable, Injector
} from '@angular/core';
import { ModalComponent } from '../components/modal/modal.component';
@Injectable({
providedIn: 'root'
})
export class ModalService {
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private appRef: ApplicationRef,
private injector: Injector
){}
private data = {};
private last;
open(component: any, obj:any = {}) {
if(!obj.id) obj.id = new Date().getTime();
// Create a component reference from the component
const componentRef = this.componentFactoryResolver
.resolveComponentFactory(component)
.create(this.injector);
// Attach component to the appRef so that it's inside the ng component tree
this.appRef.attachView(componentRef.hostView)
// Get DOM element from component
const contentElem = (componentRef.hostView as EmbeddedViewRef<any>)
.rootNodes[0] as HTMLElement;
// Create a component reference from the component
let componentRefer = this.componentFactoryResolver
.resolveComponentFactory(ModalComponent)
.create(this.injector);
// Attach component to the appRef so that it's inside the ng component tree
this.appRef.attachView(componentRefer.hostView);
// Get DOM element from component
const domElem = (componentRefer.hostView as EmbeddedViewRef<any>)
.rootNodes[0] as HTMLElement;
// Append DOM element to the body
document.body.appendChild(domElem);
// Append DcontentElemOM element to the body
domElem.querySelector("#modalHoster").appendChild(contentElem);
// Wait some time and remove it from the component tree and from the DOM
this.data[obj.id]={
componentRefer: componentRefer,
appRef: this.appRef
}
this.last=obj;
return obj.id;
}
pull(){
return this.last;
}
close(id){
this.data[id].appRef.detachView(this.data[id].componentRefer.hostView);
}
}
component.ts
import { Component, OnInit } from '@angular/core';
import { ModalService } from '../../services/modal.service';
@Component({
selector: 'modal',
templateUrl: './modal.component.html',
styleUrls: ['./modal.component.scss']
})
export class ModalComponent implements OnInit {
close(){
this.mod.close(this.id);
}
constructor(private mod: ModalService){}
ngOnInit() {
let obj=this.mod.pull();
for(let key in obj){
this[key]=obj[key];
}
}
}
Could be that my logic is wrong, this is what I am asking. Those two service and component are inside module, not the app. App only using the service, component is not accessible. Service need piece of html/css/ts code to be as container for the piece of html/css/ts code which is app providing.
Upvotes: 2
Views: 1181
Reputation: 2556
You logic is not really wrong here but you can easily avoid circular dependencies.
You sure need your component in the service to create a new instance. However you don't really need the service inside your modal.
What you can do is to add an Observable Subject inside your modal. And when you instance your modal you subscribe to this subject inside your service to remove the dialog from is container.
Here is a simple code example :)
in your modal component :
private modalClose: Subject<any> = new Subject();
onModalClose(): Observable<any> {
return this.modalClose.asObservable();
}
close() { // Will be generally called from the UI (close button for example)
this.modalClose.next();
this.modalClose.complete();
}
In your service :
componentRefer.instance.onModalClose().subscribe( () => {
// Here you close your modal :D
});
For more information :
Here you can find a "modal/dialog" of my own.
https://github.com/xrobert35/asi-ngtools/tree/master/src/components/asi-dialog
To instanciate a dialog you must use the service. The service manage a "container" which can have "many" dialog. Using the same technique I don't have circulare dependencies.
here you can find a working exemple of the component : https://ng-tools.asi.fr/views/showroom/asi-ngtools/components/asi-dialog
Upvotes: 3