angular 1 tips from the...

18
Novatrox Group ANGULAR 1 TIPS FROM THE TRENCHES Chris Klug

Upload: others

Post on 22-Jan-2020

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Novatrox Group

ANGULAR 1

TIPS FROM THE TRENCHES

Chris Klug

Page 2: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

2

Innehållsförteckning Introduktion ................................................................................................................................. 3

Undvik Angular ............................................................................................................................. 4

In-line är av ondo.......................................................................................................................... 4

MyController.js ..................................................................................................................... 5

Module.js ............................................................................................................................. 5

Undvik $scope så mycket som bara möjligt .................................................................................... 5

Använd pub/sub istället för $broadcast och $emit .......................................................................... 6

Undvik $scope i controllers genom att flytta beroendet en nivå ...................................................... 7

MyController.js ..................................................................................................................... 8

MyControllerDirective.js ........................................................................................................ 8

Module.js ............................................................................................................................. 9

Index.html ............................................................................................................................ 9

Dölj $http, och skapa en fasad som är lättare att arbeta med .......................................................... 9

Använd Providers and Configuration istället för inbyggda antaganden ........................................... 12

Använd TypeScript ...................................................................................................................... 13

JavaScript moduler ..................................................................................................................... 14

App.js ................................................................................................................................. 15

Index.htm ........................................................................................................................... 15

Om författaren ........................................................................................................................... 16

Page 3: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

3

Angular 1 – Tips from the trenches

Introduktion Angular är ett av världens mest användas SPA ramverk, och trots dess fel och brister så har det plockats

upp av en stor del av web-utvecklings-communityn, och använts för att bygga oändligt många stora

och små lösningar. Det är ett ramverk som kommer med det mesta man behöver i lådan, och som gör

utvecklarna produktiva snabbt.

Angular är även ett lätt ramverk att lära sig att arbeta med. Det finns massor av bra, och dåliga,

tutorials på nätet som visar hur man kan komma igång med Angular. Problemet är dock just det, de

visar hur man kommer igång, och ibland till och med hur man kommer igång på fel sätt. Vill man gå

vidare efter det är resurserna färre, och de tenderar att fokusera på att visa upp en, eller möjligen två,

saker som man bör tänka på. Men i mångt och mycket måste man bli ordentligt insatt innan man kan

hitta bra information om hur man utvecklar Angular applikationer som är robusta, möjliga att bygga

vidare på och underhållsbara.

I det här dokumentet har vi på Novatrox samlat tips, idéer och tankar kring Angular-utveckling som vi

samlat på oss under de senaste åren. Alla med fokuset att bygga bättre applikationer. Vi undviker dock

gärna att kalla dem för ”Best Practices” eftersom de flesta saker tenderar att vara bra under vissa

omständigheter, men inte andra. Och ”Best Practices Under Some Circumstances” låter lite

omständligt

Page 4: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

4

Undvik Angular En grundpelare i alla de tipsen och tankarna du kommer hitta i detta dokument är att försöka undvika

Angular i så hög grad som möjligt. Då menar jag inte att man ska välja ett annat ramverk, utan att man

ska försöka bygga sina egna delar med så lite kopplingar som möjligt till Angular.

Genom att bygga komponenter som inte är beroende av Angular i sig gör det lättare att göra en hel

del saker. Det blir bland annat lättare att testa koden. Det blir lättare att byta ut Angular i framtiden

om det skulle behövas. Och det blir lättare för någon som inte kan Angulars lite mer komplexa delar

att sätta sig in i koden, och kanske till och med hjälpa till med utvecklingen. Så det finns många

anledningar att bygga sina komponenter Angular-agnostiska så långt det går.

Att bygga komponenter som är Angular-agnostiska innebär dock inte att vi ska ignorera Angular. Det

innebär bara att de delar som är beroende av Angular blir mindre, och agerar mer som ett ”lim” mellan

den agnostiska koden, och den som ”känner till” Angular.

Det intressanta är dock att det behövs djupare kunskap om Angular för att bygga agnostiska

komponenter, än det behövs för att bygga saker som är hårt kopplade till Angular.

Så med ”Undvik Angular” så menar jag att man ska undvika Angular där det går, och omfamna Angular

där det behövs.

In-line är av ondo Detta är något de flesta förstår relativt snabbt när man börjar med Angular, men det är ändå tyvärr så

att man in-line är vanligt förekommande när man tittar på tutorials på nätet.

Så vad menar jag då med in-line? In-line kod i Angular är när man bygger sina komponenter

(controllers, directives etc) som in-line funktioner. Ex:

angular.module(”MyModule”, [])

.controller(”MyController”, function($scope) {

$scope.greeting = ”Hello World”;

});

Den kursiva koden i detta exempel är en in-line funktion. Man skapar en funktion ”in-line” med resten

av koden.

Controllern i det här fallet registreras i Angular snabbt och enkelt, och man är igång och skapar nytta

omedelbart, vilket i sig är bra. Problemet är att man har byggt in sig i Angular helt och hållet. Det finns

inget sätt att komma åt den funktionen utan att ”bootstrappa” Angular och be Angular om en instans

av den funktionen. Detta gör bland annat koden mycket svårare att testa, svårare att läsa och ställer

dessutom en del extra krav på ordningen de olika JavaScript filerna läses in. Vill man till exempel ha

flera komponenter i samma modul, så måste man antingen registrera modulen först i en fil, och sen

Page 5: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

5

registrera komponenterna i andra filer som laddas in efteråt. Detta innebär att koden inte bara är hårt

knuten till Angular, den ställer även krav på kontextet som den laddas i genom att den är hårt knuten

till kod i andra filer.

En bättre lösning är att separera controller-koden från registreringen i Angular enligt:

MyController.js

function MyController($scope) {

$scope.greeting = ”Hello World”;

}

Module.js

angular.module(”MyModule”, [])

.controller(”MyController”, MyController);

Detta skapar fortfarande en koppling mellan filerna i form av att Module.js förväntar sig att

MyController.js redan laddats, men i gengäld så är nu MyController inte beroende av Angular i nån

högre utsträckning.

Även om MyController använder en variabel som heter $scope, så kan jag nu skapa upp en ny instans

och skicka in ett mockat Scope objekt om jag vill. I det här fallet skulle $scope kunna vara vilket

JavaScript objekt som helst.

Har vi sedan flera komponenter som skall registreras så kan de laddas in i vilken ordning som helst, så

länge Module.js laddas in sist, och ser till att registrera dem i Angular.

Undvik $scope så mycket som bara möjligt Sedan Angular 1.2 har man försökt pusha utvecklare att använda den så kallade ControllerAs syntaxen.

Detta innebär att man deklarerar sin controller med följande syntax

<... data-ng-controller=”MyController as ctrl”>{{ctrl.greeting}}</...>

Detta gör att Angular sätter upp Scope/Controller förhållandet lite annorlunda. Istället för att

instansiera controllern och ge den en referens till $scope, och sedan låta controllern sätta egenskaper

på Scope objektet, så skapar Angular istället en instans av controllern, och sätter sedan den instansen

som en egenskap på scopet. På detta vis skapar man databindningar mot den faktiska controllern, och

inte direkt mot scopet.

Genom att ange ”as ctrl” så säger man till Angular att man vill skapa upp en controller och sätta

egenskapen ctrl på scopet till den instansen. Sedan sätter man upp alla sina bindningar till att binda

mot den instansen istället för scopet.

Page 6: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

6

Scopet är fortfarande ansvarigt för den faktiska bindingen, men det märks mycket mindre. Controllern

kan nu ofta undvika att ta in $scope i de flesta fall, och på så vis blir mer eller mindre helt Angular

agnostisk.

Med exemplet ovan skulle nu controllern se ut så här istället

function MyController() {

this.greeting = ”Hello World”;

}

och databindingen skulle se ut så här

<div data-ng-controller=”MyController as

