Ways to circumvent ng switch and create a component based on type

In my current code, I have an array called resourceTypes and I am using ngSwitch to create different components/directives based on the TypeName. However, I find this approach cumbersome as I have to update the code every time I add a new resource editor. How can I refactor the code so that the correct resource-editor is created automatically based on the type without relying on ngSwitch? I have considered using ng-content, but I'm struggling to implement it effectively.

TL;DR: How can I improve the code below to eliminate the need for ngSwitch and dynamically link the type to a component?

 <div *ngFor="let aType of resourceTypes; let i = index" role="tabpanel" class="tab-pane" [ngClass]="{'active': i==0}" [attr.id]="aType.Name">
                <span *ngSwitch="aType.Name">
                    {{aType.Name}} tab content here
                    <config-resource-editor [application]="application" ngSwitchWhen="Config" template></config-resource-editor>
                    <mvc-resource-editor [application]="application" ngSwitchWhen="MVC" template></mvc-resource-editor>
                    <other-resource-editor [application]="application" ngSwitchWhen="Other" template></other-resource-editor>
                    <wcf-resource-editor [application]="application" ngSwitchWhen="WCF" template></wcf-resource-editor>
                    <web-resource-editor [application]="application" ngSwitchWhen="Web" template></web-resource-editor>
                    <webapi-resource-editor [application]="application" ngSwitchWhen="WebAPI" template></webapi-resource-editor>
                </span>
            </div>

Answer №1

If you want to eliminate the switch case, consider creating a wrapper component and utilizing @ViewChild.

Here is an example of how your wrapper could look:

@Component({
    selector: 'my-wrapper',
    template: `<div #target></div>`
})
export class MyWrapper {
    @ViewChild('target', {read: ViewContainerRef}) target;
    cmpRef: ComponentRef<Component>;
    currentComponent: Component;
    private isViewInitialized: boolean = false;
    constructor(
            private componentFactoryResolver: ComponentFactoryResolver,
            private cdRef: ChangeDetectorRef,
            private wrapperService: WrapperService
    ){}

    updateComponent() {
        if (!this.currentComponent) {
            return;
        }
        if (!this.isViewInitialized) {
            return;
        }
        if (this.cmpRef) {
            this.cmpRef.destroy();
        }
        let factory = this.componentFactoryResolver.resolveComponentFactory(this.currentComponent);
        this.cmpRef = this.target.createComponent(factory);
    }

    ngAfterViewInit() {
        this.cdRef.detectChanges();
        this.isViewInitialized = true;
        this.currentComponentSubscription = this.wrapperService.getCurrentComponent().subscribe(c => {
            this.currentComponent = c;
            if (c) {
                this.updateComponent();
            }
        });
    }

    ngOnDestroy() {
        if (this.cmpRef) {
            this.cmpRef.destroy();
        }
        if(this.currentComponentSubscription){
           this.currentComponentSubscription.unsubscribe()
        }
    }
}

Create a WrapperService with a getter/setter for the current component where the getter returns a BehaviorSubject:

private _currentComponent: BehaviorSubject<Component> = new BehaviorSubject(null);
getCurrentComponent(): BehaviorSubject<Component> {
    return this._currentComponent;
}

setCurrentComponent(value: Component) {
   this._currentComponent.next(value);
}

Replace the ngSwitch in your parent component with the selector for the MyWrapper component and set the current component using the wrapper service.

Don't forget to add the components being attached/detached as entryComponents in the @NgModule:

@NgModule({
    entryComponents: <array of components>
})

Note: When setting the _currentComponent, provide the reference to the component rather than just a string.

Credits: Inspired by @Gunter's example. Thank you for the guidance!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide to implementing optional localization strings in React-Router paths

Incorporating react-router, I aim to implement internationalization for links following this format: domain.com/langISO/countryISO2/page Here are some examples of valid routes: domain.com/ -> Home Page domain.com/en/us -> Home Page domain.com/fr/f ...

The behavior of Angular 4 CSS and JS changes upon refreshing the page

Every time I try to load a page with this particular script: this.router.navigateByUrl('/report-result/'+report.id); It appears that not all the CSS and JS files are being loaded properly. The bootstrap popovers don't show up, and some ele ...

Is Your Website Sluggish because of an Excessive Amount of JavaScript on Page

After finally resolving some of the Javascript issues I was facing, I have streamlined my code to utilize just one library now, which is a huge improvement from how chaotic it was before. However, I have noticed a slight delay in the page load time, and I ...

Get the name of the array using JavaScript

