Honchar Denys
Honchar Denys

Reputation: 1508

Angular 6 Circular dependency Logic solution

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

Answers (1)

xrobert35
xrobert35

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

Related Questions