Legacy WebApps mit AngularJS pimpen

56
Legacy Web-Apps mit AngularJS pimpen

description

Folien zu unserem Vortrag beim Java Forum Stuttgart 2014 Besuchen Sie uns unter http://www.thecodecampus.de Müssen Sie auch noch alte servergetriebene Java-Webanwendungen weiterentwickeln und wollen Ihre Kunden dabei den Genuss der Usability moderner Webseiten bieten? In unserem Talk beim JavaForum Stuttgart 2014 haben wir anhand von Codebeispielen und Erfahrungsberichten gezeigt wie man schrittweise AngularJS in bestehende Anwendungen integriert. Dieser agile Ansatz liefert schnelle Ergebnisse und reduziert die Kosten und Risiken im Vergleich zu einer kompletten Umstellung.

Transcript of Legacy WebApps mit AngularJS pimpen

Page 1: Legacy WebApps mit AngularJS pimpen

Legacy Web-Apps mit AngularJS pimpen

Page 2: Legacy WebApps mit AngularJS pimpen

Über uns• Jan Blankenhorn und Philipp Burgmer

• Software Developers

• w11k.com / thecodecampus.de —> Esslingen / Stuttgart

• Schulungen, Projekt-Kickoff

• Consulting, Softwareentwicklung

Page 3: Legacy WebApps mit AngularJS pimpen

Was ist dein Problem?

Page 4: Legacy WebApps mit AngularJS pimpen

Probleme

• Pflege und Weiterentwicklung alter Webanwendungen

• Kunden verwöhnt von modernen Anwendungen —> wollen ähnliche Features

• Entwickler genervt von alten Technologien

• Zunehmend schwieriger Entwickler für alte Technologien zu finden

Page 5: Legacy WebApps mit AngularJS pimpen

Beispiele Anforderungen

• Autovervollständigung bei Suche

• Schnelle Rückmeldung auf Eingaben / Validierung

• Schnellere Reaktionszeiten der Anwendung

• Website als Anwendung nicht als Website

Page 6: Legacy WebApps mit AngularJS pimpen

Lösungsansatz

• Neu Implementieren: Oft zu teuer und zu gefährlich

• Bleibt nur: alte und neue Technologien verbinden

Page 7: Legacy WebApps mit AngularJS pimpen

Architekturvergleich

Page 8: Legacy WebApps mit AngularJS pimpen

Klassische Java Webanwendungen (JSP / JSF)

!

• Rendering Template -> HTML geschieht auf dem Server

• Zustand jedes Benutzers liegt auf dem Server —> Skaliert schlecht

• Komplette Page Requests pro Interaktion

• JavaScript nur für kleine Aufgaben

Page 9: Legacy WebApps mit AngularJS pimpen

Browser

Servlet/Filter (Controller)

JSP Pages (View)

JavaBeans (Model)

DB

Server

Page 10: Legacy WebApps mit AngularJS pimpen

JavaScript Apps• Client hat den Zustand und UI-Logik

• Server hat keinen Zustand —> Bessere Skalierung

• Server liefert

• statische Ressourcen wie Templates und JavaScript Code

• Daten via REST/JSON

• Weniger Redundanz bei Übertragung

Page 11: Legacy WebApps mit AngularJS pimpen

Controller (JS) View (HTML)

Model (Json, Rest)

REST API

DB

Server

Client

Page 12: Legacy WebApps mit AngularJS pimpen

Szenarien

Nur Client

Client + Server

Rest Backend

Page 13: Legacy WebApps mit AngularJS pimpen

Nur ClientSzenario 1a

Page 14: Legacy WebApps mit AngularJS pimpen

Ausgangssituation

• Klassische Web-Anwendung z.B. mit Struts

• Keine Änderungen an Server Architektur möglich

• Server liefert weiterhin fertiges HTML

Page 15: Legacy WebApps mit AngularJS pimpen

Lösungsansatz

• Client per JavaScript erweitern

• Direkte Interaktion bieten

• HTML bzw. DOM nutzen

• Höheres Level als jQuery

Page 16: Legacy WebApps mit AngularJS pimpen

AngularJS

• JavaScript-Framework zur Entwicklung von Rich Browser Applikationen

