Let's say I have html like this:
<div *ngIf="(isVisible | async)">
<app-mycomponent></app-mycomponent>
</div>
<div *ngIf="!(isVisible | async)">
<app-mycomponent></app-mycomponent>
</div>
with a button that toggles isVisible. This creates a new component instance each time I toggle the visibility.
So my question is: Can I change the implementation to have same instance of AppMyComponent to be used when visibility is toggled. E.g. by having a wrapper component that adds the app-mycomponent dynamically or something.
EDIT: My real case is quite complex and even though this example does not make sense, I'm very much interested can this be done.
EDIT2: Here's stackbliz that solves my problem.
It will create multiple instances of a service. Every time a new instance of provided service will be created when a component is used inside another component.
You can simply extend your base component and overwrite the template. This allows you to have different components with the exact same functionality, but different templates.
In this article we'll explore when and why Angular creates two instances of the same service and what solutions exist to ensure a service remains a singleton in the entire application.
Two or more components use the same element selector. Because there can only be a single component associated with an element, selectors must be unique strings to prevent ambiguity for Angular.
This answer is based on the stackblitz example provided in this answer to a similar question I asked.
Step #1: Create a directive that you will use wherever you want to have your reusable component.
@Directive({
selector: "[reusable-outlet]",
})
export class ReusableDirective implements OnInit {
constructor(
private viewContainerRef: ViewContainerRef,
private reusableService: ReusableService
) {}
public ngOnInit(): void {
this.reusableService.attach(this.viewContainerRef);
}
}
Step #2 Create the service that will be in charge of:
Note: Knowing when to detach the component is based on router events, but it should be possible to base it on messages instead, if you need to change where your component is without having navigation changes.
@Injectable()
export class ReusableService {
private componentRef: ComponentRef<ReusableComponent>;
private currentViewContainerRef: ViewContainerRef;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private injector: Injector,
private router: Router
) {
const componentFactory =
this.componentFactoryResolver.resolveComponentFactory(ReusableComponent);
this.componentRef = componentFactory.create(injector);
this.router.events.subscribe((event) => {
if (event instanceof NavigationStart && this.currentViewContainerRef) {
this.detach(this.currentViewContainerRef);
}
});
}
public attach(viewContainerRef: ViewContainerRef) {
this.currentViewContainerRef = viewContainerRef;
viewContainerRef.insert(this.componentRef.hostView);
}
public detach(viewContainerRef: ViewContainerRef) {
viewContainerRef.detach(
viewContainerRef.indexOf(this.componentRef.hostView)
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With