ctrl”><h1>{{ctrl.greeting}}</h1></div>

Detta gör det ännu enklare för oss att testa vår controller. Nu tar den ju inte ens ett $scope objekt

som vi behöver mocka. Och även om det är lätt att mocka det i det här fallet, så är det ännu lättare

att inte behöva göra nått alls. Dessutom kan man nu teoretiskt sätt använda controllern helt utan

Angular om man vill.

På alla platser där man definierar en controller i Angular så kan man nu ange ”controller as”, och man

bör definitivt göra det.

Använd pub/sub istället för $broadcast och $emit Om man har flera komponenter som behöver kommunicera med varandra i Angular, så är den

uppenbara lösningen att skicka meddelanden upp och ned i scope hierarkin med hjälp av $broadcast

och $emit. En uppenbar lösning som ofta leder till problem, eller fula lösningar.

Idén med att kommunicera mellan controllers är bra, och nödvändig i många situationer. Problemen

med att göra det med hjälp av $broadcast och $emit är tyvärr ett par.

Först och främst bygger det på att vi vet vilka scope som finns ovanför eller under det nuvarande

scopet. Detta gör att vi kopplar oss en del till hur vi kan sätta upp våra scopes, dvs vilka controllers och

directives som kan användas var.

Sen måste vi veta om vi vill skicka meddelandet uppåt eller neråt. Vi kan inte skicka till syskon, utan

bara till föräldrar eller till barn. Vill man publicera till syskon så måste man antingen skicka

meddelanden uppåt till en förälder med hjälp av $emit, för att sedan i föräldern skicka samma

meddelande nedåt till barnen med $broadscast. Något som kopplar oss ännu hårdare till ordningen

på scopen som används. Alternativt kan vi ta en referens till rot-scopet och använda det för att $emit:a

meddelanden till alla scope i hela vyn. Något som har en del uppenbara prestandaproblem.

Page 7: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

7

Till sist måste vi även ha en referens till $scope för att kunna använda $broadcast och $emit. Något

som jag precis skrev att vi skulle undvika så mycket som möjligt.

Lösningen är dock inte direkt komplicerad som tur är. Det lättaste sättet är att införskaffa ett pub/sub

ramverk och sedan kapsla in den funktionaliteten i en Angular service. Detta gör att våra komponenter

har ett beroende mot en komponent som vi själva skapat, och som bör vara lätt att mocka om det

behövs. Det innebär också att man kan skicka meddelanden åt alla håll och kanter på ett enkelt sätt.

Skicka till föräldrar? Inga problem. Barn? Inga problem! Syskon? Självklart!

Till sist innebär det också att vi har ännu ett verktyg vi kan använda för att slippa ta ett beroende mot

en Angular-feature.

I de flesta projekt hittills har jag kapslat in Postal.js i en enkel ”MessagingService” som erbjuder två

metoder, en ”subscribe” och en ”publish”. Subscribe returnerar ett objekt som kan användas att

”unsubscribea”. En modell som är identisk till den som Postal.js använder sig av, fast inkapslad i ett

par egna funktioner för att inte läcka Postal-specifik funktionalitet.

Undvik $scope i controllers genom att flytta beroendet en

nivå Det finns viss funktionalitet i $scope som vi ibland inte kan komma ifrån. Framförallt är det en fråga

om $watch. Vi är rätt ofta beroende av denna funktionalitet för att utföra det vi behöver, och det finns

tyvärr få bra sätt att bevaka värden utan $watch. Man kan använda getters och setters i vissa fall, men

i vissa fall räcker inte det.

$watch innebär ju dessutom att vi i de flesta fall behöver använda $on också för att kunna koppla bort

vår watch när scopet förstörs. Så oftast får vi kod som ser ut ungefär så här

function MyController($scope) {

var watch = $scope.$watch(“myProp”, function(newVal, oldVal) {

});

$scope.$on(“$destroy”, function() {

watch();

});

}

Den här koden gör uppenbart saker som behövs, men som kopplar controller till $scope och Angular.

