19

I have a clickable icon on the page. On click on this icon, I would like to construct some text and copy that in the clipboard

<td><img src='./assets/Copy.gif' (click)="copyToClipboard()"  /></td> 

and in the Component

  copyToClipboard() {
     this.textToCopy = this.text1 + this.text2 + this.text3;  
     this.toastr.info('Copied to Clipboard');
  }

I have looked at https://www.npmjs.com/package/ngx-clipboard. However, this package requires to refer to an input element and copy the text from that input element. In my use case, the text needs to be dynamically created and then added to clipboard.

Can I use ngx-clipboard to copy to clipboard or is there be another package that would enable me to achieve this?

kayasa
  • 2,055
  • 8
  • 37
  • 63

6 Answers6

20

User interaction is mandatory for executing document.execCommand, which is used for copying text to the clipboard.

See my other answer.

If you don't want to use any third party library, you could use below snippet for copying text to the clipboard.

function copyTextToClipboard(text) {
  var txtArea = document.createElement("textarea");
  txtArea.id = 'txt';
  txtArea.style.position = 'fixed';
  txtArea.style.top = '0';
  txtArea.style.left = '0';
  txtArea.style.opacity = '0';
  txtArea.value = text;
  document.body.appendChild(txtArea);
  txtArea.select();

  try {
    var successful = document.execCommand('copy');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Copying text command was ' + msg);
    if (successful) {
      return true;
    }
  } catch (err) {
    console.log('Oops, unable to copy');
  } finally {
    document.body.removeChild(txtArea);
  }
  return false;
}

Change copyToClipboard function as below to call the copyTextToClipboard function

copyToClipboard() {
    this.textToCopy = this.text1 + this.text2 + this.text3;
    var result = this.copyTextToClipboard(this.textToCopy);
    if (result) {
        this.toastr.info('Copied to Clipboard');
    }
}
Gangadhar Jannu
  • 4,136
  • 6
  • 29
  • 49
  • I don't have a textarea that I need to get text from. Text will come from a service, Once user clicks on the 'copy' icon, a call is made to a service, which would generate the text and then this text needs to be copied to clipboard. – kayasa Aug 19 '17 at 12:29
  • It will create the textarea on the fly. You don't need to have it. All you need to do is change the `copyToClipboard` function as mentioned in the answer – Gangadhar Jannu Aug 19 '17 at 12:31
  • @kayasa You would not be able to copy text into clipboard in async call. Either you should have text available or call should be synchronous. – Vip Jan 09 '18 at 14:12
  • 2
    This solution works Good. He is creating a dom and the unsing select, the issue with angular is that select method is not found by the compiler in typescript, but in this case he is creating a text area element and attached the text to copy, in this way the select() method is able. Thanks ! – Rafael Paredes Apr 03 '18 at 17:02
  • @GangadharJannu do you remove the textarea only after a successful copy operation? Does it stay in the DOM, if copy operation fails? – Halil Apr 28 '18 at 15:26
  • @Halil No! `textarea` will be removed and it is not dependent on `copy` operation result. Modified my answer for clarity – Gangadhar Jannu Apr 29 '18 at 18:59
  • @GangadharJannu Actually, it isn't removed in successful copy operations. I updated your answer accordingly. – Halil May 02 '18 at 20:11
16

You need to use ngxClipboard directive with your image. This is how you need to use it to solve your issue:

<td>
    <img src='./assets/Copy.gif' (click)="copyToClipboard()" ngxClipboard [cbContent]="textToCopy" />
</td> 

Remember to add ClipboardModule in your app module. Example code below:

import { ClipboardModule } from 'ngx-clipboard';

