Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created March 9, 2017 13:15
Show Gist options
  • Save bennadel/b934b937576418661e8a0c75023283ef to your computer and use it in GitHub Desktop.
Save bennadel/b934b937576418661e8a0c75023283ef to your computer and use it in GitHub Desktop.
Creating A Simple Copy-To-Clipboard Directive In Angular 2.4.9
// Import the core angular services.
import { Component } from "@angular/core";
@Component({
moduleId: module.id,
selector: "my-app",
styleUrls: [ "./app.component.css" ],
template:
`
<p>
<button
[clipboard]="value1.innerHTML.trim()"
(clipboardCopy)="logSuccess( $event )"
(clipboardError)="logError( $event )">
Copy Text
</button>
<span #value1>
Hello World!
</span>
</p>
<p>
<button
[clipboard]="value2.innerHTML.trim()"
(clipboardCopy)="logSuccess( $event )"
(clipboardError)="logError( $event )">
Copy Text
</button>
<span #value2>
Rock on With Yer Bad Self!
</span>
</p>
<p>
<button
[clipboard]="value3.innerHTML.trim()"
(clipboardCopy)="logSuccess( $event )"
(clipboardError)="logError( $event )">
Copy Text
</button>
<span #value3>
Weeezing The Ju-uice!
</span>
</p>
<textarea
#tester
(click)="tester.select()"
placeholder="Test your Copy operation here..."
></textarea>
`
})
export class AppComponent {
// I initialize the app component.
constructor() {
// ...
}
// ---
// PUBLIC METODS.
// ---
// I log Clipboard "copy" errors.
public logError( error: Error ) : void {
console.group( "Clipboard Error" );
console.error( error );
console.groupEnd();
}
// I log Clipboard "copy" successes.
public logSuccess( value: string ) : void {
console.group( "Clipboard Success" );
console.log( value );
console.groupEnd();
}
}
// 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 );
}
)
;
}
}
// 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 );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment