Angular2: How to inject two instances of the same

2019-09-07 07:49发布

问题:

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.

回答1:

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.