Sosiaaliturvatunnuksen tarkistus Angular-direktiivillä

Jep, tuli tarpeeseen ihmetellä Angularin direktiivejä ja validointia – miten sosiaaliturvatunnuksen oikeellisuus validoidaan? Tähän ei löytynyt pikaisella haulla valmista ratkaisua joten ei muuta kuin tekemään oma.

Suomalaista sosiaaliturvatunnusta kun ihmetellään, niin osan tarkistuksista voi hoitaa ihan olemassaolevilla direktiiveillä. Esim. pituus tasan 11 merkkiä, aina. Tunnuksen muodon osalta voi olettaa siinä olevan numeroita ensin kuusi, sitten välimerkki jossa on muuutamia mahdollisuuksia, sitten kolme numeroa, ja tarkistuskoodi joka voi olla numero tai kirjain tietystä valikoimasta. Tämä hoituu komeasti esim. Angularin regex-patternilla.

Mutta syy miksi kirjoitan tätä blogia itselleni muistiin on nimenomaan tuon viimeisen merkin tarkistussumman laskeminen, ja oman custom validation direktiivin teko. Se menee nimittäin näin:

(function() {
  angular.module('app').directive('validSsn', validSSN);

  validSSN.$inject = ['$log'];

  function validSSN($log) {
    var checksumChars = 'abcdefhjklmnprstuvwxy';

    var validateSSN = function(value) {
      if (value === undefined || value.length != 11) {
        return false;
      }
      var datePart = value.substr(0, 6);
      var individualNumber = value.substr(7, 3);
      var checkSumCharacter = value.substr(10).toLowerCase();
      var dividedValueModulo = parseInt(
          String(datePart) + String(individualNumber)) % 31;
      var expected;
      if (dividedValueModulo < 10) {
        expected = String(dividedValueModulo);
      } else {
        expected = checksumChars.charAt(dividedValueModulo - 10);
      }
      if (checkSumCharacter === expected) {
        return true;
      } else {
        $log.debug('Expected checksum ', expected,
          ', got checksum ', checkSumCharacter);
        return false;
      }
    };

    return {
      restrict: 'A',
      require: 'ngModel',
      link: function(scope, elem, attr, ctrl) {
        ctrl.$parsers.unshift(function(value) {
          var valid = validateSSN(value);
          ctrl.$setValidity('valid-ssn', valid);
          return value;
        });

        ctrl.$formatters.unshift(function(value) {
          var valid = validateSSN(value);
          ctrl.$setValidity('valid-ssn', valid);
          return value;
        });

      }
    };
  }
}());

No niin, tuossa on vielä paljonkin siistittävää ja optimoitavaa, mutta jätetään se itsekunkin omaksi harjoitukseksi. Idea käy silti ilmi. Katsotaan ensin että arvo on olemassa ja oikean mittainen – ellei, se automaattisti ei läpäise validointia. Jos mitta on oikea, otetaan kaikki data-osan numerot, 9 kappaletta, merkkijonona, ja jaetaan alkuluvulla 31. Jakojäännös kiinnostaa – jos se on alle 10 se on suoraan tarkistuskoodi, jos se on 10 tai yli, verrataan kirjaintaulukkoon josta on muutama kirjain selvyyden vuoksi poistettu.

Sitä voi testata esim. näin:

it('should pass validation with valid ssn', function() {

  var element =
        $compile(
          '<form name="form">' +
            '<input valid-ssn name="ssn" ng-model="value" />' +
          '</form>')($rootScope);
  var form = $rootScope.form;

  $rootScope.value = '140785-940X';
  $rootScope.$digest();
  expect(form.ssn.$valid).toBeTruthy();

  $rootScope.value = '140785-940x';
  $rootScope.$digest();
  expect(form.ssn.$valid).toBeTruthy();

  $rootScope.value = '140785-9143';
  $rootScope.$digest();
  expect(form.ssn.$valid).toBeTruthy();

});

Tällaista tällä kertaa. Jos hommaa haluaisi vielä kehittää. voi toki tarkistaa että päivämäärä on oikea, sukupuoli vastaa muualla annettua tietoa, if any, jne.