@NgModule({
  imports: [
    // Other Imports
    ClipboardModule
  ],
  // Other code
})
export class AppModule { }
FAISAL
  • 33,618
  • 10
  • 97
  • 105
  • Thank you. This gives me an error message saying cbContent is not a known property of img - "Can't bind to 'cbContent' since it isn't a known property of 'img'. (" " – kayasa Aug 19 '17 at 10:51
  • You have to include `FormsModule` and `ClipboardModule` in your AppModule imports. Then it will work – FAISAL Aug 19 '17 at 11:32
  • Yes, it does take care of the error. However, there is one problem. It does not consider the text updated in the copyToClipboard() function but takes the value of textToCopy which was before the function was called. textToCopy: string = 'Hello'; copyToClipboard() { this.textToCopy = this.textToCopy + '! How are you!'; } The text that will be copied is 'Hello' and not 'Hello! How are you!'. Is there a way to copy the updated text? – kayasa Aug 19 '17 at 11:58
  • ngxClipboard works great - and the most recent version exposes a `(cbOnSuccess)` event that you can hook to instead of using the `click` event. That should solve the idiosyncratic issue mentioned above using `click` to capture. – Scott Byers Jan 19 '18 at 21:05
  • Always Error: Target should be input or textarea at ClipboardService.push../node_modules/ngx-clipboard/dist/src/clipboard.service.js.ClipboardService.isTargetValid – Anton Pegov May 14 '18 at 10:53
15

This is the easiest way to copy to clipboard.

In your template

<button (click)="copyToClipboard(sharableLink)">Copy link</button>
<input type="text" value="This is the value to copy" #sharableLink>

In component

copyToClipboard(element) {
    element.select();
    document.execCommand('copy');
    this.toaster('success', 'Success!', 'Link copied to clipboard.');
  }
Zahidul Islam Ruhel
  • 1,114
  • 7
  • 17
9

Here another quick and dirty option without the need of third-party libraries or modules. Taken from here

In your template

<a class="accent" (click)="copyLink(textToCopy)">{{textToCopy}}</a>

And in your component

copyLink(text:string) {
        const event = (e: ClipboardEvent) => {
            e.clipboardData.setData('text/plain', text);
            e.preventDefault();
            // ...('copy', e), as event is outside scope
            document.removeEventListener('copy', e);
        }
        document.addEventListener('copy', event);
        document.execCommand('copy');
    }
Gabriel Balsa Cantú
  • 1,954
  • 1
  • 14
  • 11
velval
  • 3,072
  • 36
  • 45
3

ngx-clipboard now doesn't require you to use input element. Now it's more straight forward and provide several ways of doing this. One way to do is simply use ClipboardService. From three, documentation

import { ClipboardService } from 'ngx-clipboard'

constructor(private _clipboardService: ClipboardService){
...
}
copy(text: string){
  this._clipboardService.copyFromContent(text)
}

But in my case this didn't work. And I got some warnings at the compile time in angular that peer dependencies are not met. Since I was using Angular 4 these warning I expected. But there's a simple way to do this with @ViewChild if the above solution doesn't work for you.

in your html :

<textarea name="copyText" #copyText id="" style="opacity: 0;height: 0;"></textarea>

And in Component :

@ViewChild('copyText', { read: ElementRef }) copyText: ElementRef;

copyText() {
    const element = this.copyText.nativeElement;
    element.value = 'some text';
    element.focus();
    element.select();
    document.execCommand('copy');
}

This is just simple vanilla javascript approach with Angular's @ViewChild

dilantha111
  • 1,388
  • 1
  • 17
  • 19
0

The problem (later highlighted by the OP) of the approved answer, using NgxClipboard, is that the content could not be set dynamically.

Using a (click) event listener doesn't work, because it is triggered after ngxClipboard execution.

So, simply define [cbContent] with an @Input getter, and forget about the (click) event:

In the template:

<button ngxClipboard [cbContent]="foo">Click me</button>

In the component:

@Input()
get foo() {
    // Dynamic generation of the text to put in the clipboard:
    return this.text1 + this.text2 + this.text3
}
Philo
  • 169
  • 2
  • 4