• Bringt grundlegende UI Konzepte wie z.B. MVC in den Browser

• Erweitert HTML anstatt zu abstrahieren

• HTML kann nach den gegebenen Bedürfnissen erweitert werden

Page 17: Legacy WebApps mit AngularJS pimpen

AngularJS• Eigentlich für Single-Page-Anwendungen gedacht

• Leichtgewichtig, schnelle Initialisierung

• Kann ruhig bei jedem Page-Reload geladen werden

• JavaScript Code in Dateien auslagern -> Caching

• Auch auf Teil des DOM anwendbar

Page 18: Legacy WebApps mit AngularJS pimpen

Beispiel

• Formular Validierung mit AngularJS

• Server generiert HTML mit speziellen Attributen

• AngularJS verwendet Attribute zum Validieren

• Client zeigt Fehlermeldungen sofort an (mitgeliefert vom Server im HTML)

Page 19: Legacy WebApps mit AngularJS pimpen

<div ng-app>!! <form name="userForm" novalidate post="createUser.do">!! <label for="userForm.email">E-Mail:</label>!! <input type="email" id="userForm.email" ng-model="user.email" name="email" required>! <div ng-messages="userForm.email.$error">! <div ng-message="required">Please enter your email address</div>! <div ng-message="email">Please enter a valid email address</div>! </div>!!! <button type="submit" ng-disabled="userForm.$invalid"></button>!! </form>!</div>!

Page 20: Legacy WebApps mit AngularJS pimpen

Validatoren• Standard HTML Attribute

• min, max, required

• type mit email, date, time, number, url

• AngularJS Attribute

• ng-min-length und ng-max-length

• ng-pattern

• Eigene Validatoren per Attribut und JavaScript Code

Page 21: Legacy WebApps mit AngularJS pimpen

Ajax mit DWRSzenario 1b

Page 22: Legacy WebApps mit AngularJS pimpen

Ausgangssituation

• Klassische Web-Anwendung z.B. mit Struts

• Keine Änderungen an grundlegender Server Architektur möglich

Page 23: Legacy WebApps mit AngularJS pimpen

Lösungsansatz

• Server liefert weiterhin fertiges HTML

• Beliebige JavaScript Frameworks oder VanillaJS im Client

• Ajax Kommunikation mittels DWR

Page 24: Legacy WebApps mit AngularJS pimpen

DWR • Servlet, dass Ajax-Requests verarbeitet

• DWR erstellt JavaScript Stubs für Java Klassen und übernimmt Client - Server Kommunikation

• Eine Art “Remote Procedure Call”

• Spring / Guice / Struts Integration

• http://directwebremoting.org/dwr/index.html

Page 25: Legacy WebApps mit AngularJS pimpen

Beispiel• Dynamisches Anzeigen einer Liste

• Klassisch: jeweils ein voller Page Request nötig

• DWR Lösung:

1. AJAX Request zum Laden der Daten

2. JavaScript: Anzeige der Daten

Page 26: Legacy WebApps mit AngularJS pimpen

Konfigurieren

Auch per Annotations konfigurierbar

<dwr> <!-- Nur Klassen in <allow> werden konvertiert --> <allow> <!-- Definieren der Klasse die freigegeben werden soll Erstellt wird die Klasse von Struts --> <create creator="struts" javascript="AjaxService"> <!-- auflisten der Methoden --> <include method="getAllElements"/> </create> </allow> </dwr>

Page 27: Legacy WebApps mit AngularJS pimpen

Einbinden

<script .. src="/lib/static/dwr/2.0/engine_and_util.min.js"/> <script .. src="/dwr/interface/AjaxService.js"/>

Page 28: Legacy WebApps mit AngularJS pimpen

BenutzenWebbrowser Server

