2

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.

7
  • in my case I need such thing. I am making plugn which use component to fill other component. Commented Sep 15, 2018 at 22:53
  • Maybe if you posted some of the code I would understand why you need such thing. Commented Sep 15, 2018 at 22:54
  • yes, sorry, dropped service and component. This is logic of modal which I need to put incoming component within mine premade component. Commented Sep 15, 2018 at 23:00
  • I do not see why you need the service.ts file at all. Why not move the methods defined in service.ts to component.ts? Commented Sep 15, 2018 at 23:10
  • 2
    If you think you need to use any component inside of a Service -> you are doing it wrong. Please read the guidelines and try to work with the following pattern: "high cohesion low coupling" this is what Services try to realize. Commented Sep 15, 2018 at 23:29

1 Answer 1

3
+50

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

Sign up to request clarification or add additional context in comments.

6 Comments

I made all modifications to add this code, and close function is not working, what exactly is closing it, can you drop some comments
You already have the function close in your service. So you can just adapt it? I will give you some link later if it still doesn't work
Ok, what do you mean by the close function is not working ? In your dialog component you will call the close function from a button or something else. Since you service will have subscribe to the "onModalClose()" it will be notified. Perhaps what you are missing here is the "obj.id" that you use in the close function of your service. So perhaps you can assign to your "component" an "id" and when you close it you give this information to the subscriber. this.modalClose.next(this.id);
I just now figured out that you simply make the bridge working between two files, and this is nice, working. Thank you allot, my problem is solve and more logical now :)
Happy it helped :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.