JavaScript-Framworks wie Ember.js erleichtern die tägliche Arbeit in vielerlei Hinsicht: UI-Elemente lassen sich in funktionale Komponenten eingliedern und an beliebig vielen Stellen wiederverwenden. Es werden Code-Redundanzen vermieden und somit auch die Kohäsion der gesamten Applikation verbessert.
Soweit so gut! Doch die Applikation wächst weiter und baut sich auf dieser Grundlage nach und nach ein Set aus allgemeinen Tools auf. Gerade einfache HTML-Elemente, wie erweiterte Inputfelder, oder gekapselte Third-Party-Elemente, wie Modals oder Popovers, ändern sich im Wesentlichen nur noch selten. Es entstehen also viele kleine, aber auch große Komponenten, die kaum weiterentwickelt werden und im wahrsten Sinn nur noch stillschweigend die Ordner füllen. Genau dann, kann es nicht falsch sein, darüber nachzudenken, wie hoch die Applikationszugehörigkeit der Elemente ist und ob diese nicht auch in anderen Projekten ihre Anwendung finden.
en die Komponenten aus der Applikation entkoppelt, in ein eigenständiges Projekt eingebettet und über einen Paket-Manager wiederaufgenommen. Doch ist der Aufwand dazu wirklich gerechtfertigt und inwiefern erleichtert oder erschwert es die Wartung der einzelnen Komponenten? Diese Frage möchte ich in diesem Artikel beantworten, indem ich Schritt für Schritt den Ablauf zum Auslagern einer Komponente als Ember CLI-Addon beschreibe.
Der Anwendungsfall
Selections für JavaScript gibt es wie Sand am Meer, doch viele werden nicht mehr weiterentwickelt und verstauben, sind zu jung und instabil oder die benötigte Anforderung ist für eine gewöhnliche open-source Entwicklung zu speziell. Dies alles sind Gründe, warum wir uns für eine eigene flexiblere Ember-Komponente entschieden haben. Als einzige Aufgabe soll sie einen validen Selection-DOM-Baum generieren und damit möglichst unabhängig von weiteren Bibliotheken sein. Durch das Erweitern der Komponente, kann auf der HTML-Basis eine Selection-Bibliothek passend zur Applikation verwendet werden. Somit hängt die Komponente nur noch von unserer Grundanforderung ab und dient als gutes Beispiel für ein projektunabhängiges Element zum Wiederverwenden.
Bevor es los geht sollte geprüft werden, welche Version von Ember CLI installiert ist. Die letzte stabile Version zum Zeitpunkt dieses Artikels liegt bei 2.5.0 und ich empfehle auch diese zu installieren.
npm install -g ember-cli
Da das Addon eine Art Toolbox mit unterschiedlichen Komponenten werden soll, nenne ich es av-ember. Dafür lege ich die typische Ordnerstruktur über Ember CLI mit folgendem Befehl an.
ember addon av-ember
Es werden folgenden wichtigen Daten und Ordnern erzeugt:
- app/ - wird mit dem Namensraum der Applikation vereint.
- addon/ - Teil des Namensraums vom Addon selbst.
- app/ - Namensraum der Applikation, die das Addon integriert.
- public/ - beinhaltet alle statische Dateien auf die von außen zugeriffen werden kann
- tests/ - Test-Umgebung mit Dummy-App und Hilfsmittel für Akzeptanztest.
- vendor/ - Aufbewahrungsort für externe Daten, wie Libs oder native JS-Files
- package.json – hier werden sämtliche Informationen über das Projekt hinterlegt
Addon-Modul aufbauen
Alle Ember Addons sind Module des Node Package Managers und haben somit viele Gemeinsamkeiten. Da die Konfigurationsmöglichkeiten die Rahmen dieses Artikels sprengen, verweise ich an dieser Stelle auf die Node-Dokumentation.
Für die interne Verwendung habe ich die package.json wie folgt angepasst:
{
"name": "av-ember",
"version": "0.0.0",
...
"repository": "http://git.avocado-se.local/scm/av/av-ember.git",
...
"author": "avocado software engineering GmbH",
"private": true,
"devDependencies": {
...
},
"keywords": [
"ember-addon",
"utilities",
"selection"
],
"dependencies": {
...
},
"ember-addon": {
"configPath": "tests/dummy/config"
}
}
Als nächstes erstelle ich die auszulagernde Komponente neu über die Ember CLI, als hätte ich gerade erst angefangen diese zu schreiben.
ember generate component av-select
Für gewöhnlich werden beim Generieren in der Applikation nur eine Controller und eine Template-Datei erzeugt. Beim Addon muss zuätzlich eine File im App-Ordner angelegt werden, die die Namensräume beider Module verbindet. Durch die Generate-Funktion müssen wir uns über die Import-Pfade keine Sorgen machen, da sie automatisch zugeordnet werden.
Die Files im Ordner addon und test fülle ich nun mit dem auszulagernden Code aus der Applikation. Dabei muss das Template in der Komponente explizit angegeben werden.
addon/components/av-select.js
import Ember from 'ember';
import layout from '../templates/components/av-select';
export default Ember.Component.extend({
layout, //Layout nicht überschreiben
//Hier kommt der eigene Code
});
** addon/templates/components/av-select.hbs**
<select>
... //Handlebar-Code
</select>
Im app Ordner wird die Schnittstelle zwischen Addon und Applikation definiert. Dazu brauchen wir eine Applikations-Komponente, die unserer Addon-Komponente importiert.
Das bedeutet die Komponente muss im Namespace der Applikation definiert sein, um so nach außen gegeben zu werden. Letztendlich ist also der Name dieser File zuständig, wie die Komponente innerhalb der Applikation referenziert wird.
app/components/av-select.js
export { default } from 'av-ember/components/av-select';
//Diese Datei stellt lediglich die Verbindung zwischen den Namensräumen von Applikation und Addon her
tests/integration/components/av-select-test.js
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('av-select', 'Integration | Component | av select', {
integration: true
});
//Weitere Testfunktionen für die Komponente
Als nächstes führen wir die Tests der Komponente aus, um sicher zu gehen, dass alles wie gewohnt funktioniert.
ember try:testall
Addon publizieren
Anschließend kann das Addon mit git und npm ins Repository geladen werden. Dazu sollte die Version-Nummer durch ein Version-Tag markiert werden. In unserem Fall möchten wir die Version auf 0.0.1 setzen.
npm version 0.0.1
git push origin master
git push origin –tags
Weitere Informationen zur Versionierung der Node Modules gibt es auf https://docs.npmjs.com/cli/version
Prinzipiell muss sich das Ember-Addon in den node_modules der Applikation befinden und in den dependencies der package.json enthalten sein, um über Ember CLI mit kompiliert und auch verwendet zu werden. Standartgemäß werden durch den Befehl npm install alle Packete aus dieser Konfigurationsdatei geladen. Es stellt sich daher die Frage wie wir über npm unser eigenes lokales, Repository ansprechen und die richtige Version beziehen. Denn sobald eine neue Version für das gegebene Tool herausgegeben wird, möchten wir das Update mit möglichst wenig Aufwand an alle Entwickler verteilen.
Wir verwenden den Zugriff auf unser lokales Git per SSH und die Version soll die zuvor getaggte 0.0.1 sein. Bei Node gibt es mehrere Möglichkeiten, Git-Abhängigkeiten per URL aufzulösen:
git://github.com/user/project.git#commit-ish
git+ssh://user@hostname:project.git#commit-ish
git+ssh://user@hostname/project.git#commit-ish
git+http://user@hostname/project/blah.git#commit-ish
git+https://user@hostname/project/blah.git#commit-ish
Für die Verwendung von SSH muss sowohl der Git-Client als auch der Git-Server mit den privaten bzw. öffentlichen SSH-Keys konfiguriert sein. Zum Teil gibt es Server die an dieser Stelle mit einer Passphrase nicht umgehen können. Weitere Informationen dazu gibt es im Git-Kochbuch unter "Generiere Deinen öffentlichen SSH-Schlüssel".
Installiere ich das Modul per npm und trage das Repository inklusive Versionsnummer direkt in die Development-Umgebung ein:
npm install git+ssh://git@git.avocado-se.local:7999/av/av-ember.git#v0.0.1 --save-dev
Danach sollte in der package.json folgender Eintrag hinzugefügt werden
...
"devDependencies": {
"av-ember" : "git+ssh://git@git.avocado-se.local:7999/av/av-ember.git#v0.0.1",
...
},
...
Ist der node_modules-Ordner gefüllt, kann die Applikation mit der Addon-Komponente verwendet werden. In unserem Fall erbt die filter-selection von der ausgelagerten selection-component. Hier muss das Naming noch angepasst werden.
import Ember from 'ember';
import AvSelect from 'myapplikation/components/av-select';
//... from myapplikation/components/<alter-name>';
export default AvSelect.extend({
...
});
Fazit
Das Auslagern von Ember-Elementen ist kein großer Aufwand. Gerade dann wenn es in ein bereits vorhandenes Addon-Projekt eingelagert wird, sollte dieser Prozess in nur wenigen Minuten passiert sein. Durch die homogene Addon-Umgebung bringt sowohl die Wartung also auch die Testbarkeit einige Vorteile mit sich. Wird beispielweiße eine neue Ember-Version veröffentlicht, können einzelne Grund-Elemente unabhängig getestet und in Upgrade-Versionen eingegliedert werden. Projekte die also einer früheren Ember-Version angehören, beziehen eben eine andere Version, wie Projekte auf dem aktuellen Stand.
Die Schwierigkeit liegt eher in der Konzeption der Komponenten. Der Grat zwischen einer sehr spezifischen und sehr abstrakten Lösung ist recht groß und der Overhead, der durch eine Zerstückelung passiert, ist nicht immer gerechtfertigt.