5

We have a requirement where depending on a static/global setting which will be set depending on the customer they want a different component to load for a route. The reason being they want slightly different functionality for a part of the application so we are going to write a component for each that caters for their scenarios. Is there a way to choose the component for a route dynamically/at runtime and keep the same route/url. Below is a simplified example of what we would like to achieve:

Component1:

@Component({
  selector: 'customeronecomponent',
  templateUrl: './customeronecomponent.component.html'
})

export class CustomerOneComponent implements OnInit {
}

Component2:

@Component({
  selector: 'customertwocomponent',
  templateUrl: './customertwocomponent.component.html'
})

export class CustomerTwoComponent implements OnInit {
}

Route:

{ path: 'home', component: CustomerComponentProvider },

In this instance the CustomerComponentProvider will internally check the setting and either return CustomerOneComponent or CustomerTwoComponent.

In angular2/4 is that the best way to do this or is it better to have a single component and on that component load the right component, the downside I see there is we would have three components instead of two for each route we need this.

5
  • Cant you have two route, home-1 and home-2 both registered and based upon customer condition you navigate to either one of them? may be this is totally not applicable, you can provide more detail in your question. Commented Aug 11, 2017 at 19:09
  • I have tried to add more information, I cannot find a way to do this in Angular, there may not be a way in which case hopefully someone can provide the best option that keeps the URL the same. We are hoping to use this for additional components and don't want to add route-1, route-2, etc. for many components but rather have providers that get the right component as there may be more than just 2 options. Commented Aug 12, 2017 at 6:44
  • How exactly should that component resolve be performed? Is the correct component read from config and then whole application restarted (browser refresh) or you need on-the-fly change without app restart? Commented Aug 13, 2017 at 8:23
  • No on the fly change needed, there will be a const value set and on deployment will be changed and not changed again for that deployment. Commented Aug 14, 2017 at 15:35
  • You can use a global variable in routing configuration. The routing generator would be a function that build the routing array based on that/those global variables. Commented Aug 16, 2017 at 21:48

4 Answers 4

8
+50

You may already be doing this internally in your CustomerComponentProvider, but in case not - there is Dynamic Component Loader.

The example given in the docs is a bit busy, so here's a dumbed-down version: Plunker

Essentially, a wrapper is used to switch between components depending on config string passed in, or retrieved from a service.

import { Component, Input, OnChanges, 
  ComponentFactoryResolver, ViewContainerRef } from '@angular/core';
import { Component1 } from './component1';
import { Component2 } from './component2';

@Component({
  selector: 'component-wrapper',
  template: `<div></div>`
})
export class ComponentWrapper implements OnChanges {

  @Input() config;

  private componentMap = {
    component1 : Component1,
    component2 : Component2,
  }

  constructor(
    private viewContainerRef: ViewContainerRef,
    private componentFactoryResolver: ComponentFactoryResolver
  ) {}

  ngOnChanges() { 
    this.setComponent();
  }

  setComponent() {
    const componentFactory = this.componentFactoryResolver
      .resolveComponentFactory(this.componentMap[this.config]);
    this.viewContainerRef.clear();
    this.viewContainerRef.createComponent(componentFactory);
  }

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

8 Comments

Thank you for the comprehensive example, can you tell me if this is a better (performance or best practices) that the solution by Younis below which shows or hides the right component? Will this not create an instance of each component unless it's used as opposed to switching in the template which does?
I have just tested and the constructor is not called for the second component so I will assume while this is more complicated than switching with an *ngIf in the template it means not creating more instances than we want to show.
Actually I have just tried Younis's solution and again only one constructor is called, can you clarify why this is better than the simpler solution below?
I personally would use Younis' solution as it is simpler, (always better for cost of ownership), also Adrian's route configuration answer is simpler. I mention ComponentFactoryResolver / ViewContainerRef as it's put out there by Angular as the way to do dynamic components. This example rounds out the question. I also wanted to pare down the example given in Angular docs.
The only advantage I see with dynamic component is when coupling is a concern. The component map or the component itself can be injected via @Input. See Günter Zöchbauer's comment here (stackoverflow.com/questions/37649358/…) Aside from that, dynamic component is hot-switchable. If I'm not mistaken, ngIf requires a reload to change components. (Not one of your requirements).
|
2

You can update the router configuration whenever you want, so depending on the setting you have you can add or update a route config to resolve to the component that you need.

Have a look at the config property or maybe more suited for this, the resetConfig method on the Router. You could inject the Router in the component where you want to handle this and update the routes according to the global setting that you have.

1 Comment

Of all the approaches I think this is the cleanest - Richard and Younis's approaches result in you having a third component that manages two other components. In my case I had two standalone admin "end-user" UIs. They both display independently to admin but when the given end-user is logged in I wanted to display their respective UI on a /my-profile route - I load a config from the server when they log in so I was able to tap into this and manipulate the route - I just find it by path and then set it accordingly.
2

in the template for CustomerComponentProvider , you can display either of the components by checking the setting

say your setting returns the type on a customer (1/2) based on which you can set showCustomerOne/showCustomerTwo to true

<app-customer-one-component *ngIf="showCustomerOne"> </app-customer-one-component>
<app-customer-two-component *ngIf="showCustomerTwo"> </app-customer-two-component>

check for the setting in constructor of CustomComponentProvider and set either showCustomerOne to True or showCustomerTwo to True.

  showCustomerOne;
  showCustomerTwo;

  constructor() {
     if(customer.type == '1'){
         this.showCustomerOne = true;
      }else if (customer.type == '2'){
         this.showCustomerTwo = true;
      }
  };

Comments

0

You could try something like reflect-metadata

You would define metadata key for type and then get the type of the component based on whatever is in your config and feed that into routeConfig or by resetting the router through router.resetConfig as @Adrian Faciu already suggested:

https://angular.io/api/router/Router#resetConfig

Comments

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.