Here is an example of my situation: var list1 = ['apple', 'banana', 'orange']; var list2 = ['carrot', 'lettuce', 'tomato']; When I use: alert(list1) I get: apple, banana, orange. This is corre ...

Verify the presence of a JSON object within the session storage using JavaScript

I'm currently developing a website where some data is stored within the session. In the initial page, I need to verify if the JSON object already exists in the session. Below is the code snippet that's causing issues: var passedTotal = JSON.par ...

Using `reduce` in TypeScript, you can organize an array of objects based on a shared property

Here is an example of an array: [ { id: '1', task: 'Grocery shopping', isImportant: true }, { id: '2', task: 'Meeting', isImportant: false }, { id: '3', task: &apos ...

Activate a button utilizing jQuery keyboard functionality

Here is what I have accomplished so far: http://jsfiddle.net/qEKfg/ I have created two buttons that activate on click and resemble keyboard keys. My goal is to make them animate only when the corresponding keys (CTRL and ...

When the mouse button is released or when an event listener is

I've been pondering a question that has yet to be fully answered. When I implement this technique to catch a mouse up event: <div onmouseup="/*Script to be executed*/"></div> Is it more efficient than this newer approach: <div id=" ...

The headers are correct, however Chrome is displaying the message "Resource interpreted as Document."

After reading numerous queries like this, I'm still struggling to find a solution... I utilize the archiver and express Node.js modules. My goal is to effortlessly send a zip file to the client. Here's a snippet of my code: res.set("Content-Typ ...

What could be causing this program to continuously add values to the message table whenever the page is refreshed?

Looking for a simple chatting system using php, mysql, html, css and javascript? Check out this code snippet. However, there seems to be an issue with the message sending functionality. Every time a user sends a message and refreshes the page, the same m ...

I have noticed that when I close the Material-UI dialog, it prevents me from interacting with my

After closing the material-ui dialog box, I seem to be unable to click or touch anything on my page. The useState hook is used here: const [open, setOpen] = useState(false); This function is called when handling the dialog close action: const handleClose ...

Comparison of various approaches for invoking JavaScript/jQuery functions

Do the following examples have a performance variation? Example 1: $(window).on('resize', abc); function abc(){ //some abc code } Example 2: $(window).on('resize', function(){ //some abc code }); If so, what are the positives ...

[server-auth]: The `useSession` function is required to be enclosed within a <SessionProvider /> component to avoid the error occurring in the current JavaScript file

While trying to validate authentication by following the next-auth documentation, I encountered an error stating "[next-auth]: useSession must be wrapped in a SessionProvider". I am using GitHub credentials for the validations. Here is my code: Currently ...

Angular - How to fix the issue of Async pipe not updating the View after AfterViewInit emits a new value

I have a straightforward component that contains a BehaviorSubject. Within my template, I utilize the async pipe to display the most recent value from the BehaviorSubject. When the value is emitted during the OnInit lifecycle hook, the view updates correc ...

Aggregate information from an array containing multiple nested arrays

With regards to marking this as answered by another question, please take note that this is not a flat array but an array of arrays. Additionally, the numbers provided are just examples for visual representation. I am attempting to iterate through an arra ...

Tips for retrieving the parameter value from a Javascript regular expression

let statement = "var text = "{templateUrl: 'conversations/conversations.tpl.html',"; let outcome = statement.match(/templateUrl:(\s*['"]\S*['"])/g); The intended result should be 'conversations/conversations.tpl.html&apo ...

Exploring the possibilities of utilizing React server components in my project

I am interested in experimenting with the new React API for making server-side component calls. However, I am unable to find any information on how to begin a project using server components. In an example of source code that I stumbled upon, it mentioned ...

What is preventing the dependency injection of AuthHttp (angular2-jwt) into a component?

UPDATE: Success! Problem Solved After much trial and error, I finally discovered the solution to my issue. It turned out that the problem lied in a simple configuration mistake. To rectify this, I made changes to both my package.json (dependencies section ...

"Sending a POST request from the smartphone application client developed using the Meteor

I'm currently working on a simple mobile app with Meteor that aims to send user location data to a geospatial database or server. However, I'm facing some challenges and uncertainties about the feasibility of this task using Meteor. The issue ari ...

Ways to incorporate an external JavaScript file into Angular and execute it within an Angular application

Imagine you have a file called index.js containing a function expression: $scope.submit = function() { if ($scope.username && $scope.password) { var user = $scope.username; var pass = $scope.password; if (pass == "admin" && user ...