/** * Java Script Code **/ AjaxService.getAllElements(populateList); !function populateList(data) { //etwas mit den Daten machen console.log(data); }

public List<String> getAllElements() { return elements; }

Page 29: Legacy WebApps mit AngularJS pimpen

Client + ServerSzenario 2a

Page 30: Legacy WebApps mit AngularJS pimpen

Ausgangssituation

• Klassische Web-Anwendung z.B. mit Struts

• KEINE saubere Trennung zwischen Business-Logik und Web-Schnittstelle

• Änderungen am Server in begrenztem Umfang möglich

• Anwendung soll schrittweise erneuert werden

Page 31: Legacy WebApps mit AngularJS pimpen

Lösungsansatz

• Server liefert nur noch Daten und Templates (getrennt)

• State kann im Server bleiben

• AngularJS im Client setzt Daten und Templates zusammen

Page 32: Legacy WebApps mit AngularJS pimpen

Probleme

• Server ist eigentlich gedacht fertig gerenderte HTML-Seiten auszuliefern -> Hack ;)

• Java Daten müssen serialisiert werden

Page 33: Legacy WebApps mit AngularJS pimpen

Template & Daten

• Laden der Seite in 2 Requests aufteilen

• Normaler“ Struts Request liefert eine JSP Seite mit dem HTML Template und Code für AngularJS Anwendung

• AngularJS Anwendung lädt dann die Daten als JSON über zusätzliche Requests vom Server

Page 34: Legacy WebApps mit AngularJS pimpen

