如何在 Angular 2 Typescript 中复制到剪贴板?

How do I copy to clipboard in Angular 2 Typescript?

有没有办法在 Angular2 Typescript 框架中复制剪贴板(多浏览器)中的文本?

我只找到使用 Javascript 的资源,例如

document.execCommand('copy')

目前只实现了最常见的APIs抽象,主要是为了能够在运行在服务器(服务器端渲染(https://github.com/angular/universal)中传递不同的实现在 API 不可用的网络工作者中。

我很确定剪贴板中还没有任何内容 API。不过,我们计划实施更多包装器。

您可以围绕 clipboard.js 库实施 Angular2 指令。

首先将库配置到 SystemJS 中:

<script>
  System.config({
    map: {
      clipboard: 'https://cdn.rawgit.com/zenorocha/clipboard.js/master/dist/clipboard.js'
    },
    packages: {
      'app': {
        defaultExtension: 'js'
      }
    } 
  });
  (...)
</script>

我们希望能够通过指令将剪贴板附加到元素上,并提供我们想要 link 的 DOM 元素作为参数。指定到指定目标元素中的值将用于复制其文本。这是一个使用示例:

<div>
  <input #foo/>
  <button [clipboard]="foo">Copy</button>
</div>

指令的实现如下:

import {Directive,ElementRef,Input,Output,EventEmitter} from 'angular2/core';
import Clipboard from 'clipboard';

@Directive({
  selector: '[clipboard]'
})
export class ClipboardDirective {
  clipboard: Clipboard;

  @Input('clipboard')
  elt:ElementRef;

  @Output()
  clipboardSuccess:EventEmitter<any> = new EventEmitter();

  @Output()
  clipboardError:EventEmitter<any> = new EventEmitter();

  constructor(private eltRef:ElementRef) {
  }

  ngOnInit() {
    this.clipboard = new Clipboard(this.eltRef.nativeElement, {
      target: () => {
        return this.elt;
      }
    });

    this.clipboard.on('success', (e) => {
      this.clipboardSuccess.emit();
    });

    this.clipboard.on('error', (e) => {
      this.clipboardError.emit();
    });
  }

  ngOnDestroy() {
    if (this.clipboard) {
      this.clipboard.destroy();
    }
  }
}

查看此 plunkr 示例:https://plnkr.co/edit/elyMcP5PX3UP4RkRQUG8?p=preview

感谢@ThierryTemplier,

根据他的回答,我在 github & npm 上整理了一个指令和分享。

这是github

上的项目

更新:2017 年 4 月 30 日

这个库不再依赖于 clipboard.js。

就Angular!

快速示例(组件代码):

import { ClipboardService } from 'ngx-clipboard'

...

constructor(private _clipboardService: ClipboardService){
...
}

// not sure, but this should the result of user interaction (e.g. (click) )
copyToClipboard(){
  const text = computeText();
  this._clipboardService.copyFromContent(text)
}

我只从 https://github.com/pehu71/copy-component/blob/master/src/simple/copy.component.ts 得到了一种方法 甚至适用于 android 4.1.2

copy(val) {

    let selBox = document.createElement('textarea');

    selBox.style.position = 'fixed';
    selBox.style.left = '0';
    selBox.style.top = '0';
    selBox.style.opacity = '0';
    selBox.value = val;

    document.body.appendChild(selBox);
    selBox.focus();
    selBox.select();

    document.execCommand('copy');
    document.body.removeChild(selBox);
}

Ben Nadel 有一个很好的例子,它适用于任何 html 元素类型,并且不依赖于任何要安装的东西。参见 Ben's blog post 或者查看 Git gist

查看他的博客了解更多信息以及他所做的日志记录,这里是相关的并稍作修改,因此更适合这里:

制定指令:clipboard.directive.ts

// Import the core angular services.
import { Directive } from "@angular/core";
import { EventEmitter } from "@angular/core";

// Import the application components and services.
import { ClipboardService } from "./clipboard.service";

// This directive acts as a simple glue layer between the given [clipboard] property
// and the underlying ClipboardService. Upon the (click) event, the [clipboard] value
// will be copied to the ClipboardService and a (clipboardCopy) event will be emitted.
@Directive({
selector: "[clipboard]",
inputs: [ "value: clipboard" ],
outputs: [
    "copyEvent: clipboardCopy",
    "errorEvent: clipboardError"
],
host: {
    "(click)": "copyToClipboard()"
}
})
export class ClipboardDirective {

public copyEvent: EventEmitter<string>;
public errorEvent: EventEmitter<Error>;
public value: string;

private clipboardService: ClipboardService;


// I initialize the clipboard directive.
constructor( clipboardService: ClipboardService ) {

    this.clipboardService = clipboardService;
    this.copyEvent = new EventEmitter();
    this.errorEvent = new EventEmitter();
    this.value = "";
}

// ---
// PUBLIC METODS.
// ---

// I copy the value-input to the Clipboard. Emits success or error event.
public copyToClipboard() : void {
    this.clipboardService
        .copy( this.value )
        .then(
            ( value: string ) : void => {

                this.copyEvent.emit( value );

            }
        )
        .catch(
            ( error: Error ) : void => {

                this.errorEvent.emit( error );
            }
        )
    ;
}
}

还有一项服务clipboard.service.ts

// Import the core angular services.
import { DOCUMENT } from "@angular/platform-browser";
import { Inject } from "@angular/core";
import { Injectable } from "@angular/core";
@Injectable()
export class ClipboardService {

private dom: Document;
// I initialize the Clipboard service.
// --
// CAUTION: This service is tightly couped to the browser DOM (Document Object Model).
// But, by injecting the "document" reference rather than trying to reference it
// globally, we can at least pretend that we are trying to lower the tight coupling.
constructor( @Inject( DOCUMENT ) dom: Document ) {
    this.dom = dom;
}

// ---
// PUBLIC METHODS.
// ---
// I copy the given value to the user's system clipboard. Returns a promise that
// resolves to the given value on success or rejects with the raised Error.
public copy( value: string ) : Promise<string> {
    var promise = new Promise(
        ( resolve, reject ) : void => {
            var textarea = null;
            try {
                // In order to execute the "Copy" command, we actually have to have
                // a "selection" in the currently rendered document. As such, we're
                // going to inject a Textarea element and .select() it in order to
                // force a selection.
                // --
                // NOTE: This Textarea is being rendered off-screen.
                textarea = this.dom.createElement( "textarea" );
                textarea.style.height = "0px";
                textarea.style.left = "-100px";
                textarea.style.opacity = "0";
                textarea.style.position = "fixed";
                textarea.style.top = "-100px";
                textarea.style.width = "0px";
                this.dom.body.appendChild( textarea );

                // Set and select the value (creating an active Selection range).
                textarea.value = value;
                textarea.select();
                // Ask the browser to copy the current selection to the clipboard.
                this.dom.execCommand( "copy" );
                resolve( value );
            } finally {
                // Cleanup - remove the Textarea from the DOM if it was injected.
                if ( textarea && textarea.parentNode ) {

                    textarea.parentNode.removeChild( textarea );
                }
            }
        }
    );
    return( promise );
}
}

在 app.module.ts 中导入两者,然后您可以在 html 中引用它,如下所示:

<p>
        <button [clipboard]="value1.innerHTML.trim()">
            Copy Text
        </button>
        <span #value1>
            Hello World!
        </span>
    </p>

这是一个简单的 pure Angular2javascript 解决方案 不需要任何库 并且可以在 angular 组件中使用。如果需要,您可以将其变成一项服务或使其更通用,但这将确立基本思想。

目前浏览器只允许将文本从 Selection 中的 <input> 或 [=13] 复制到剪贴板=]

在组件中做这样的事情:

import {Inject} from "@angular/core";
import {DOCUMENT} from "@angular/platform-browser";

export class SomeComponent {
    private dom: Document;

    constructor(@Inject(DOCUMENT) dom: Document) {        
       this.dom = dom;
    }

    copyElementText(id) {
        var element = null; // Should be <textarea> or <input>
        try {
            element = this.dom.getElementById(id);
            element.select();
            this.dom.execCommand("copy");
        }
        finally {
           this.dom.getSelection().removeAllRanges;
        }
    }
}

然后在与组件关联的 html 块中,执行以下操作:

<div>
   <button (click)="copyElementText('elem1')">Copy</button>
</div>
<textarea id="elem1">Some text</textarea>

就是这样!该按钮在其组件中调用 copyElementText() 函数并将 html 元素的 ID 传递给它以从中获取文本并复制到剪贴板。

该函数使用标准 javascript 通过其 ID 获取元素,select 它,在 selection 上执行 "Copy" 命令,然后 deselect就这样了。

你提到的代码是正确的方法,它也可以在 Angular 2+ 中完成。

我不知道你具体需要做什么,但是如果你有一个输入和一个按钮:

(.html file)

<input id='inputId'></input>
<button (click)="copyToClipboard()'>click me</button>

那么您需要做的就是:

(.ts file)

public copyToClipboard(): void {
  const inputElement = document.getElementById('inputId');
  (<any>inputElement).select();
  document.execCommand('copy');
  inputElement.blur();
}

这是一个简单的代码,以防您的文本不在输入或文本区域内,而是 div 或任何其他 HTMLElement 并且您不想使用任何外部库:

window.getSelection().selectAllChildren(document.getElementById('yourID'));
document.execCommand("copy");

我无法使用 select() 命令,因为 Angular 无法识别它。希望这对某人有所帮助!

这是一种无需任何外部依赖或创建假元素即可实现此目的的方法,只需使用 Clipboard API:

import { DOCUMENT } from '@angular/common';
import { Directive, EventEmitter, HostListener, Inject, Input, Output } from '@angular/core';

@Directive({
  selector: '[myClipboard]'
})
export class ClipboardDirective {

  @Input() myClipboard: string;
  @Output() myClipboardSuccess = new EventEmitter<ClipboardEvent>();

  constructor(@Inject(DOCUMENT) private document: Document) {}

  @HostListener('click')
  onClick() {
    this.document.addEventListener('copy', this.handler);
    this.document.execCommand('copy');
  }

  private handler = (e: ClipboardEvent) => {
    e.clipboardData.setData('text/plain', this.myClipboard);
    e.preventDefault();
    this.myClipboardSuccess.emit(e);
    this.document.removeEventListener('copy', this.handler);
  }

}

Can I use Clipboard API?

这是一个简单的纯 Angular2 和 javascript 解决方案,不需要任何库,可以在 angular 组件中使用。如果需要,您可以将其变成一项服务或使其更通用,但这将确立基本思想。

目前浏览器只允许将文本从 或 中的选择复制到剪贴板。这可以在 div

中实现
(.html file)
<div id="inputId">Some texts</div>
<button (click)="copyToClipboard()'>click me</button>

//(.ts file)

public copyToClipboard(){
  var el = document.getElementById('inputId');
  el.setAttribute('contenteditable','true');
  el.focus();
  document.execCommand('selectAll');
  document.execCommand('copy');
  el.setAttribute('contenteditable','false');
  el.blur();
}