angularrenderingangular-componentsangular-dynamic-components

Deprecated ComponentFactoryResolver, Trying to use ViewContainerRef


I am trying to setup my auth.component.ts file to use ViewContainerRef from the placeholder.directive.ts file, but I am unsure what I need to populate inside the createComponent() at the end of the auth.component.ts file.

Below is the auth.component.ts file, with the original code using ComponentFactorResolver commented out at the bottom in the showErrorAlert method:

import { Component, ComponentFactoryResolver, ViewChild, ViewContainerRef } from "@angular/core";
import { NgForm } from "@angular/forms";
import { AuthService, AuthResponseData } from "./auth.service";
import { Observable } from "rxjs";
import { Router } from "@angular/router";
import { AlertComponent } from "../shared/alert/alert.component";
import { PlaceholderDirective } from "../shared/placeholder/placeholder.directive";

@Component({
    selector: 'app-auth',
    templateUrl: './auth.component.html'
})

export class AuthComponent {
    isLoginMode = true;
    isLoading = false;
    error: string = null;
    @ViewChild(PlaceholderDirective, { static: false}) alertHost: PlaceholderDirective;

    constructor(
        private authService: AuthService, 
        private router: Router,
        private componentFactoryResolver: ComponentFactoryResolver,
        private viewContainerRef: ViewContainerRef
    ) {}

    onSwitchMode() {
        this.isLoginMode = !this.isLoginMode
    }

    onSubmit(form: NgForm) {
        if (!form.valid) {
            return;
        }
        const email = form.value.email;
        const password = form.value.password;

        let authObs: Observable<AuthResponseData>;

        this.isLoading = true;
        if (this.isLoginMode) {
            authObs = this.authService.login(email,password)
        } else {
            authObs =  this.authService.signup(email,password)
        }

        authObs.subscribe(
            resData => {
                console.log(resData);
                this.isLoading = false;
                this.router.navigate(['/recipes']);
            }, errorMessage => {
                console.log(errorMessage);
                this.error = errorMessage
                this.showErrorAlert(errorMessage);
                this.isLoading = false;
            }
        );

        form.reset();
    }

    onHandleError() {
        this.error = null;
    }

    private showErrorAlert(message: string) {
        // const alertCmpFactory = this.componentFactoryResolver.resolveComponentFactory(AlertComponent);
        // const alertCmpFactory = this.componentFactoryResolver.resolveComponentFactory(AlertComponent);
        // const hostViewContainerRef = this.alertHost.viewContainerRef;
        // hostViewContainerRef.clear();
        // hostViewContainerRef.createComponent(alertCmpFactory);



        const hostViewContainerRef = this.alertHost.viewContainerRef;
        hostViewContainerRef.clear();

        hostViewContainerRef.createComponent(hostViewContainerRef);
    }
}

Below is the placeholder.directive.ts code:

import { Directive, ViewContainerRef } from "@angular/core";

@Directive({
    selector: '[appPlaceholder]'
})

export class PlaceholderDirective {
    constructor(public viewContainerRef: ViewContainerRef) {}
}

I feel like I was able to get most of it converted over, but the line from auth.component.ts below I cannot figure out what should be the dynamic child component that was mentioned in another Stack Overflow question

Replace deprecated Angular ComponentFactoryResolver, ComponentFactory

hostViewContainerRef.createComponent(hostViewContainerRef);

Please let me know your thoughts. Thank you!

I tried following along with the post from here Replace deprecated Angular ComponentFactoryResolver, ComponentFactory

However, I am struggling to understand what "theYourDynamicChildComponent" or "YourElement" should be.


Solution

  • You can just render the component using createComponent directly.

    I think the references to "theYourDynamicChildComponent" is to AlertComponent.

    The thing to note is that, if your component (AlertComponent) is standalone then it's simpler to use. If your component is not standalone, then it might be needed to be added to the declarations array.

    Also when you create the component and you notice the component does not work as expected, you can try adding environmentInjector as a parameter to the createComponent(AlertComponent, {environmentInjector: environmentInjector}); Where the environment injector can be fetched using environmentInjector = inject(EnvironmentInjector);.

        const hostViewContainerRef = this.alertHost.viewContainerRef;
        hostViewContainerRef.clear();
    
        hostViewContainerRef.createComponent(AlertComponent);
    

    If the component is rendered multiple times, you might get the error.

    NG0100: Expression Changed After Checked • Angular