Created
March 9, 2017 13:15
-
-
Save bennadel/b934b937576418661e8a0c75023283ef to your computer and use it in GitHub Desktop.
Creating A Simple Copy-To-Clipboard Directive In Angular 2.4.9
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 ); | |
} | |
) | |
; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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