Det är dessutom inte helt trivial kod som behöver skrivas för att mocka bort $scope i det här fallet om

vi vill kunna skriva tester. För att inte tala om att vi måste veta om hur $scope används för att mocka

rätt delar eftersom vi inte vill mocka hela objektet i onödan.

Page 8: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

8

En lösning på detta är att bygga controllern helt Angular agnostisk, vilket vi gärna vill, och sen skapa

ett directive som hanterar skapandet av controllern och hanteringen av de $scope specifika sakerna.

Ett directive har redan en hård koppling mot Angular, så det gör inget om vi tar beroenden på

$scope i ett directive. Så att använda detta för att slippa Angular beorendet i controllern är en bra

lösning.

Så hur fungerar det då?

Enkelt sett så skapar vi ett nytt directive för varje controller som behöver denna funktionalitet. Det

ser ut ungefär så här

MyController.js

function MyController() {

this.onValueChanged = function(newValue) {

// Handle change!

}

this.value = “”;

}

MyControllerDirective.js

function MyControllerDirective($injector) {

return {

scope: true,

link: function(scope, element, attrs, controller) {

var ctrl = $injector.instantiate(MyController, { $scope: scope

});

scope[attrs.myController] = ctrl;

var watch = scope.$watch(function() { return ctrl.value; },

function(newVal, oldVal) {

ctrl.onValueChanged(newVal);

});

scope.$on("$destroy", function() {

watch();

});

}

}

}

Page 9: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

9

Module.js

angular.module(“MyModule”, [])

.directive(“myController”, MyControllerDirective);

Index.html

<div data-my-controller=”ctrl”>

<input type="text" data-ng-model="ctrl.value" />

</div>

Ok, det är ju rätt mycket kod, men det mesta är rätt självförklarande hoppas jag. Man skapar en

controller som inte tar ett beroende på $scope för att bevaka en egenskap. Istället så exponerar man

en funktion för att hantera att något ändrats. I detta fallet onValueChanged(). Sedan skapar man ett

directive som har till uppgift att skapa upp controllern, sätta den som en egenskap på scope objektet

precis som med ”controller as”, och till sist hantera $watch och $scope, och anropa funktionen på

controllern när värdet ändras.

Till sist registrerar man bara directivet i Angular, eftersom det inte finns något behov för Angular att

ens känna till controllern eftersom den skapas upp av directivet.

Användning blir lite förändrad i form av att man inte använder ngController, istället använder man sitt

eget directive för att sätta upp controllern. Så istället för data-ng-controller=”...” så skriver man nått i

stil med data-my-controller=”ctrl”, beroende på vad för namn man registrerat sitt directive under.

Fördelen med detta är än en gång att koppla loss controllern från Angular. Det blir lite mer kod, men

och andra sidan så blir den intressanta koden, dvs, controllern, enklare att förstå och smidigare att

jobba med. Det är dessutom mer likt den modell som Angular 2 arbetar enligt med sina components.

Angular 1.5 har även introducerat components i Angular 1, vilket kan vara intressant om man ser en

potentiell flytt till 2:an i framtiden. Men det är av förklarliga skäl inte en exakt kopia av det som

kommer i 2:an, men är trots det intressant att överväga.

Dölj $http, och skapa en fasad som är lättare att arbeta med De flesta Angular-baserade applikationerna i världen anropar tjänster på nätet för att hämta, eller

lämna/uppdatera information. Det lättaste sättet är att helt enkelt bara ta in en instans av $http och

använda den för att göra anropen. Detta introducerar dock ett gäng intressanta problem.

Först och främst så kopplar vi oss en än gång hårt mot Angular genom att ta ett beroende på $http

hela vägen uppe i controllern.

Sen leder det tilll att vi sprider ut URL:er/paths i våra controllers. Något som kan verka som ett litet

problem, tills man behöver uppdatera några av dem och inte riktigt vet var de används nånstans.

Page 10: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

10

Det innebär också att det inte finns någon direkt central plats att lägga in felhantering. Det går att göra