Mainokset

AngularJS, Protractor, ja hidas testiympäristö

Tulipa ajankohtaiseksi miettiä miten protractorin voisi saada odottelemaan että jotain on saatavana ruudulla, ennen kuin yrittää alkaa sitä klikkailemaan.

Periaattessahan protractorin pitäisi olla Angular-elinkaaresta hyvinkin tietoinen, ja odotella asynkronisten kutsujen valmistumista ennen kuin etenee. Hätätapauksessa on taikauskonomainen browser.waitForAngular() jonka pitäisi tehdä samantapaista.

Nyt näyttäisi kuitenkin siltä, että protractor (traktorin puolesta? 🙂 ei näin aina tee, etenkin jos testikone on hidas/jähmeä *köhköhvirtuaalikoneköhköh*. Voi siis käydä niin että yhdessä koneessa testit sujahtavat iloisen vihreänä läpi ja toisessa helottavat punaisina satunnaisesti. Tämä ei ole oikein hyvä juttu testiympäristön kannalta.

Protractorissa on melkoisen epämiellyttävää kirjailla koodia joka tutkii onko elementit näkösällä – nuo element(by.xxx()) komennot kun palauttavat lupausta tulevasta, promiseja, jotka pitää ratkoa asynkronisesti. Kun haluaa tarkistaa useamman elementin näkyvyyden/enabloinnin, tulee ihastuttavia sisäkkäisiä lohkoja joita itse en tykkää koodissa katsella.

Vaan eipä hätää. Löysin taannoin juuri tähän sopivan syntaksin, joka toimii porttina estäen testin etenemisen kunnes kohde löytyy tai timeout pamahtaa. Se on näinkin simppeli:

browser.wait(element(by.id('my-id')).isPresent);

browser.wait(element(by.id('my-id')).isDisplayed);

browser.wait(element(by.id('my-id')).isEnabled);

Tämäkin on jonnekin manuaalin uumeniin haudottu. Näillä voi hidastaa tahtia sen verran että hitaammatkin myllyt ehtivät painelemaan nappeja kun käyttöliittymä morffaa silmien edessä. Tuolle browser.wait funktiolle muuten kelpaa miten hyvänsä muotoiltu promise jota voi tutkiskella.

Tietysti menisi liian helpoksi jo se olisi ihan noin toiminut. Tuo isPresent ainakin itsellä antoi jotain herkullista cannot get count for undefined erroriherjaa joten tein sille hiukan kankeamman vastineen:

browser.wait(function () {
 return browser.isElementPresent(element(by.id('zzzzz')));
 }, 10000);

Nämä voi hautoa mukaviin uudelleenkäytettäviin funktioihin, ja taas testiautomaatio jauhaa luotettavammin!

 

Angular Protractor ja Drag&Drop testaus

No niin, mitäpä ei moderni tablettikäyttöliittymä olisi ilman drag&drop kikkailua. Ajankohtaiseksi tuli miettiä miten sellaista voisi testata, automatisoidusti.

Onnekkaasti protractorista löytyi tähän suoraan rahkeet, jotka jopa toimivat:

// Grab two panels
var panels = element.all(by.repeater(’panel in panelsInGrid’));
var panel1 = panels.get(0);
var panel2 = panels.get(1);

// Drag&drop to new location
browser.actions().dragAndDrop(
panel1,
panel3
).perform();

Ja sillä siisti. Toimii (paremmin kuin) junan vessa. Joskus on tarve absoluuttisille koordinaateille:

browser.actions().dragAndDrop(
panel1DragHandleEast,
{x: 200, y: 200}
).perform();

Jep, sekin onnistuu. Onkohan jotain mitä Protractorilla ei voi automatisoida.. 😉

AngularJS, Animate, ja Material slideshow

No niin, tulin kirjailleeksi Angular-appsin korvaamaan powerpoint kalvosulkeisia jollain joka toimii kivasti selaimessa, vaikkapa ipadissa. Tarkasti ottaen modifioin olemassaolevaa täysin hyvää koodia, lisäten siihen muutaman oman mausteen. Originaali artikkeli ja koodi löytyy täältä:

http://onehungrymind.com/build-a-full-page-angularjs-slideshow/

Mitäpä modasin tästä? No, originaali artikkelissa oli $timer palvelulla ajastetut slidet, halusin itse vaihtaa nappia painamalla. Lisäksi halusin ottaa Material Design kirjastot mukaan jatkokehitystä varten, ja käyttää sieltä tulevaa primary button-ideaa. Ja tietysti päivitellä kirjastot tuoreimpiin. Lisäksi kokeilin tehdä tämän alusta alkaen Netbeans HTML5 projektina. Ja täytyy sanoa että sekin puoli alkaa hiljalleen olemaan toimiva.

Joka tapauksessa, jahka projektirunko on luotu (esim. Netbeans HTML5 projekti ja angular seed), on aika päivittää kirjastoriippuvuudet bower.jsoniin:

{
 "name": "angular-seed",
 "description": "Spagettikoodi slideshow presentation",
 "version": "0.0.1",
 "private": true,
 "dependencies": {
 "angular": "1.3.x",
 "angular-route": "1.3.x",
 "angular-loader": "1.3.x",
 "angular-animate": "1.3.x",
 "angular-mocks": "~1.3.x",
 "angular-material": "~0.5.x",
 "bootstrap":"*",
 "angular-bootstrap":"*",
 "gsap": "~1.14.x",
 "PreloadJS": "*",
 "material-design-icons": "*"
 },
 "resolutions": {
 "angular": "~1.3.x"
 }
 }

Seuraava tempaus: Bower lykkää tiedostot juuren alle bower_components kansioon, kun taas Netbeans sisäänrakennettu serveri ei osaa sieltä hakea tiedostoja. Tein siis muutoksen jossa bower lykkääkin hakemansa tiedostot app/lib alle, tekemällä .bowerrc tiedoston jonka sisältö on:

{
 "directory" : "app/lib"
 }

Nyt kun ajan bower install operaation, tarvittavat kirjastot tiukkuvat app/lib kansion alle – jonka muuten lisään .gitignoreen samalla.

Seuraava steppi on ladata javascript kirjastot. Tässä projektissa en käyttänyt requirea joten lataus menee ihan näin:

<script src="lib/jquery/dist/jquery.js"></script>
 <script src="lib/angular/angular.js"></script>
 <script src="lib/angular-route/angular-route.js"></script>
 <script src="lib/angular-animate/angular-animate.js"></script>
 <script src="lib/gsap/src/uncompressed/TweenMax.js"></script>
 <script src="lib/angular-bootstrap/ui-bootstrap-tpls.js"></script>
 <script src="lib/PreloadJS/lib/preloadjs-0.4.1.combined.js"></script>
 <script src="lib/angular-aria/angular-aria.js"></script>
 <script src="lib/hammerjs/hammer.js"></script>
 <script src="lib/angular-material/angular-material.js"></script>
 <script src="js/app.js"></script>

Tämän ohella on hyvä latailla material design tyylisivut ja haluamasi teema, esim. näin:

<link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.css"/>
 <link rel="stylesheet" href="./lib/angular-material/angular-material.css"/>
 <link rel="stylesheet" href="./lib/angular-material/themes/default-theme.css"/>
 <link rel="stylesheet" href="./css/app.css"/>

Nyt saakin koota sitten index.html sivun body-tagin sisälle slideshow käyttöliittymä. Se menee taas näin:

<div>

<div class="img-container">
 <img ng-repeat="slide in slides"
 class="fullBg {{currentAnimation}}"
 ng-swipe-right="nextSlide()"
 ng-swipe-left="prevSlide()"
 ng-if="isCurrentSlideIndex($index)"
 ng-src="{{slide.image}}"
 >
</div>

<md-button
 aria-label="Next"
 style="position: absolute; right: 3em; top:2em;"
 class="md-fab md-primary"
 ng-click="nextSlide()"
 >
 <md-icon icon="lib/material-design-icons/navigation/svg/production/ic_arrow_forward_24px.svg"
 style="width: 24px; height: 24px;">
 </md-icon>
</md-button>
</div>

No niin, pari huomiota tuosta koodista:

– Päätyön tekee tuo kuva joka hakee ng-repeat direktiivillä controllerin scope-muuttujista slides-muuttujalta kuvien nimet, ja lataa ne. ng-if vuorostaan näyttää valittuna olevan, css luokka määrittää täysikokoisen kuvan sekä valitsee halutun animaation käyttöön siirtymiin. Tästä koodista voi kiittää originaalia 100%.

– Alla on kokeiluluontoinen md-button. Sliden vaihtamisen voi näprätä mihin hyvänsä muuhunkin muotoon, mutta tässä oli tarkoitus testata materialia, joten otetaan pyöreä fab-nappi, primary tehostein. Jos haluaa kokeilla muita teemoja, voi lisätä tähän esim. md-theme=indigo tms – mutta sitten on myös syytä ladata vastaavat css-tyylisivut.

– Button käyttää myös yhtä material-icons kirjaston valmis-ikoneista.

Homma alkaa kasaantumaan. Tämähän vaatii myös tyylisivun. En tehnyt originaaliin kummempia muutoksia joten se löytyy tuolta alussa olevan linkin takaa, mutta tässä keskeinen kohta, eli miltä .fullBg näyttää:

.fullBg {
 position: fixed;
 width: 100%;
 height: 100%;
 top:0px;
 left: 0px;
 }

No niin, sitten varsinaiseen mielenkiintoiseen kohtaan eli koodiin. Tässä on taustacontrolleri:

'use strict';

var app = angular.module('website', ['ngMaterial','ngAnimate', 'ui.bootstrap']);

// Declare app level module which depends on filters, and services

app.controller('MainCtrl', function ($scope) {

  $scope.slides = [];
  for (var i = 1; i <= 29; i++) {
  $scope.slides.push(
    {
      image: 'images/Slide' + i + '.PNG',
      description: 'Image ' + i
    }
  );
}

$scope.currentIndex = 0;
$scope.currentAnimation = 'slide-left-animation';
//$scope.currentAnimation = 'slide-down-animation';
//$scope.currentAnimation = 'fade-in-animation';

$scope.setCurrentSlideIndex = function (index) {
 $scope.currentIndex = index;
};

$scope.isCurrentSlideIndex = function (index) {
 return $scope.currentIndex === index;
};

$scope.nextSlide = function () {
 $scope.currentIndex = ($scope.currentIndex < $scope.slides.length - 1) ? ++$scope.currentIndex : 0;
};

$scope.prevSlide = function () {
 $scope.currentIndex = ($scope.currentIndex > 0) ? --$scope.currentIndex : $scope.slides.length - 1;
};

$scope.setCurrentAnimation = function (animation) {
 $scope.currentAnimation = animation;
};

$scope.isCurrentAnimation = function (animation) {
 return $scope.currentAnimation === animation;
};

$scope.keyPress = function (keyEvent) {
 $scope.nextSlide();
};

});

Jep, eli alussa ladataan slide-kuvia 29 kappaletta. Nollataan indeksi ja valitaan animaatiotyyli. Sitten määritellään muutama funktio – lähinnä set/get funktioita tilanhallintaan – niillä näprätään mikä on valittuna oleva slide ja millä ehdoilla edetään tai peruutetaan, ja mikä on tämänhetkinen animaatio.

Sitten päästään itse mielenkiintoiseen: miten angular-animate toimii. Sillä voi määritellä animaatio-tyyliluokkia, ja kun html elementille on sellainen määritelty, se määrittää mitä tapahtuu esim. elementin poistuessa tai ilmestyessä säiliöön. Esim. jos kuvallemme on annettu, kuten yllä, tällainen tyylimääritys:

<img ng-repeat="slide in slides"
 class="fullBg slide-left-animation"
 ...

..niin kuvan poistuessa näkyvistä tai ilmestyessä ajetaan seuraava animaatio, jonka voit myös määritellä controllerille:

app.animation('.slide-left-animation', function ($window) {
 return {
 enter: function (element, done) {
 TweenMax.fromTo(element, 1, {left: $window.innerWidth}, {left: 0, onComplete: done});
 },
 leave: function (element, done) {
 TweenMax.to(element, 1, {left: -$window.innerWidth, onComplete: done});
 }
 };
});

Se tekee mitä vain haluat. Tässä siis sisääntullessa ajetaan TweenMax.fromTo funktio (GreenSock kirjastosta, joka aiemmin otettiin käyttöön) – ja se animoi siirtymän oikealta vasemmalle (kuvan vasen reuna on aluksi ruudun oikean reunan ulkopuolella, sitten siirtyy sieltä nollaan).

Vastaavasti kun kuva poistuu säiliöstä, se lipuu ikkunan koon verran vasemmalle ruudun ulkopuolelle.

Muita vastaavia animaatiovaihtoehtoja:

app.animation('.slide-down-animation', function ($window) {
 return {
 enter: function (element, done) {
 TweenMax.fromTo(element, 1, {top: -$window.innerHeight}, {top: 0, onComplete: done});
 },
 leave: function (element, done) {
 TweenMax.to(element, 1, {top: $window.innerHeight, onComplete: done});
 }
 };
 });

app.animation('.fade-in-animation', function ($window) {
 return {
 enter: function (element, done) {
 TweenMax.fromTo(element, 1, {opacity: 0}, {opacity: 1, onComplete: done});
 },
 leave: function (element, done) {
 TweenMax.to(element, 1, {opacity: 0, onComplete: done});
 }
 };
});

Mutta tosiaan, ei ole omaa keksintöä, kunhan ihastelen ja sovellan ratkaisua. Angular-animate on hyvin mielenkiintoinen kirjasto ja varovaisen tutustumisen perusteella myös vakaa. Angular-Material tutkinta jatkuu, ei voi sanoa että tässä olisi käytetty sen parhaita piirteitä ja ainakin omalla ruudulla tuo oikean yläkulman fab-nappi on penteleen ruma (sen saa sulautumaan taustaan jos jättää primary luokan pois määrittelystä) – mutta tämä oli itselle enempi tech demo edelleen.

Joka tapauksessa, taas koodia ja linkkivinkkejä itselle muistiin.

 

 

Netbeans 8.1, Angular 1.3 ja Angular-Material

No niin, nyt on mehukas pläjäys päivityksiä. Netbeans 8.1 päivitys tipahti muutama viikko sitten, ja se paransi huomattavasti tukea Angular projektien ja Angular Seedin suhteen. Aiemmat bower pulmat ovat poistuneet – ainakin hetkeksi. Sillä pystyy tekemään hyvän Angular projektirungon siis nyt.

Angular frameworkistä vuorostaan on tullut ulos juuri tuoreeltaan odotettu 1.3 versio. Jatkossa toki luvassa vielä odotetumpi 2.0 versio joka järisyttää kaiken uusiksi. Mutta tämä 1.3 on mukava vakaa pikkupäivitys. Sen saa käyttöön vaikka heti, ja aika monet kirjastot sitä jo vaativatkin.

Lopulta angular-material. Olen kovasti itse ollut kiinnostunut Googlen Polymer/Material kirjastosta muutamasta syystä: Sitä saa niin Androidiin kuin webiinkin, se on selkeästi tablet/phablet/mobile first ui framework, se suuntaa tulevaisuuden selaimiin, ja se on aika siisti. Siinä on selkeä filosofia takana ui-kehitykseen. Ja nyt sitä saa Angulariinkin.

Kun kaikki nämä kolme lyö yksiin, tulee taas aika metka alusta kehitellä tai prototypoida. Se onnistuu esim. näin:

Lataa Netbeans 8.1 tai päivittele se tuoreimpaan versioon:

https://netbeans.org/

Tee sinne Uusi HTML 5 projekti tyyppiä Angular seed:

HTML 5 projektityyppi

Angular Seed pohja

Angular Seed antaa mukavat pohjat kehitykselle: Siellä on jo npm, ja bower mukana.

Seuraava tempaus on mennä Important Files-kansioon, editoimaan bower.json tiedostoa. Ideana on muutella siellä Angular-versiot 1.2.x versiosta versioon 1.3.x. Samalla lisää sinne myös angular-material 0.4.x, sekä angular-animate 1.3.x. Voit myös muokata projektin nimen, kuvauksen, version ja kotisivulinkin makusi mukaan. Esim- näin:

{
 "name": "Material Design Demo",
 "description": "Angular Material Design demo",
 "version": "0.0.1",
 "homepage": "https://spagettikoodi.wordpress.com",
 "license": "MIT",
 "private": true,
 "dependencies": {
 "angular": "1.3.x",
 "angular-route": "1.3.x",
 "angular-loader": "1.3.x",
 "angular-mocks": "~1.3.x",
 "angular-animate": "~1.3.x",
 "html5-boilerplate": "~4.3.0",
 "angular-material": "~0.5.x"
 },
 "resolutions": {
 "angular": "1.3.x",
 "angular-animate": "1.3.x",
 "angular-aria": "1.3.x"
 }
 
}

Huomaa tuossa myös resolutions-osa, ilman sitä saat mehukkaan angular not found errorin aikaiseksi, kun bowerille jää epäselväksi mitä versiota Angularista tulisi käytellä.

Nyt kun koko roska on valmis, aja npm install ja bower install, ja haluamasi kirjastot lataillaan interwebistä koneelle. Jos Javascriptin jumalat suovat. Taisin aiemmin kirjaillakin metkoista ongelmista npm työkalusta windows 2012 koneissa suhteessa npm central registryyn. Tätä kirjoittaessa ne ovat edelleen olemassa, täältä suomen kamaralta suunnattuna.

Npm and Bower run tools

Angular-Material kirjasto vaatii index-sivulle muutaman linkin toimiakseen: tarvitset linkit javascript-tiedostoihin ja tyylisivuihin. Ne kannattaa laittaa olemassaolevien kaveriksi, ja huomioida myös miten aiempien linkkien polut bower-components kansioon toimivat. Mutta tähän tapaan:

<!DOCTYPE html>
<!--[if lt IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html lang="en" ng-app="myApp" class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html lang="en" ng-app="myApp" class="no-js"> <!--<![endif]-->
 <head>
 <meta charset="utf-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <title>My AngularJS App</title>
 <meta name="description" content="">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
 <link rel="stylesheet" href="/bower_components/angular-material/angular-material.css">
 <link rel="stylesheet" href="/bower_components/angular-material/themes/default-theme.css">
 <link rel="stylesheet" href="bower_components/html5-boilerplate/css/normalize.css">
 <link rel="stylesheet" href="bower_components/html5-boilerplate/css/main.css">
 <link rel="stylesheet" href="app.css">
 <script src="bower_components/html5-boilerplate/js/vendor/modernizr-2.6.2.min.js"></script>
 </head>
 <body>
 <ul class="menu">
 <li><a href="#/view1">view1</a></li>
 <li><a href="#/view2">view2</a></li>
 </ul>

<!--[if lt IE 9]>

 <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
 <![endif]-->
<div ng-view></div>
<div>Angular seed app: v<span app-version></span></div>
<!-- In production use:
 <script src="//ajax.googleapis.com/ajax/libs/angularjs/x.x.x/angular.min.js"></script>
 -->
 <script src="bower_components/angular/angular.js"></script>
 <script src="/bower_components/angular-aria/angular-aria.js"></script>
 <script src="/bower_components/angular-animate/angular-animate.js"></script>
 <script src="/bower_components/hammerjs/hammer.js"></script>
 <script src="/bower_components/angular-material/angular-material.js"></script> 
 <script src="bower_components/angular-route/angular-route.js"></script>
 <script src="app.js"></script>
 <script src="view1/view1.js"></script>
 <script src="view2/view2.js"></script>
 <script src="components/version/version.js"></script>
 <script src="components/version/version-directive.js"></script>
 <script src="components/version/interpolate-filter.js"></script>
 </body>
</html>

Editoi app.js tiedostoa, lisää sinne material design moduuli niin saat myös funktiot esim. modaali-ikkunoille käyttöön:

’use strict’;

// Declare app level module which depends on views, and components
angular.module('myApp', [
 'ngRoute',
 'ngMaterial',
 'myApp.view1',
 'myApp.view2',
 'myApp.version'
]).
config(['$routeProvider', function($routeProvider) {
 $routeProvider.otherwise({redirectTo: '/view1'});
}]);

Lopuksi, jos haluat heti nähdä toimivatko linkit suht oikein, avaa view1/view1.html tiedosto, ja lisää sinne material-design nappi.

<p>This is the partial for view 1.</p>
<md-button class="md-fab md-raised">PUSH ME</md-button>

Huomaa pari seikkaa:

– Paketissa tulee mukana myös angular-aria ja hammerjs, nekin mielenkiintoisia kirjastoja tabsujen ja kämmyköiden kanssa.

– Näissä linkeissä on viittaus angular/material kansion alla olevaan themes/default-theme.css tiedostoon, se tulee angular-material 0.5:sta alkaen mukana.

Jos haluat, voit nyt ajaa sovelluksen ja tarkistaa että virheilmoituksia ei tule ja nappi tulee näkyviin. Se tapahtuu Netbeansissä yksinkertaisesti oikealla hiirennapilla projektin päällä:

Run app

 

First material design button

 

Huomaa material design napin onhover efekti ja onclick ripple-efekti. Siinä esimakua mitä on luvassa.

No niin, se siitä. Nyt sinulla on Netbeans 8.1, AngularJS 1.3, sekä angular-material asennettuna, ja hupi voi alkaa. Mitäpä tällä kombolla voi tehdä? Kirjoitan siitä toisen artikkelin myöhemmin.

Tässä muutama asiaan liittyvä linkkivinkki:

https://github.com/angular/angular.js/blob/master/CHANGELOG.md

https://material.angularjs.org/#/

https://github.com/angular/bower-material

 

Polymer + AngularJS helposti: Yeoman

Olen pyöritelly vähän Polymeriä koska se kiinnostaa kovasti. Kyseessä on siis uuden sukupolven JavaScript-ui kirjasto jossa pääjuju on komponentisointi, uudelleenkäyttö, laajennettavuus – mutta vaihteeksi UI puolella.

Ongelmana on että se ei ole yksinään kovin vahva – se keskittyy vain yhteen asiaan eli komponentteihin. Frameworkit kuten Angular ovat suosittuja koska sieltä löytyy järeästi arkkitehtuuria tarpeeseen kuin tarpeeseen.

Olen käytellyt Yeomania aiemminkin, mutta aika ajoin huonoin kokemuksin. Nyt päätin testata sitä tässä yhteydessä vielä kerran, ja vaihteeksi pelittää hienosti. Tässä tarinaa vähän elementeistä ja kokemuksista.

Pohjalle koneeseen on syytä asentaa Node.js ja npm, niiden avulla pystyy sitten asentamaan kaiken muun. Vakityökalut mitä asennan tyypillisesti ensiksi, ovat esim. bower ja grunt, ja tässä yhteydessä yeoman. Ne voi asentaa globaalisti esim. näin:

npm install -g bower grunt yo

Nyt pitäisi komentoriviltä onnistua em. komennot, esim. bower, yo, grunt. Jos ei onnistu, todennäköisesti poluissa on vikaa. Esim. omassa Windows 8 koneessani piti lisätä PATH perään c:\users\munprofiili\AppData\Roaming\npm kansio johon nämä oletuksena tuikataan. Ja jo pelittää.

Seuraavat ohjeet on apinoitu suoraan lähteestä http://www.html5rocks.com/en/tutorials/webcomponents/yeoman/

Asenna polymer generaattori näin:

npm install -g generator-polymer

Seuraavaksi tehdään kansio, ajetaan generaattori, ja käynnistetään serveri:

mkdir MunPolymeeri

cd MunPolymeeri

yo polymer

(Tässä välissä voi vastata että juujuu, core ja paper kaikki mukaan vain!)

grunt server

Ja jos menee yhtä sulavasti kuin mulla, niin käynnistyy livereload serveri, joka avaa selaimen, ja lataa Angular+Polymer softarungon. Tämä on siitä metka että sitä voi haluamallaan tekstieditorilla muokkailla ja samantien kun tallentaa, se lataa muutokset ruudulle.

Olen pyöritellyt muutamia mukavia editoreita. Kaupallisella puolen varmaan IntelliJ WebStorm on kovimmasta päästä – mutta se on omaan makuuni joko turhan raskas/jäykkä/rajoittunut tai sitten en vain osaa käyttää sitä. Pidän kuitenkin keveämmistä, nopeammista ja notkeammista välineistä. Tähän asti olen käyttänyt ihan Notepad++ editoria, sopivin plugarein varustettuna. Mutta nyt aloin innostumaan Sublime Text -editorista, ja se pysyy testissä kunnes osaan sanoa onko se parempi vai huonompi kuin notepad++. Ominaisuuksia on ainakin enempi, tosin rahaa pitäisi pistää peliin jos alkaa tosissaan käyttämään. Siinä on aina pieni kynnys, onneksi kokeilemaan pääsee ilmaiseksi. Myönnettäköön myös, että Sublimen lisenssimalli on perin reilu.

No niin, siinä on toimiva softarunko pyörimässä, ja meillä on editori. Mitäs seuraavaksi? 😉

 

Linkkivinkkejä:

http://www.sublimetext.com/

http://www.jetbrains.com/webstorm/

http://notepad-plus-plus.org/

Angular End-To-End testing ja Protractor, kuvaruutukaappaukset

Olen testaillut Angular-koodia reippaasti Protractorilla, ja taannoin tuli ajankohtaiseksi pohtia miten voi tarkemmin tutkia miksi joku protractor test case epäonnistuu. Tässä muutama vinkki siihen:

Protractoriin voi asentaa kuvaruutukaappaus-ohjelman, näin:

npm install protractor-screenshot-reporter

Sen voi säätää makunsa mukaan ottamaan kuvia aina, tekemään niistä raportin, tai ottamaan vain epäonnistuneista testeistä otoksia, näin:

 

var ScreenShotReporter = require('protractor-screenshot-reporter');

exports.config = {
  // your config here ...

  onPrepare: function() {
    // Add a screenshot reporter and store screenshots to `/tmp/screenshots`:
    jasmine.getEnv().addReporter(new ScreenShotReporter({
       baseDirectory: '/tmp/screenshots',
      takeScreenShotsForSkippedSpecs: true,
      takeScreenShotsOnlyForFailedSpecs: true
   }));
  }
}

Tuo plug-in ottaa kuvia vain testin päätteeksi. Voit myös ottaa omia kaappauksia mistä vaiheista vain, tähän tapaan:

var fs = require('fs');

function writeScreenShot(data, filename) {
 var stream = fs.createWriteStream(filename);
 stream.write(new Buffer(data, 'base64'));
 stream.end();
}

browser.takeScreenshot().then(function(png) {
 writeScreenShot(png, 'exception.png');
});

Jos haluat päästä käsiksi selaimen logeihin, tässä koodia siihen:

browser.manage().logs().get('browser').then(function(browserLogs) {
  // browserLogs is an array of objects with level and message fields
  browserLogs.forEach(function(log) {
    if (log.level.value > 900) { // it's an error log
      console.log('Browser console error!');
      console.log(log.message);
    }
  });
});

Pitäisiko testissä odotella hetki? Onnistuu ProTractorissa, näin:

browser.sleep(5000);

Pitäisikö selain läväyttää koko ruudun kokoiseksi? Onnistuu, näin:

browser.driver.manage().window().maximize();

Näin voit poistaa evästeet – esim. ennen kirjautumista:

 // delete all cookies
 browser.manage().deleteAllCookies();

Kenties hyvä idea kääntää jquery animaatiot pois päältä testauksen ajaksi – välttääksesi viiveitä esim. navigaatiopalkkien esiintulossa tms:

 // disable jQuery animation effects
 browser.driver.executeScript("$.fx.off = true;");

Onko muita hyviä Protractor tai JavaScript e2e-testausvinkkejä?