Search code examples
angularangular2-servicesangular2-providers

Angular2: How to inject two instances of the same service into multiple components?


Suppose that I have an Angular Service that looks like this:

@Injectable()
export class Clipboard {

    constructor(private multiple: Multiple, private di:DI, private injected:Injected, private things: Things){}

    // The clipboard has local state: 
    private isCut: boolean;
    private toPaste: Hero;

    cut(hero: Hero){
        this.isCut = true;
        this.toPaste = hero;
    }

    copy(hero: Hero){
        this.isCut = false;
        this.toPaste = hero;
    }

    paste(locaction: Location){
        // Lots of really complex logic
    }

    canPaste(potentialLocation: Location){
        // Lots of really complex logic
    }

}

Currently I have several components that uses the Clipboard Service.

When you right click on a hero you can copy/cut them. Later, in the same component or a different component, you can paste the hero. Something like this:

@Component({
    ...
})
export class HeroTable {

    constructor(private clipboard: Clipboard){}

    cutHero(hero: Hero): void {
        this.clipboard.cut(hero);
    }
}

I now want to add drag and drop to my components. Interestingly, the canPaste, paste, cut and copy methods are identical for drag and drop, however I need to use a separate instance of the clipboard to prevent the following scenario:

  1. User cuts 'Batman'
  2. User drags & drops 'Superman' to a new location
  3. User attempts to paste 'Batman' but unfortunately the clipboard has been polluted by the drag-n-drop.

I could create a new class called DragDrop that extends the Clipboard:

@Injectable()
export class DragDrop extends Clipboard{

    // Drag and Drop behaves identically to the Clipboard.  Please
    // don't override any behaviour here.  This class is a hack to 
    // get a second injectable instance of Clipboard.

}

This allows me to update the HeroTable like this: j

@Component({
    ...
})
export class HeroTable {

    constructor(private clipboard: Clipboard, private dragDrop: DragDrop){}

    cutHero(hero: Hero): void {
        this.clipboard.cut(hero);
    }

    dragHer(hero: Hero): void {
        this.dragDrop.cut(hero);
    }
}

This also allows me to use the two instances of the Clipboard in another component and tell which is which. I need to make sure that all components know which Clipboard should be used for Cut/Paste and which should be used for drag/drop.

Unfortunatly, this solution feels like a hack. Is there an Angular blessed way to do this?


I found this question: Angular2: How to use multiple instances of same Service? which seems very similar, however I am hoping that given the details that I am providing, I may get slightly different responses.


Solution

  • There are not so many ways to do this. I believe they are covered in the cited question and also here.

    For Clipboard injectable class without dependencies, it is

    ...
    // NgModule
    providers: [
      { provide: Clipboard, useValue: Clipboard }
    ]
    

    and

    export class HeroTable {
        private clipboard: Clipboard;
        private dragDrop: Clipboard;
    
        constructor(Clipboard: Clipboard){
          this.clipboard = new Clipboard;
          this.dragDrop = new Clipboard;
        }
        ...
    }
    

    For Clipboard injectable class with dependencies, it is

    @Injectable()
    class DragDropClipboard {}
    
    ...
    // NgModule
    providers: [
      Clipboard,
      { provide: DragDropClipboard, useClass: Clipboard }
    ]
    

    and

    export class HeroTable {
        constructor(private clipboard: Clipboard, private dragDrop: DragDropClipboard) {}
        ...
    }
    

    There's nothing wrong with

    @Injectable()
    class DragDropClipboard extends Clipboard {}
    

    There should be a placeholder for the second provider any way, at least the typing will be correct in this case, but it will likely create more verbose output.