Search code examples
servicetypescriptangularcomponentsfactory-pattern

How to manually create and wire a service at runtime to a component in typescript Angular 2


Here's my use case:

I have an interface with 4 different implementations WorkflowA, WorkflowB, WorkflowC and WorkflowD.

export interface IWorkflow {
  getQuestions(); 
  validateQuestions();
  getWorkflowSteps();
}

I have a WorkflowManager class that returns the appropriate workflow based on an input variable (Factory pattern?)

export class WorkflowManager {

  workflow:IWorkflow;

  getWorkflow(workflow){
    switch (workflow) {
      case 'workflowA':
        return new workflowA();
      case 'workflowB':
        return new workflowB();
      case 'workflowC':
        return new workflowC();
      case 'workflowD':
        return new workflowD();
      default:
        throw new Error(`Unrecognized workflow: ${workflow}`);
    }
  }

}

I also have a component WorkflowComponent that gets loaded

Example: /workflow=WorkflowA loads WorkflowComponent. The component then extracts the routeparam workflow and based on the value, it then passes it to the WorkflowManager which returns the appropriate workflow.

Here's a snippet of my component

@Component({
 ...
})

export class Workflow implements OnInit {

  workflowInstance: IWorkflow;

  constructor(
    private route: ActivatedRoute,
    private workflowManager: WorkflowManager,
  ) {

  }

  ngOnInit() {
    this.route.params.subscribe(params => {
      let regType: string = params['workflow']; 
      this.workflowInstance = this.workflowManager.getWorkflow(workflow);
    });
  }

}

But I don't like the idea of manually instantiating Workflow* classes. How can I improve this so I wire it using Angular2 supplied API so that it can be managed/injected in to other components?

Also in my current implementation, I will have to make ajax calls outside of Angular framework which I don't think is the best thing to do.

Any ideas on how to take advantage of the Angular framework and improve this?


Solution

  • One way is to inject an injector and acquire the workflow instance imperatively like:

    @Injectable()
    export class WorkflowManager {
      constructor(private injector:Injector) {}    
    
      workflow:IWorkflow;
    
      getWorkflow(workflow){
        switch (workflow) {
          case 'workflowA':
            return this.injector.get(workflowA);
          case 'workflowB':
            return this.injector.get(workflowB);
          case 'workflowC':
            return this.injector.get(workflowC);
          case 'workflowD':
            return this.injector.get(workflowD);
          default:
            throw new Error(`Unrecognized workflow: ${workflow}`);
        }
      }
    }
    

    Alternatively you can inject workflowA, workflowB, workflowC, workflowA and just return the right one depending on the workflow but I think using the injector is a better fit for this use case.

    Ensure you provide workflowA, workflowB, workflowC.