detta genom inbyggd funtionalitet i Angular, men det innebär ju att vi bygger in en relativt central del

av vår applikation (felhantering och loggning) i Angular. Något som kanske inte är jättebra i alla fall.

Till sist är det även svårare att hantera autentisering när man sprider ut $http anropen på olika platser

i koden. I SPA-applikationer tenderar vi att använda bearer tokens för autentisering, vilket innebär att

alla anrop som görs måste få en autentiserings header satt innan man skickar iväg det. Att ha denna

logik, inkl logik för att hantera potentiell utloggning, utspridd i hela applikationen är inte speciellt

önskvärt.

Lösningen kring detta är att skapa ett, eller kanske två lager emellan controllern och $http.

Den enklaste lösningen, dvs ett lager, innebär att man kapslar in $http i en mer specifik service. På så

vis ligger alla anropen till en specifik endpoint på ett ställe, och likaså loggning, felhantering och

hantering av eventuell utloggning.

Till exempel skapar man en ProductService om man behöver produkt information från en tjänst.

ProductService:n kapslar sedan in själva anropet till servern med allt vad det innebär, och exponerar

istället en enkel funktion som förklarar för konsumenten vilka parametrar som behövs.

function ProductService($http) {

this.searchProduct = function(query) {

return $http.get("https://www.endpoint.com/products?q=" +

encodeURI(query))

.then(function(response) {

return response.data;

}, function(error) {

// Handle error

})

}

}

För controller innebär det att man istället för att behöva ha URLer och http konfiguration själv, så tar

man ett beroende på ProductService och får ett enkelt API att arbeta med, dvs färdiga funktioner för

det man kan göra.

function ProductController(productService) {

this.onSearchClick = function() {

productService.searchProduct(this.query)

.then(function(products) {

// Handle result

})

}

Page 11: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

11

this.query = “”;

}

Denna lösning har många fördelar jämfört med att ta in $http i controller. Men den har också en del

nackdelar jämfört med att introducera ett lager till.

Genom att bara ha ett lager som i den här lösningen, så sprider vi fortfarande ut felhantering, loggning,

autentisering etc i ett antal olika services. Något som är bättre att hantera på ett enda ställe.

Så, istället för att ProductService nivån tar ett beroende på $http, så introducerar man en egen

HttpService, vilken ProductService i sin tur tar ett beroende på.

HttpServicen erbjuder grundläggande HTTP funktionalitet, dvs get, put, post etc, men kapslar in $http

och står för felhantering, loggning och autentisering. Det skulle kunna se ut i stil med det här

function HttpService($http, authService) {

this.get = function(path) {

var config = {

method: ‘GET’,

url: path

};

if (authService.userIsAuthenticated) {

config.headers = { Authorization: 'Bearer ' +

authService.accessToken }

};

return $http (config)

.then(function(response) {

return response.data;

}, function(error) {

// Log

// Handle Unauthorized

return getStandardizedErrorResult(error);

})

}

}

Som synes så tar HttpService ett beroende på $http, och kapslar in anropet till denna service. I den

inkapsling så hanteras även loggning och felhantering, samt autentisering.

I det här fallet används en fiktiv service som heter authService, som hanterar huruvida användaren är

inloggad, samt dess access token.

Page 12: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

12

Detta är självklart en förenkling av vad man bör bygga, men den visar förhoppningsvis vad målet med

denna abstraktion är.

Väljer man dessutom att hantera promises manuellt med $q, så kan man erbjuda funktionalitet som

återinloggning vid utloggning utan att man förlorar data som skulle ha postats till servern etc. Något

som är väldigt svårt att göra på ett strukturerat sätt utan en HTTP abstraktion som denna.

Använd Providers and Configuration istället för inbyggda

antaganden De flesta lösningar man bygger kommer att befinna sig i olika miljöer under sin livstid. De kommer leva

i en utvecklingsmiljö, en testmiljö, en produktionsmiljö etc. Alla dessa miljöer har troligen olika

konfiguration. Kanske kör servicarna man är beroende av på lokalt på en annan port i

