Frontend-utveckling i JavaScript har alltid varit full av fallgropar. Ett moget team känner till dessa och tar höjd för dem — med linters, tester, best practices, style guides, etc. Alla dessa saker höjer kodkvalitén, men de adresserar inte det grundläggande problemet: att JavaScript som språk är illa rustat för utveckling av större applikationer.
TypeScript löser det problemet. Från dag 1 med TypeScript fångar man misstag som JavaScript glatt struntar i. Man kan bygga större och mer ambitiöst, och få bättre kontroll över applikationens arkitektur. Samtidigt har man med sig och kan återanvända sin kunskap om JavaScript.
I JavaScript är det OK att glömma en property i en objektliteral, att anropa en funktion med färre eller fler argument än vad det var tänkt, eller att skriva kod som aldrig kommer att nås i ett program. TypeScript flaggar upp alla dessa saker som fel, oftast direkt i ens IDE.
how inline styles through JS is a (nearly) full solution
Towards the end we'll use React as a vehicle for our example, but the concepts apply equally well to Angular or any other modern JS framework. We won't use any "CSSinJS library".
A small caveat: according to many, what we're describing in this post isn't strictly CSSinJS, but inline styles via JS. However at this stage the distinction doesn't matter much, so if you came here because you're curious about CSSinJS, by all means read on!
Our example
Imagine we have a complex webapp with lots of styling. We now want to add a new UI concept; an explanation box, meant to be shown inside a bigger context to explain some related concept.
Here's what it should look like:
Disregarding styling, here's the markup:
<div>
<h2>Did you know?</h2>
<p>Ozelots hate maccaws!</p>
</div>
We want to apply these styles to the explanation boxdiv:
The generic h2 styles would get their own selector:
h2 {
font-family: Verdana;
font-size: 16px;
}
Note that both of these later selectors will have an opinion about the font-size of our explanation headline, but since .explanationBox > h2 is more specific than h2, the former will win.
Unanswerable question 1 - the unknown killer problem
With our styles in a CSS file, we now have the following situation:
The rule blobs are connected with their respective target elements via selectors. While they work fine for our simple example, they don't scale well. As the app grows it is very easy to end up in a messy situation where we are scared to change both HTML and CSS, in fear of some selector somewhere breaking.
Even in our small example the main reasons for the unscalability are easy to demonstrate.
Look again at our HTML...
<div class="explanationBox">
<h2>Did you know?</h2>
<p>Ozelots hate maccaws!</p>
</div>
...and ponder this simple question with regards to the div:
What styles are applied to this element?
We have no easy way of knowing! The elements are being shot at by CSS selectors somewhere out there in the dark. From this side we have no idea whether any of them are targetting this specific element.
It is likely that there is a selector targetting the .explanationBox class in a selector somewhere. But because CSS selectors are global we'd have to search all CSS in order to know.
And consider asking the same question about the h2 - it doesn't have a class, which might imply it is not in the crosshairs, but it is in fact targeted by two selectors; h2 and .explanationBox > h2.
Just by looking at the HTML we cannot know what styles, if any, are applied to an element.
Unanswerable question 2 - the unknown victim problem
We find the reverse of the same problem in the CSS file. Look for example at this part:
This is impossible to know. We'd have to search through all our HTML files, or templates in case of a dynamic app. And in a dynamic setting the .explanationBox element and the contained h2 might live in completely different places, making it even harder to figure out the answer.
This makes editing a CSS file really scary. Can we remove this part? If the question above was easy to answer then we could know whether it is safe to remove a certain part, but now we cannot!
Take 2 - using the BEM naming convention
There has been many attempts to mitigate the mess caused by the inability to answer these two questions. One such attempt is the BEM naming convention, which basically boils down to...
have selectors consist of a single class always
name the classes according to a strict convention
For our example, the explanation box would be a Block, while the h2 inside of that would be an Element of that block. This is the B and E of BEM (the M stands for modifier but we don't need that here).
Using BEM logic our markup would become:
<div class="explanationBox">
<h2 class="explanationBox__headline">Did you know?</h2>
<p>Ozelots hate maccaws!</p>
</div>
It is now slightly more clear what styles are applied where. Both the earlier questions are easier to answer, since we can do a search for the more precise class names.
But we still have the problem of the generic h2 selector. Also class names might be dynamically created and/or added to an element, so again, in a dynamic setting it might still be hard to find out exactly what's going on.
Take 3 - inline styles in a React component
The basic idea in BEM, as well as in OOCSS or any of the other attempts at bringing structure to CSS, is to think in terms of components. All style definitions, as far as possible, should be tied to a certain component.
This jives very well with modern JS frameworks, who (almost) all have a component-centric architecture. If components are our main abstraction, then it is very easy to make an API to style individual components.
React (and most of the competition) does this by letting us provide a CSS-like JS object to the style prop:
const box = {
borderRadius: "15px",
padding: "10px",
border: '2px solid springgreen',
backgroundColor: 'moccasin',
color: 'magenta'
};
const headline = {
fontFamily: 'Verdana', // should live in generic h2 styling, but we're cheating for now
fontWeight: "normal",
letterSpacing: "5px",
paddingLeft: "15px",
fontStyle: "italic",
fontSize: '14px'
};
export class ExplanationBox extends React.Component {
render() {
return (
<div style={box}>
<h2 style={headline}>{this.props.title}</h2>
{this.props.children}
</div>
);
}
}
Elsewhere in our React app we can now do this...
<ExplanationBox title="Did you know?">
<p>Ozelots hate maccaws!</p>
</ExplanationBox>
...and we have our reusable, styled component!
Take 4 - imported inline styles
However, what you just saw was really nothing but a regression to our Take 0 attempt, where we put all styles inline. Like an animal.
But we can improve on this super easily, simply by migrating the styles to a separate file! Imagine the following living in a file named styles.js:
We have now sort of reinvented the stylesheet! Except, where before the stylesheet used selectors to fire blindly into the dark...
...we now let the components go get exactly the styles they need via imports:
It might not be immediately obvious, but the flipping of this arrow makes a world of difference!
Finding the killer
Remember the first unanswerable question?
What styles are applied to this element?
In our imported inline style setup, this becomes trivial to answer. I simply follow the thread backwards from the style prop!
Finding the victim
Our second unanswerable question was:
To which elements are these styles applied?
Again, because of the explicit imports, this becomes very easy to answer (if you have a semi-competent editor):
LESS isn't more
Through moving our style definitions to JS land, the need for CSS preprocessors goes out the window. Remember how we used the ... spread operator to mix in the h2 styles into our headline rules?
That was the equivalent of a LESS/SASS mixin! You'll quickly find that the other syntax they give us can be easily replicated with pure JS. For example, having global variables becomes trivial:
As do implementing helper functions to add vendor prefixes, saturate colours, etc.
Wrapping up
To recap; by turning the arrows around through using JS and inline styles, almost all opaqueness of regular CSS goes away. Previously difficult tasks such as...
renaming a CSS class
identifying and removing dead CSS code
hitting the correct elements with selectors
NOT hitting the wrong elements with selectors
...suddenly become easy or entirely moot.
While there are still a lot of questions to be answered regarding moving CSS to JS space, there's no doubt in my mind that just being able to flip the arrow of responsibility makes it all worth it.
One of the primary downsides of inline JS styles is that we can't use pseudoselectors such as :hover or @media. I hope to show how we can get around that using helper libraries in an upcoming post.
Edument AB was founded in 2010 by Acke Salem and Tore Nestenius. They met for the first time at a conference where they began discussing the lack of services in the development sector that included both education and mentoring. Hence the company name stems from EDUcation and MENTorship.
Today Edument has 25 employees and offices in Helsingborg, Malmö, Falkenberg and Prague. We have educated and mentoring thousands of developers around the world. And been awarded "Årets Gasell" by Dagens Industri two years in a row.
Jag har tänkt en del på
typer på sistone. Jag har kommit fram till att jag gillar dem, och att de
fortfarande är på uppgång i konventionella programmeringsspråk.
Jag pratar om ord som
`int` i nedanstående programsats:
int sum = 0;
Detta bruk av ordet
"typ" är bekant för användare av språk i C-familjen, inklusive Java
och C#. I samband med att vi introducerar variabeln `sum` så deklarerar vi
också att den ska ha en heltalstyp med en viss storlek. Om inte annat så att
kompilatorn ska veta hur den ska allokera variabeln, och vilka operationer den
ska tillåta.
"OK", kan
läsaren tycka. "Det låter ju rätt uppenbart." Låt oss genast göra
saker lite mindre självklara.
Min bakgrund är till stor
del med dynamiska språk, som Perl, Python, och JavaScript. I vart och ett av de
språken anger man _inte_ typen på en variabel man introducerar:
my $sum = 0; # Perl
sum = 0 #
Python
let sum = 0; // JavaScript (ES6)
Med undantag för ytliga
skillnader i syntax så gör de språken exakt samma sak i det här fallet: de
introducerar variabeln _utan_ att ange att dess typ ska vara `int` (eller något
annat, för den delen). Det är lite det vi menar med "dynamiskt
språk".
En av de stora
säljpunkterna med de dynamiska språken är att man kan skriva kod utan att vara
så nitisk med sina typer. Det liksom funkar i alla fall. Den resulterande
känslan är luftigare och smidigare; vägen från problem A till lösning B kantas
inte av typrelaterade kompileringsfel.
En van användare av C++
eller något annat statiskt typat språk kan mycket väl fråga sig hur de
dynamiska språken över huvud taget kan fungera:
Hur vet
kompilatorn hur mycket minne den ska allokera om den inte vet vilken typ
variabeln ska ha
Vad händer om
man försöker lägga en sträng i `sum` senare?
Vad händer om
man försöker göra något med en `int` som man i själva verket bara kan göra med
strängar, eller arrayer?
Svaret är att
kompileringsprocessen mestadels håller sig utanför sådana frågor i dynamiska
språk. Variabeln `sum` i sig _har_ inte någon typ. Det är bara värdet (0) som
har en typ. Vilket svarar på frågan om vad som skulle hända om man lade en
sträng eller något annat i `sum` senare i programmet i ett dynamiskt språk:
ingenting särskilt. Det fortsätter att fungera.
På samma sätt fungerar
det att inte veta vid kompilering hur mycket minne en variabel kommer att
behöva. Allt det hanteras istället under körning. Resultatet blir att vi inte
är helt optimala vad gäller prestanda och minnesåtgång i dynamiska språk. Men
det betraktas som en acceptabel tradeoff mot att programmet är lättare att
skriva och ändra.
Slutligen, om man
försöker behandla värdet 0 som en sträng eller en array eller liknande: de två
saker som kan hända är
1.att värdet 0
konverteras implicit till rätt typ innan operationen utförs, eller
2.att ett fel
meddelas vid den punkten under körning.
(Vilken av dessa som
händer beror på exakt vilka typer som är inblandade, samt hur tillåtande
språket är.) I ingetdera fall får vi en tidig varning under kompilering; vi
måste vänta tills någonting faktiskt går fel i programmet för att vi ska märka
det.
Det är den här sista
punkten jag har tänkt på mycket på sistone. Förespråkare för de statiskt typade
språken kan omedelbart peka på detta som en form av vansinne: varför skulle man
vilja få fel vid körning som man skulle kunna få vid kompilering? Varför skulle
man välja att ens starta ett program som var felaktigt på det sättet?
("Skjut aldrig upp till körning vad du kan göra vid kompilering"...)
Man kan ju svara att
program som skrivs i de dynamiska språken ofta är rätt småskaliga: de är inte
så långa i antalet rader mätt, och de tar inte så lång tid att köra. Så alla
eventuella fel man gör på vägen upptäcker man också rätt snabbt.
Även om det antagandet
ofta stämmer, så tror jag att det allt oftare är fel. Idag skrivs storskaliga,
ambitiösa, affärskritiska program även i dynamiska språk. Ta PHP hos Facebook
som exempel. Eller JavaScript hos i princip vem som helst med frontend-kod. Det
är två exempel på språk som gradvis har funnit sig i betydligt större skor än
vad de ursprungligen skapades för. När saker skalar upp så blir avsaknaden av
statisk typning allt mer kännbar.
Något spännande har dock
hänt de senaste åren. Facebook har lanserat Hack, en statiskt typad version av
PHP. Microsoft har släppt TypeScript, en statiskt typad version av JavaScript.
Båda de här insatserna vinner framgång genom att *behålla* de dynamiska språken
som redan har vunnit popularitet, men att i efterhand förse dem med statisk
typning. Ofta kan man skriva sin kod som man brukade göra, men där man vill kan
man ange en statisk typ, och då kontrolleras den vid kompilering. Det är på
många sätt den bästa av två världar: ett dynamiskt språk med statiska drag.
Även Python har på
sistone fått statisk typning, via PEP 484 (ett förslag om "Type
Hints" i språket) och det externa verktyget `mypy`. Men känslan är lite
annorlunda än Hack och TypeScript — PEP 484 är tydlig med att det
_inte_ handlar om att få typfel vid kompilering, utan att det är en sorts
API-dokumentation. Riktlinjer, snarare än hårda regler. I slutänden får
marknaden avgöra om man vill skriva Python med eller utan typer. Jag ser fram
emot att prova med, och känna hur det känns.
Jag gillar tanken på att
kunna ha ett dynamiskt språk som inte är i vägen när man skriver små saker, men
som också klarar av att stötta upp en med statisk typkontroll när man behöver
struktur och skala.
Typer i programmering har
en intressant historia. Tre till synes oberoende definitioner har flödat samman
till ett enda koncept:
·Typ som i
"datatyp", precis som i `int sum` ovan: representationen av ett värde
i minnet. Det bruket går tillbaka till Algol på 60-talet, och är vad de flesta
idag menar med ordet. Innan kunde man se ordet "typ" användas på ett
vardagligt sätt; i och med Algol börjar det betyda "representation".
·Typ som i
"algebraisk typ" eller "typalgebra": insikten att typer är
värden som kan kombineras och manipuleras på olika sätt. `struct` i C är en
typisk produkttyp, till exempel, men även `Tuple` i många språk. Det finns typer
som `Either` och `Option` som indikerar val av olika slag. Den största påverkan
den här traditionen har haft på industrin är nog dock generiska typer: om man
har en `String` så kan man också bilda en `List<String>` eller en
`Promise<String>`.
·Typ som i
"typteori". På tidigt 1900-tal gick matematiken igenom en kris i sina
grundvalar. Bertrand Russell och andra undersökte sätt att undvika paradoxer i
formella logiska system; typer blev en möjlig mekanism. Långt senare visade det
sig att det finns en bro som överför alla resultat i logik till motsvarande
resultat i programmering, och vice versa. Det som man hade kallat typer i
matematik är precis det som man kallar typer i programmering.
Vi som industri har rätt
bra koll på den första definitionen av typ. Vi får allt bättre förståelse av
den andra definitionen. Den tredje har fortfarande inte börjat lämna spår i
konventionella språk, men det känns som att det är i den riktningen vi rör oss.
Vi vet inte hur de
kommande årtiondens programspråk kommer att se ut, men min gissning är att de
kommer att utnyttja statisk typning på alltmer intressanta sätt. Man får
mersmak av att skriva program som gör rätt sak första gången man kör dem.
Carl
Mäsak
A developer with a fondness for
dynamic languages, evolutionary design, and meaningful unit testing. Likes to
heal ailing legacy applications. Designs and implements programming languages
in his spare time.
Då var det bloggdags igen! Förra gången var det java och maskinlärande, nu blir det modern webbutveckling med Bootstrap, jQuery, Ajax, ASP.NET MVC och OrigoDB som backend. I denna första post börjar vi med användargränsnittet och bygger en prototyp. I nästa post skall vi bygga server-sidan och knyta ihop allt med jQuery ajax. Så starta notepad, eller annan favoriteditor, och häng med!
Webbrevolutionen
Det händer mycket inom webb just nu. Med html5, javascript, css3, websockets och hundratals olika ramverk kan man bygga rika, interaktiva webbapplikationer. Jag brukar kalla denna stacken för Native RIA. Samtidigt ser vi andra RIA platformar på tillbakagång. Flash är borta från iOS och Silverlight, vad är det? Med CSS3 Media Queries är det möjligt att skapa responsiva webbappar som anpassar sig till mobiler, plattor och olika skärmstorlekar.
Typiskt för Native RIA är single-page webbappar där sidan laddas en gång och sedan drivs av javascript händelser och asynkrona serveranrop. Att ladda och rendera hela sidan vid varje förändring, även små, är inte speciellt användarvänligt och dessutom tyngre för servern. Det finns många javascript ramverk som stödjer ajax och manipulation av DOM-trädet. Exemplet kommer använda jQuery, min favorit.
Börja alltid med en prototyp
Det sägs att en bild säger mer än tusen ord. Med en interaktiv prototyp får kunden känna på vad de får och kan ge feedback tidigt i utvecklingen. Med prototypen som utgångsläge blir det också enklare att bygga rätt backend från början.
I detta stadiet brukar jag använda en enklare text editor, fast Visual Studio har numera riktigt bra stöd för html, css och javascript. Webbläsaren Chrome har bra inbyggda utvecklarverktyg med interaktiv konsol. Ett annat verktyg för att enkelt testa saker med olika javascript ramverk är jsfiddle, här är en cool demo av jQuery ElementStacks.
Lizzy
Som exempel tänkte jag att vi bygger en extremt enkel single-page app där användaren kan hantera ett antal listor. Målen är att göra det extremt enkelt och avskalat, med få men bra features, enkelt att använda samt med ett snyggt gui. Så här ser prototypen ut. Man klickar för att redigera text och pekar för att få fram knappar med olika alternativ.
Skärmdump prototyp Lizzy
Bootstrap
Bootstrap är ett ramverk med css-klasser och javascript-baserade komponenter med stöd för snabb och enkel utveckling av webbgränssnitt. Det stöder även responsiva webbapplikationer. Det är idealiskt för att göra snabba prototyper.
För att använda bootstrap länkar man in deras CSS. Man kan ladda ner och servera från den egna webbservern eller länka direkt från ett CDN, Content Delivery Network:
Här är ett block html (kopierat från bootstraps webb!) som renderar en responsive meny. Den kollapsar och visar en menyikon när fönsterbredden blir för liten. Klistra blocket i body-taggen ovan och prova!
I övrigt kommer vi använda en 12-kolumners layout, 3 kolumner för menyn till vänster och 9 kolumner för innehållet till höger, listan. Vi använder en modal dialog för att bekräfta borttagning samt enkla UL element med transparenta INPUT-fält för själva listorna, det visas nedan eftersom vi kommer manipulera det med jQuery. Hela designen kan du se i den färdiga prototypen.
Javascript
För att sidor skall laddas snabbare är det nu vanligast att man lägger javascript i slutet av html-dokumentet, sist i body-taggen. Vi börjar med att importera jQuery, jQueryUI för drag/drop i listan och bootstraps javascript lib som behövs för den modal dialogen.
jQuery är en funktion med ett antal överlagrade argument. I grunden används jQuery för att selektera element i DOM-trädet och sedan manipulera de valda elementen på olika sätt. Här är ett exempel som döljer alla div-element på sidan med en långsam animering.
jQuery('div').hide('slow');
Dollartecket, $, är ett alias för jQuery, eller en variabel som refererar till samma objekt om man skall vara helt korrekt. Således kunde man skrivit
För att slå på drag/drop på listan, selekterar vi på id och anropar jqueryUI funktionen sortable med lite in-parametrar som styr beteendet. Detta gör vi i en funktion som skickas som argument till jQuery-funktionen. Funktionen anropas först när DOM-trädet laddats, detta garanterar att vi inte refererar till DOM-noder som ännu inte finns. Hela konstruktionen är en kortform för $(document).ready(function(){...})
Användarinteraktion
Nu börjar det bli intressant. Det som återstår av prototypen är att rigga upp alla händelsehanterare med jQuery. Bland annat skall det:
Visas knappar med ikoner (flytta, papperskorg) när vi pekar på ett element i listan
Visas en bekräftelse dialog när vi klickar på ta-bort knappen
Tas bort rätt rad i listan vid bekräftelse
Göras uppdateringar mot servern med ajax
Alla dess händelser binds till funktioner med hjälp av jQuery, även dessa i document.ready callbacken. Observera alla todo-kommentarer där vi senare skall göra ajax-anrop för att göra uppdateringar på serversidan. Vi skriver även till konsolen för att se att vi har rätt data för uppdatering.
Sammanfattning
Vi har skapat en single-page webapp (Native RIA) med html, bootstrap css, jQuery och jQueryUI. I nästa post skall vi bygga server-sidan med ASP.NET MVC, en OrigoDB backend och knyta ihop allt med jQuery ajax.
HTML5 innebär ett enormt förskott för moderna
webbapplikationer och kommer att öka möjligheterna i webbläsare och
webbapplikationer i många år framöver. Webbläsare som Chrome, Firefox,
Safari och Internet Explorer 9 stödjer redan många av de nya API:er och
funktioner som HTML5 medför.
Informator, Nordens ledande företag inom IT- och företagsutbildning, lanserar idag två nya Microsoftkurser i HTML5: HTML5 Programming (MS10953) och Introduction to HTML5 (MS50564).
På kursen HTML5 Programmering får deltagarna lära sig att
använda HTML5, samt att utveckla HTML5, CSS3 och JavaScript
webbapplikationer med Microsoft Visual Studio 2010. 2-dagarskursen Introduction to Html5 är en "get your hands dirty" där deltagarna får gå rakt på sak och hoppa direkt in i HTML5-kod.
Events are
a relatively simple, but very powerful idea. At its simplest, an event is a
statement that something has happened. Usually, it includes some data that further
defines what has taken place. Events show up very naturally when doing GUI
programming, either for the desktop (think WinForms) or web (JavaScript).
However,
that's not the use case I want to discuss today. Instead, I want to consider
the use of events for decoupling business logic.
Boundaries
Lack of
boundaries has to be one of the most common problems I see in software systems.
Layered architecture has taken a hold in many systems, and often helps bound
the UI and business logic (and sometimes too many other things beyond that). However,
too often the business domain is seen as one big thing, with no serious effort
made to look for and maintain explicit boundaries. Both DDD and SOA are pretty
clear that we need to hunt for such boundaries, however.
The
argument that tends to crop up next is that one part of the system will often need
to know what's going on in other parts. For example, it's very well for a travel
company to say they'll bound sales, booking and their transport services into
separate services or contexts. However, in reality, if a booking were to be cancelled
at the last minute then the sales context should know about this so they can do
a special last minute deal, and the transport services context also need to remove
the passenger from its lists.
Clearly,
the booking context is responsible for handling the cancellation. In a monolithic
system, the cancellation logic would mark the booking as cancelled, and then go
and handle the notification of the sales team and updating the passenger list.
If we are series about boundaries, however, then this simply isn't going to fly.
So what can we do?
Publish
Subscribe
We can
resolve this problem using a popular messaging pattern, known as publish subscribe.
First, we define a BookingCancelled event. Typically, I would just represent
this as a simple class.
public
class BookingCancelledEvent{ public int
BookingID;public List
Passengers;}
The booking
context would mark the booking as cancelled and do anything that is directly
related to bookings. It would then publish this event. Meanwhile, the sales and
transport contexts would be subscribed to booking cancellation events. They
receive the event, look at the data contained within it and take action as appropriate.
We have now
managed to achieve a very loose coupling between the different parts of our
business system. The only things that flow over boundaries are events. Should a
new context be interested in knowing about cancellations, then it's trivial to
handle; the booking context needs no changes whatsoever, and the new context is
just added as an extra subscriber.
It's all in
the past
The naming
of events is of great importance. An event should always be in the past tense.
This makes it absolutely clear that the event in question has already taken
place. There's nothing a receiver can do to change that it happened; they can
only act upon that new reality.
Events
should also have a single source. It should never be possible for more than one
context to publish the same type of event. This maps down to a deeper principle:
every piece of information in your system should have a single owner that is
responsible for it. It's fine for other contexts to maintain their own local
copies of the data, or at least the parts they are interested in – but there is
only one single authority for it.
Implementation
There are
many ways to implement events along with publish subscribe. For a small,
single-process system then a relatively simple in-memory message bus or event
aggregator isn't a great deal of code. For larger systems, however, events come
in to their own. Using an enterprise service bus - such as NServiceBus -
enables a service to publish events and other services to subscribe to them.
Such systems use store and forward, providing reliable and asynchronous message
delivery.
Learn more
I consider
events important, and thus they show up in many courses I'm involved with. Of
note, in the C# Masterclass I work through building a simple in-process event
aggregator. The Software Architecture course talks about events somewhat. They
will also be a core topic in the forthcoming Hands On DDD and CQRS course,
where we'll consider events not only as a mechanism for decoupling, but also as
a primary storage mechanism.
Idag drar JFokus igång och som veckans gästbloggare har vi passande nog Java-experten Kjell H Carlsson. Kjell håller bland annat kurs i Java for Managers, Open Source Effective Java, samt Grund och Avancerad kurs i java mm. Kjell anlitas flitigt av Informator sedan många år tillbaka och uppskattas av deltagarna för sin djupa kunskap, sin pedagogiska förmåga och såklart för att han alltid har koll på det senaste.
Vi är mycket glada att Kjell bloggar hos oss den här veckan!