CopyToClipboard Directive mit AngularJs, TypeScript und ControllerAs Syntax


Bisher war Flash notwendig um auf einer Webseite einen Button anzubieten, der einen Text in die Zwischenablage kopiert. Wenn man aber das Glück hat und bei seiner Anwendung nur die neuesten Browser unterstützen muss, kann man jetzt auch auf eine native JavaScript Funktion zurückgreifen.

$("#inputText").select();
document.execCommand("copy");

Denn man kann mit diesen zwei Zeilen Code ab Chrome:42+, FF:41+, IE9+ und Opera29+ (Safari:nicht unterstützt) den Text z.B. in einer Textbox auswählen, den man dann in die Zwischenablage kopieren möchte.

Meine Direktive kann folgendermaßen verwendet werden in einem “div” oder jedem beliebigen anderen Element:

<div sq-copy-to-clipboard ng-model="ctrl.viewModel.Name" sq-call-back-fct="ctrl.DoSomething" sq-icon-css="'fa fa-fw fa-copy'" sq-btn-css="'btn btn-default btn-xs'" sq-title="'In die Zwischenablage'"></div>

In der AngularJS Direktive “sqCopyToClipboard” möchte ich aber nur einen Button anbieten der ein ngModel Element enthält und dessen Wert in der Zwischenablage ablegt. Damit dies funktioniert, wird mit Hilfe von jQuery jedes mal wenn der Button zum Kopieren des ngModel Wertes geklickt wird, ein Temporäres HTML Textfeld erstellt was nur 1x1px groß ist und am Ende des DOMs angehängt wird. Dann wird der Text darin markiert und kopiert und danach wird das Element auch gleich wieder aus dem DOM entfernt. Das Element muss 1x1px groß sein, damit Chrome den Text darin auch markieren kann. Denn bei 0x0px größe kann Chrome keinen Text markieren.

In der Direktive selbst verwende ich die “controllerAs” Syntax, die mit AngularJs 1.2 eingeführt wurde und die neueste Version von “bindToController”, die seit AngularJs 1.4 existiert. Der $scope wird damit nicht mehr benötigt und muss auch nicht mehr in der Controllerfunktion injected werden. Denn auch in Direktiven kann jetzt genau wie beim Controller die “ControllerAs” Syntax verwendet werden. Damit kann man im Template der Direktive über den “ControllerAs” Namen z.B. “sqCopyPasteCtrl” auf die Variablen zugreifen die man in “bindToController” definiert hat und auch auf alle weiteren Variablen und Funktionen die man in der Controllerfunktion über “this” zur Verfügung stellt. Zur genauen Funktionsweise von “controllerAs” kann ich nur den Blogeintrag “Exploring Angular 1.3 Binding to Directive Cotnrollers” empfehlen.

HTML Template meiner Directive Link “a” mit Icon Class:

<a ng-click="sqCopyPasteCtrl.copyToClipboard()" ng-class="sqCopyPasteCtrl.sqBtnCss" title="{{sqCopyPasteCtrl.sqTitle}}">
<i ng-class="sqCopyPasteCtrl.sqIconCss"></i></a>

Die Direktive selbst in TypeScript verfasst.

module App.Directives {
    /* 
    * Definition der Scope Variablen
     */
    export interface ICopyToClipboardScope {
        sqValues : string;
        sqTitle : string;
        sqIconCss : string;
        sqBtnCss: string;
        sqCallBackFct;
    }

    /*
     * Directive, die den String im ngModel in die Zwischenablage kopiert. 
     * https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
     * ACHTUNG geht nur ab Chrome:42+, FF:41+, IE:9+, Opera:29+, Safari:Not Supported
     *
     * Verwendung: 
     *</pre>
<div></div>
<pre>
     */
    export class CopyToClipboard implements ng.IDirective {
        public restrict: string = "A";
        public replace: boolean = true;
        public require = "ngModel";
        public templateUrl: string = 'ScriptsApp/directives/templates/copyToClipboard.directives.html';
        public scope = {}

        public controller = CopyToClipboardCtrl;
        public controllerAs = "sqCopyPasteCtrl";
        public bindToController: ICopyToClipboardScope = {
            sqValues: "=ngModel",    //Der Wert der in die Zwischenablage kopiert werden soll.
            sqTitle: "=",            //Der Titel der angezeigt werden soll Optional
            sqIconCss: "=",          //CSS für das Icon
            sqBtnCss: "=",           //CSS für den Button
            sqCallBackFct: "&"        //Callback Funktion die ausgeführt wird, wenn diese gesetzt wurde.
        }

        constructor() { }