utvecklingsmiljön, på en intern server i test och på en publik server i produktion. Något vi måste ta

hänsyn till.

Miljöer är bara en sak som förändras när vår kod används i olika scenarion. Kanske är en del av

konfigurationen användarbaserad? Kanske vill vi kunna tända och släcka features med featuretoggles?

Lösningen är att inte bygga in logik i koden för att automatiskt byta inställningar. Logik som ”om hosten

är localhost, använd endpoint X” etc. Använd istället providers och konfigurera servicarna som

används istället.

Konfigurationen kan antingen komma genom ett anrop till en ”konfigurations” endpoint när

applikationen startar upp, eller så kan konfigurationen finnas i sidan. Eller kanske en kombination.

När jag säger att konfigurationen finns i sidan, så menar jag att man renderar konfigurationen i ett

script-block i HTML sidan enligt:

<script src=”...”></script>

<script>

var appConfig = {

serviceBaseUrl: ’http://myserver.com/api/’

};

</script>

<script src=”.../MyModule.js”></script>

Den här appConfig variabeln kan sen kommas åt och användas för service configuration i Angular

modulernas config steg. Så här

angular.module(”MyModule”, [])

...

.config(httpServiceProvider) {

Page 13: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

13

httpServiceProvider.setBaseUrl(appConfig.serviceBaseUrl);

}

Det går även att komma åt appConfig i all annan kod, men undvik detta. Det är ”illa nog” att config

funktionen förutsätter att det finns en magisk variabel. Att sprida ut det beroendet är inte bra.

Alternativt kan man även skapa en standard konfiguration som man avviker ifrån med hjälp av

Angular’s extend funktion.

angular.module(”MyModule”, [])

...

.config(httpServiceProvider) {

var defaultConfig = {

serviceBaseUrl: ‘/api/’,

myOtherConfig: ‘configValue’

}

var config = angular.extend(defaultConfig, appConfig || {});

httpServiceProvider.setBaseUrl(config.serviceBaseUrl);

}

På det här sättet så har man alltid värden satta till nått standardvärde, men möjligheten att skriva över

dem med en annan konfiguration vid behov.

Ibland kan det till och med vara vettigt att bygga in config för att skapa en fördröjning i HttpServicen.

Genom att göra det, så kan vi se hur applikationen kommer att uppföra sig i produktion där det finns

latency, istället för att bara se hur applikationen fungerar i utvecklingsmiljön där anropen till servern

är mer eller mindre försumbara.

Använd TypeScript TypeScript är ett superset till JavaScript, som transpileras till JavaScript. Det ger oss tillgång till features

som JavaScript inte erbjuder, samt features som JavaScript kommer erbjuda i framtiden, men som

webläsarna ännu inte hunnit implementera.

Bland annat ger TypeScript stöd för statisk typning. Något som kan hjälpa till på ett flertal sätt. För det

första ser det till att vi inte försöka använda fel typer när vi anropar funktioner. Det ser även till att vi

inte skickar in för många, eller för få variabler till constructors och funktioner. Till sist möjliggör det

även bättre verktygsstöd.

Genom statisk typning, så kan verktygen helt plötsligt ge utvecklaren en massa stöd som tidigare inte

varit möjligt pga JavaScripts lösa typning. Vi kan få IntelliSense funktionalitet som faktiskt fungerar.

Refactoring stöd. ”Go To” funktionalitet. Och en massa annat.

Page 14: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

14

TypeScript ger oss även stöd för klasser och interface, vilket kan underlätta och strukturera koden för

oss.

Samtidigt är det JavaScript-kompatibelt, så om man behöver JavaScripts lösa typning, eller

funktionella funktionalitet, så är det fritt fram att använda den i TypeScript också. Så TypeScript ger

oss det bästa av både de statisk typade och löst typade världarna. Det är upp till oss att välja vilka

delar vi vill använda var.

JavaScript moduler JavaScript erbjuder möjligheten att använda moduler, något som kan underlätta massor. I ES2015