/* * Struts Action */ public ActionForward doExecute(...) throws Exception { final String acceptType = request.getHeader("Accept"); // Abfragen des Accept Types // 1. Call if (false == acceptType.startsWith("application/json")) { // JSP Seite zurückgeben. // Enthält die JavaScript Anwendung return mapping.findForward("template"); } // 2. Call else { // Daten erstellen und serialisieren final Object data = buildData(request, response, form); final String json = serializeData(data); request.setAttribute("jsonResponse", json); ! // antworten mit JSON return mapping.findForward("jsonResponse"); } }

Page 35: Legacy WebApps mit AngularJS pimpen

https://code.google.com/p/google-gson/

/* * Beispiel: Daten mittels GSON zu JSON sterilisieren */ private String serializeData(final Object data) { final GsonBuilder builder = new GsonBuilder(); builder.serializeNulls(); final Gson gson = builder.create(); ! final String json = gson.toJson(data); ! return json; }

Page 36: Legacy WebApps mit AngularJS pimpen

<!-- JsonResponse.jsp Minimale JSP Seite, in die das Json Eingebunden wird --> <%@ page contentType="application/json; charset=UTF-8" pageEncoding="UTF-8"%> ${jsonResponse}

Page 37: Legacy WebApps mit AngularJS pimpen

<!-- JSP Seite mit AngularJS Anwendung -->!<script type="text/javascript">!angular.module("List", []);!!angular.module('List').controller("ListCtrl", function ($scope) {!! $scope.data = [];!! $scope.search = function() {! var requestConfig = {! searchText: $scope.searchText! };!! // Aufruf der Server REST Schnittstelle! $http.get('http://localhost/showData.do', requestConfig).then(function (response) {! // Verarbeitung der Daten! $scope.data = response.data;! });! };!! $scope.search();!});!</script>

Page 38: Legacy WebApps mit AngularJS pimpen

<div ng-app="List" ng-controller="ListCtrl">! <div class="header">! <input type="text" ng-model="searchText" placeholder="Full Text Search">! <button ng-click="search()">Search</button>! </div> !! <table>! <thead>! <tr>! <th ng-click="sortDataBy('name')">Name</th>! <th ng-click="sortDataBy('description')">Description</th>! </tr>! </thead>! <tbody>! <tr ng-repeat="entry in data">! <td><a ng-href="{{entry.link}}">{{entry.name}}</a></td>! <td>{{entry.description}}</td>! </tr>! </tbody>! </table>!</div>

Page 39: Legacy WebApps mit AngularJS pimpen

Anwendungsfälle

• Listen-Ansicht mit Sortierung, Filtern und Volltextsuche

• Detail Ansicht mit dynamischem Nachladen von Daten (z.B. in Tabs oder Popups)

• Mini-Single-Page-App auf einer Unterseite (CRUD)

Page 40: Legacy WebApps mit AngularJS pimpen

Client + sauberer ServerSzenario 2b

Page 41: Legacy WebApps mit AngularJS pimpen

Ausgangssituation

• Klassische Web-Anwendung mit z.B. Struts

• Saubere Trennung zwischen Business-Logik und Web-Schnittstelle

• Änderungen am Server in begrenztem Umfang möglich

Page 42: Legacy WebApps mit AngularJS pimpen

Lösungsansätze

• Ersetzen der Web-Schnittstelle durch REST API

• Ausliefern der Templates als statisches HTML

• Client wird wieder mit AngularJS umgesetzt

Page 43: Legacy WebApps mit AngularJS pimpen

REST (Jersey)

https://jersey.java.net/

/** * wird unter dem Pfad "resource" bereitgestellt */ @Path("resource") public class Resource { /** * * Methode verarbeitet HTTP GET requests. * Das Resultat wird als "text/plain" gesendet */ @GET @Produces(MediaType.TEXT_PLAIN) public String getIt() { return "Got it!"; } }

Page 44: Legacy WebApps mit AngularJS pimpen

REST BackendSzenario 3

Page 45: Legacy WebApps mit AngularJS pimpen

Ausgangssituation

• Etwas modernere Web-Anwendung

• REST Backend

• Flex Client

Page 46: Legacy WebApps mit AngularJS pimpen

Lösungsansatz

• Client schrittweise portieren

• Flex Anwendung anpassen und in neues HTML Grundgerüst integrieren

• Flex nur bei Bedarf anzeigen

• Kommunikation Flex <-> JavaScript

Page 47: Legacy WebApps mit AngularJS pimpen

Probleme

• Flash —> display: none —> display: block —> neue Initialisierung

• JS Code ruft ActionScript zu früh auf

• ActionScript ruft JS Code auf —> global, kein Angular Kontext

Page 48: Legacy WebApps mit AngularJS pimpen

Lösung: w11k-flash

• Open Source

• Github: http://github.com/w11k/w11k-flash

• Blog Artikel mit ausführlicher Erklärung: http://blog.thecodecampus.de/migration-von-flex-zu-angularjs

Page 49: Legacy WebApps mit AngularJS pimpen

<div ng-controller="TestCtrl">! <div w11k-flash="flash.config"! w11k-select-visible="flash.visible"! >! </div>!</div>

Page 50: Legacy WebApps mit AngularJS pimpen

angular.module('app').controller('TestCtrl', function ($scope) {! $scope.flash = {! config: {! swfUrl: 'assets/test.swf',! callback: function (readyPromise) {! $scope.flash.ready = readyPromise;! }! }! };!! $scope.talkToMe = function (message) {! $scope.message = message;! $scope.response = 'Hello! My name is AngularJS.';! return $scope.response;! };!! $scope.talkToFlex = function () {! if (angular.isDefined($scope.flash.ready)) {! $scope.flash.ready.then(function (flash) {! $scope.message = 'Hello! My name is AngularJS. What is your name?';! $scope.response = flash.talkToMe($scope.message);! });! }! };!});!

Page 51: Legacy WebApps mit AngularJS pimpen

protected function application_creationCompleteHandler(event:FlexEvent) :void {! this.angularjs = AngularJSAdapter.getInstance();! ! // initialize flex application! this.currentState = defaultState.name;! ExternalInterface.addCallback("talkToMe", talkToMe);! ! this.angularjs.fireFlashReady();!}!!protected function talkToMe(message :String) :String {! this.message = message;! response = 'Hello! My name is Flex.';! return response;!}!!protected function talkToAngularJS() :void {! message = 'Hello! My name is Flex. What is your name?';! response = this.angularjs.call('talkToMe(message)', { message: message });!}

Page 52: Legacy WebApps mit AngularJS pimpen

Fazit

Page 53: Legacy WebApps mit AngularJS pimpen

Herausforderungen• Transaktionen, da asynchrone Kommunikation

• Sicherheit: Client vs. Server

• Validierung: Client + Server

• Integration in bestehendes Build System

• Sehr viele Technologien = Polyglott

Page 54: Legacy WebApps mit AngularJS pimpen

Aber

• Macht Spaß

• Meist schnell und recht einfach möglich

• Produktivitätsschub für neue Features

• Generiert beim Kunden Lust auf mehr

Page 55: Legacy WebApps mit AngularJS pimpen

Legacy Web-Apps mit AngularJS pimpen