        //#region Angular Module Definition
        private static _module: ng.IModule;
        /**
        * Stellt die Angular Module für CopyToClipboard bereit.
        */
        public static get module(): ng.IModule {
            if (this._module) {
                return this._module;
            }

            //Hier die abhängigen Module für unsere Direktive definieren.
            this._module = angular.module('copyToClipboard.directives', [CopyToClipBoardServiceProvider.module.name]);
            this._module.directive('sqCopyToClipboard', [() => { return new CopyToClipboard(); }]);
            return this._module;
        }
        //#endregion
    }

    /*
    * Implementierung unseres CopyToClipboard Controllers.
    */
    export class CopyToClipboardCtrl implements ICopyToClipboardScope {
        public sqValues: any;
        public sqTitle: string;
        public sqIconCss: string;
        public sqBtnCss: string;
        public sqCallBackFct;

        static $inject = ['copyToClipBoardConfig'];

        /* 
        * Da wir die CSS Klassen für einen Provider setzen, hier den passenden Provider injecten und im Template dann auf dessen Config Werte zugreifen.
        */
        constructor(private copyToClipBoardConfig: ICopyToClipBoardServiceProvider) {
            this.init();
        }

        init(): void {
            //Prüfen ob ein Titel oder andere CSS Klasse übergeben wurde, sonst den Wert aus dem Provider verwenden
            if (this.sqTitle === undefined) {
                this.sqTitle = this.copyToClipBoardConfig.config.title;
            }

            if (this.sqBtnCss === undefined) {
                this.sqBtnCss = this.copyToClipBoardConfig.config.btnCssClass;
            }

            if (this.sqIconCss === undefined) {
                this.sqIconCss = this.copyToClipBoardConfig.config.iconCssClass;
            }
        }

        /*
        * Unseren ModelValue in die Zwischenablage kopieren.
        */
        public copyToClipboard(): void {
            var inputId: string = "sqCopyToClipboardText";
            
            //Unser Input erstellen inkl. des Textes den wir kopieren wollen, da die Angular Implementierung auf "this.sqValues" zugreift ist dies 
            //durch die Extra Definition des CopyToClipboard Controllers problemlos möglich. Das Input selbst ist Mindestens 1px breit und hoch, denn
            //sonst kann der Inhalt im Chrome nicht markiert werden, was zwingend notwendig ist damit der Inhalt kopiert werden kann.
            var input = $(``);

            try {
                //Unser Input dem DOM Hinzufügen
                $(input).appendTo(document.body);
                //Den Inhalt des Inputs auswählen, denn der execCommand kopiert nur die Inhalte in die Zwischenablage, die ausgewählt sind.
                $(`#${inputId}`, document.body).select();
                document.execCommand("copy");
            } finally {
                //Am Ende das Eingabefeld wieder aus dem DOM entfernen
                $(`#${inputId}`, document.body).remove();

                //Sollte eine CallBack Funktion angegeben sein, diese hier ausführen.
                if (this.sqCallBackFct !== undefined) {
                    this.sqCallBackFct();
                }
            }
        }
    }

    /*
     * Configklasse für unsere CopyPaste Direktive. Hier kann man z.B. die passenden Css Klassen ändern die gesetzt werden.
     */
    export class CopyToClipBoardServiceProvider implements ng.IServiceProvider, ICopyToClipBoardServiceProvider {
        //Die Konfigurationsdaten entsprechend setzen.
        config: ICopyToClipBoardConfig = {
            btnCssClass: "btn btn-default btn-sm",  //Bootstrap
            iconCssClass: "fa fa-fw fa-copy",       //Font Awesome
            title: "In die Zwischenablage kopieren"
        }

        $get = () => {
            return {
                config: this.config
            }
        };


        //#region Angular Module Definition
        private static _module: ng.IModule;
        /**
        * Stellt die Angular Module für CopyToClipboardProvider bereit.
        */
        public static get module(): ng.IModule {
            if (this._module) {
                return this._module;
            }

            //Hier die abhängigen Module für unsere Direktive definieren.
            this._module = angular.module('copyToClipBoardConfig.provider', []);
            this.module.provider("copyToClipBoardConfig", () => new CopyToClipBoardServiceProvider());
            return this._module;
        }
        //#endregion

    }

    export interface ICopyToClipBoardServiceProvider {
        config: ICopyToClipBoardConfig;
    }

    export interface ICopyToClipBoardConfig {
        btnCssClass: string;
        iconCssClass: string;
        title: string;
    }
}

Den Aktuellen Quellcode findet Ihr auch auf meinem GitHub Account.

Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s