lanserades idén att använda export/import i filer för att på så vis göra en fil till en ”modul”. Andra

moduler kan sedan deklarera ett beroende på en annan modul, dvs filer kan deklarera andra filer som

de är beroende på. Så istället för att alltid behöva se till att alla filer laddas in i rätt ordning på sidan,

så kan man automatisera processen genom att titta på vilka moduler man behöver ladda in, och i

vilken ordning de behöver laddas in. TypeScript stödjer även det moduler på detta vis.

Problemet är att detta är en ES2015 feature, vilket gör att det inte stöds natively av webläsarna som

finns ute idag. Det är även så att utvecklare tidigare sett detta behov, och utvecklat egna lösningar för

modulär laddning. Så det finns ett antal olika modulsystem på marknaden. Något man behöver ta i

beaktande när man väljer bibliotek och ramverk att arbeta med.

Antingen så låter man ett verktyg bundla ihop alla filerna till en stor fil på servern och serverar den till

klienten, vilket är bra i produktion, eller så använder man ett JavaScript-bibliotek i webläsaren som

kan ladda filerna asynkront för oss, vilket är bra under utveckling. Eller, så gör man det ena under

utveckling, dvs använder individuella filer och ett bibliotek för att ladda dem, och det andra i

produktion. Exempel på verktyg för att bundla moduler är WebPack och Browserify, och för dynamisk

laddning av filer i webläsaren kan man använda System.js. Alla dessa tenderar att kunna hantera alla

de olika modultyperna.

Vettigt att känna till är även system.js-builder, som är ett verktyg för att bundla moduler med hjälp av

samma logik som System.js gör i webläsaren.

Modulärladdning av moduler gör saker mycket enklare. Det är lätt att råka glömma en fil som skall in

i HTML-sidan, eller inkludera filer i fel ordning. Moduler löser detta utan problem. Dessutom gör det

att det blir lätt att blanda lösa filer under utveckling, och bundlade och minifierade filer i produktion.

Det är dock bra att veta att om man väljer att använda moduler på detta vis, och inte använder

bundling, utan laddar de individuella filerna asynkront, så kan man inte ”bootstrappa” Angular som

vanligt. Istället måste man vänta tills alla filer laddats ner, och sedan exekvera bootstrappningen

manuellt.

Page 15: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

15

Om man har en fil som heter app.js som ”entry point”, dvs som första fil att be om från servern, så

skulle den kunna se ut så här.

App.js

import { AppDirective } from './appdirective';

angular.module("MyModule", [])

.directive("myApp", AppDirective);

angular.element(document).ready(() => {

angular.bootstrap(document, [MyModule]);

});

Index.htm

<div data-my-app></div>

<script src="scripts/libs/system.js"></script>

<script>

System.config({ … });

System.import('app.js');

</script>

Det här exemplet använder ES2015 moduler med import/export syntax. Det behöver transpileras till

lämplig ES5 syntax för att fungera i en webläsare, men det är lättare att visa det så här.

Html sidan har lite kod som konfigurerar System.js för att det biblioteket ska förstå vad det ska göra.

Därefter så ombeds System.js att importera app.js. System.js ser då att app.js har ett beroende på

appdirective.js, och laddar ner den filen. Sen anges det som en del av System.js konfigurationen att

app.js har ett beroende till Angular också, så System.js laddar ner Angular också. När alla de filerna är

nerladdade, så laddas appdirective.js in i webläsaren, sedan Angular, och till sist app.js. Allt baserat på

hur de olika modulerna har beroenden till varandra.

Problemet med detta är att när Angular laddas in, så kommer det att försöka bootstrappa MyModule

om det finns ett ng-app attribut. Problemet med det är att MyModule är definierat i app.js som ännu

inte laddats in i webläsaren. Så det går inte. Så istället för att använda ngApp direktivet för att

bootstrappa Angular, så låter vi app.js innehålla koden för att göra det. Och det är koden som ser ut

så här

angular.element(document).ready(() => {

angular.bootstrap(document, [MyModule]);

});

