Как скопировать в буфер обмена в Angular 2 Typescript?

Есть ли способ скопировать текст в буфер обмена (multi-browser) в Angular2 Typescript framework?

Я нахожу только источники использования Javascript, например

document.execCommand('copy')

Ответ 1

Вы можете реализовать директиву Angular2, используя clipboard.js библиотеку.

Сначала настройте библиотеку в SystemJS:

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

Мы хотим иметь возможность прикреплять буфер обмена к элементу через директиву и предоставлять в качестве параметра элемент 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.

Ответ 2

Престижность @ThierryTemplier,

Основываясь на его ответе, я собрал директиву и разделил на github и npm.

Вот проект на github

ОБНОВЛЕНИЕ: 4/30/2017

Эта библиотека больше не зависит от clipboard.js.

Просто Angular!

Ответ 3

У меня есть только один метод из 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);
}

Ответ 4

В настоящее время реализованы только самые распространенные аббревиатуры API, в основном для передачи различных реализаций при запуске на сервере (рендеринг на стороне сервера (https://github.com/angular/universal) внутри веб-сайта, где API недоступен.

Я уверен, что еще нет ничего для API буфера обмена. Однако есть планы внедрить больше оберток.

Ответ 5

Это простой чистый Angular2 и javascript решение, в котором не требуется никаких библиотек и которые могут использоваться в angular. Вы можете превратить его в сервис или сделать его более общим, если это необходимо, но это установит основную идею.

В настоящее время браузеры разрешают копирование текста в буфер обмена из Выбор в <input> или <textarea>

В компоненте сделайте что-то вроде этого:

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 для получения текста и копирования в буфер обмена.

Функция использует стандартный javascript для получения элемента по его идентификатору, выбирает его, выполняет команду "Копировать" на выбор и затем отменяет его.

Ответ 6

Указанный вами код - это правильный способ сделать это, и это можно сделать и в 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();
}

Ответ 7

Вот простой код, если ваш текст не находится внутри ввода или textarea, но div или любой другой элемент HTMLElement:

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

Мне не удалось использовать команду select(), потому что она не была распознана Angular. Надеюсь, это поможет кому-то!

Ответ 8

Ben Nadel имел отличный пример, который работал для любого типа элемента html и не полагался на что-либо, что нужно установить. См. Сообщение блога Бена Или см. 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 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>