Först använder vi Angular för att få tag i document elementet. Sen säger vi att när document är

färdigladdat, vilket det redan bör vara, så vill vi exekvera angular.bootstrap, och bootstrappa

Page 16: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

16

document elementet med modulen MyModule. Denna processen kommer då gå igenom DOMen och

hantera alla Angular attribut som väntat.

Om författaren Chris Klug är Technical Dude på Novatrox Consulting med bland annat femton års erfarenhet av

arkitektur, systemutveckling och Azurespecifik arkitektur. Han är en omtyckt lärare, mentor och talare

vid konferenser runtom i världen, nu senaste [augusti 2016] i Sydney (Australien) och Redmond (USA).

Chris har dessutom de senaste fem åren erhållit status som Microsoft MVP inom ASP.NET samt som

Microsoft Azure insider.

Page 17: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

17

Novatrox Group Till följd av en allt mer föränderlig värld med snabbare processer gör sig behovet av struktur,

informationsförsörjning och automatisering gällande. Skillnaden mellan att kunna möta

marknadens behov eller att totalt misslyckas beror på förmågan att kunna förhålla sig till dessa

snabba förändringar i en digital värld. Novatrox Group tillhandahåller resurser inom våra områden

Verksamhetsutveckling, Arkitektur, Systemutveckling samt Ledning & Styrning. Vi levererar både

resurskonsulter samt tar oss an kortare eller längre helhetsåtaganden gentemot våra kunder.

Arkitektur Inom arkitektur är Novatrox ett av Stockholms större företag med ett tiotal arkitekter. Vi hjälper organisationer att få ut mer från sina investeringar genom att stötta med struktur, erfarenhet och mentorskap.

Vi tillhandahåller tjänster inom analys och utredning, arkitektur- granskningar, arkitekturstyrning samt stöd vid strukturförflytt- ningar.

Verksamhetsutveckling Novatrox konsulter består av ett exklusivt team med unik spetskompetens och track-record inom förändringsledning och agil verksamhets- utveckling.

Vi tillhandahåller tjänster inom affärs- och verksamhetsutveckling, förändringsledning och processutveckling. Vi arbetar också med problemlösning för att få upp projekt eller team på banan igen.

Ledning & Styrning Novatrox ledningsteam säkerställer att projektens effekt- hemtagningar förverkligas. Teamet är certifierat i projekt- metodiker och agila metoder. Vi tillhandahåller tjänster inom projekt-, test- och förvaltnings- ledning. Vidare kan vi stötta med releasehantering samt driva och införa projektkontor.

Systemutveckling Novatrox är specialister inom .NET-platformen och frontend- utveckling med erfarenhet av molnlösningar, utbildning och coaching av team.

Till våra kunder tillhandahåller vi tjänster såsom föreläsningar, utbildningar, coaching, mjukvaruarkitektur samt systemut-veckling. Bland Novatrox konsulter finns Microsoft MVP’er , Azure Insiders. Vi har välkända talare vid konferenser runtom i världen.

Page 18: ANGULAR 1 TIPS FROM THE TRENCHESc1940652.r52.cf0.rackcdn.com/57c3db3aff2a7c38fb001861/2016-08-29... · Augusti 2016 3 Angular 1 – Tips from the trenches Introduktion Angular är

Augusti 2016

18

Novatrox Group

Om Novatrox Group Novatrox Group är ett svenskt Stockholmsbaserat IT- och managementkonsultföretag baserat i

Stockholm. tillhandahåller resurser inom kompetensområdena Verksamhetsutveckling, IT-

Arkitektur, Systemutveckling samt Ledning & Styrning. Vi levererar både resurskonsulter samt tar

oss an kortare eller längre helhetsåtaganden gentemot våra kunder.

Våra kunder återfinns idag bland både små och stora organisationer samt offentlig verksamhet.

Christer Ogenstad Charlotte Grip VD Säljchef [email protected] [email protected] 070 – 418 43 88 076 – 273 54 83