wtorek, 11 listopada 2014

Angular 2.0 - bardziej szczegółowo

Przekopałem się wreszcie przez drafty opublikowane do momentu rozpoczęcia pisania tego posta.  Trzeba dodać, że te dokumenty zmieniają się każdego dnia, a nawet co kilka godzin.  Rzeczy czasami powtarzają, ujawniają się pewne sprzeczności, widać intensywną wymianę poglądów w komentarzach.  Postarałem się wybrać z tej całej masy pomysłów, rozważań, opinii, wywodów najbardziej kluczowe zagadnienia i przedstawić je tutaj skrótowo, próbując uporządkować spływające informacje.

 

Ogólne założenia

  • modularność - Angular 2.0 jako zbiór luźnych modułów (deweloper może wybrać, co chce z frameworka użyć)
  • unobtrusive - jak najwięcej naturalności, jak najmniej API Angulara w kodzie dewelopera
  • możliwość zmiany zachowania przez dewelopera - np. służy temu odseparowanie modelu metadanych od wykrywania metadanych. Domyślnie są to adnotacje na klasach i jej składowych, ale są też inne sposoby. Każdy serwis korzystający z metadanych (np. compiler, DI, router) powinien wspierać delegat do wykrywania metadanych. Domyślna implementacja pobierałaby dane z adnotacji, ale własna implementacja mogłaby pobierać je w dowolny sposób.

 

ES6+

Adnotacje – przykład:

@NgDirective('[ng-bind]')

class NgBind {

      @Inject([Element])

       constructor(element) {

              this.element = element

       }

      @NgMapExpression('ng-bind')

       setText(value) {

              this.element.textContent = value;

       }

}

  • Instancjonowanie klasy kiedy atrybut ng-bind jest w szablonie HTML
  • Przekazywanie podczas tworzenia bieżącego elementu DOM
  • Wywoływanie setText po każdej zmianie wyrażenia w atrybucie ng-bind

Typy runtime = kontrakty.

Asercje runtime

  • wyniki zwracane przez funkcje innych bibliotek
  • JSON zwracany przez serwer

Ta sama adnotacja/składnia dla deklaracji typu dla statycznego sprawdzania powinna zostać użyta dla generowania asercji runtime.

function concat(a:string, b:string) {

       return a+b;

}

function concat(a:string, b:string) {

       assert(typeof a == 'string');

       assert(typeof b == 'string');

       return a+b;

}

Podobne do "guards" z ES7.  Można wyłączyć w produkcji dla większej wydajności, przydatne dla dewelopera i w testach.

 

Dependency injection

Usunięcie konfiguracji modułu w dotychczasowej postaci.

Usunięcie globalnej rejestracji modułu:

var m1 = angular.module();
var m2 = angular.module();
angular.bootstrap([m1, m2, …], document.body);

Nie będzie już automatycznego bootstrapowania. Drugi parametr opcjonalny.

Do tej pory wszystko było singletonem.

Integracja z modułami ES6:

http.js:

http.$inject = ['$q'];
export function $http(q) {}

main.js:

module http from 'http';
angular.bootstrap(element, [http]);

 

Dyrektywy

Trzy rodzaje:

Dyrektywa komponent

Szablon + izolowany kontekst wykonania dla niego, coś jak dyrektywy z izolowanym scope w 1.x z szablonem. Zawsze izolowany kontekst wykonania, nie ma miejsca na szablon, izolacja DOM / wyrażeń /css.  Służy do budowania UI aplikacji. Przechowuje dane aplikacji i jej logikę, jak również html template.

Enkapsuluje logikę w javascript, szablon html i opcjonalnie style css w łatwo reużywalny komponent. Izoluje wyrażenia w szablonie, drzewo DOM i css od reszty aplikacji i dostarcza dobrze zdefiniowany interfejs. Ma swój, nowy kontekst wykonania dla wszystkich wyrażeń w szablonie. Mogą one mieć dostęp tylko do propercji i funkcji, które są w instancji dyrektywy.

Izolacja DOM i CSS: dyrektywy komponent używają Shadow DOM, aby inne dyrektywy nie modyfikowały jej zawartości. Zdarzenia z elementów w Shadow DOM nie wypływają na zewnątrz.

Interfejs dla komponentu:

  • właściwości i zdarzenia
  • właściwości CSS (szablony używane przez komponent mogą zmienić jego stylowanie w myśl Shadow DOM)
  • child fragments: szablony, których używa komponent mogą dostarczać elementów dzieci, które mogą być wstawione przy pomocy znacznika <content> z Shadow DOM. Wyrażenia na tych wstawianych elementach są powiązane z zewnętrznym kontekstem poza szablonem (nie są w kontekście wykonywania komponentu, podobnie jak w transclusion w 1.x)

import {ng} from '...';

 

@ng.ComponentDirective(

        select: 'pane',

        template: ng.inline('<div>{{title}}</div><content/>'),

        css: ng.url('pane.css'),

)

class sample.Pane {

        constructor() {}

        set title() { … }

}

Bardzo często strona html będzie mieć komponent na znaczniku <body>.

Dyrektywa szablon

Instancjonuje szablony i dodaje je do DOM przez ViewPort, w 1.x odpowiednikiem tego są dyrektywy z transclude. Może, ale nie musi utworzyć kontekst wykonywania, ma miejsce na szablon, nie ma izolacji.

Używanie znacznika <template>

  • dla niestandardowych elementów
  • przy wielu elementach root
  • określenie porządku przy zagnieżdżaniu się template dyrektyw

<template ng-repeat>

        <div>...</div>

</template>

Tylko jedna dyrektywa template jest dozwolona w znaczniku <template>. Kilka dyrektyw template możemy dodać do danego elementu html zagnieżdżając wokół niego znaczniki <template>:

<template ng-repeat>

      <template ng-if>

           <div>...</div>

      </template>

</template>

Dyrektywy template mogą też być używane na zwykłych elementach. Niejawnie zostaną one skonwertowane na template.

<ul>

       <li ng-repeat>

           <span ng-if>...</span>

       </li>

</ul>

jest skrótem do:

<ul>

       <template ng-repeat>

           <li>

              <template ng-if>

                   <span>...</span>

              </template>

         </li>

     </template>

</ul>

W Angular 1.x jest faza linkowania i kompilacji w dyrektywach z uwagi na szablony, w Angular 2.0 mamy do tego osobne dyrektywy template.

Dyrektywa dekorator

Rozszerzenie istniejących elementów o dodatkową funkcjonalność, w 1.x są to dyrektywy bez transclude, bez własnego scope i szablonu. Nie mają kontekstu wykonania, miejsca na szablon, izolacji.  Zachowania, zwykle w postaci atrybutów.

Ograniczenia

  • tworzy nowe elementy i może z nimi wszystko zrobić
  • może zmieniać tylko atrybuty w elementach, których nie utworzyła

 

Dyrektywy używają klas ES6 i adnotacji do definicji meta danych.

Adnotacje do dyrektyw mają następujące właściwości:

  • selector: string - selektor css to jakich elementów odnosi się dyrektywa (bez child i pseudo-klas)
  • events: string[] - lista zdarzeń wyrzucanych przez dyrektywę, można je obsłużyć przy pomocy atrybutów on-...
  • visibility ('local' | 'direct-children' | 'any-children') - które dyrektywy w poddrzewie elementu mają dostęp do instancji dyrektywy
  • microsyntax: string - definicja składni dla specjalnych właściwości i jak części tych propercji mapują się na inne propercje

Dyrektywa dla każdego elementu z atrybutem ng-show:

import {ng} from '...';

@ng.DecoratorDirective(selector: '[ng-show]')

class NgShowDirective {

}

Wartości parametrów konstruktora przez DI:

@ng.DecoratorDirective(selector: '[ng-show]')

class NgShowDirective {

       @Inject(window.HTMLElement, ng.Http, ng.SomeOtherDirective)

       constructor(element, http, someOtherDirective) { … }

}

Kontener może się odwoływać do swoich dzieci, a dzieci mogą się odwoływać do kontenera  (kto pamięta czasy jak takie założenia były głoszone przy okazji wykładu z attached properties w XAML?). Nie odbywa się to przez data binding, ale poprzez DI. Brak wsparcia dla zdarzeń scope z Angular 1.x.  Dyrektywa może poprosić o wstryknięcie dyrektyw innego rodzaju przez swój konstruktor. Możemy też określić, które dyrektywy w poddrzewie elementu mają dostęp do instancji dyrektywy:

<form>

       <input type="text" ng-model="user.value" required>

</form>

 

import {ng} from '...';

 

@ng.DecoratorDirective(

      selector: 'form',

      visibility: 'subtree'

)

class NgFormDirective {

      constructor() { … }

}

 

@ng.DecoratorDirective(

      selector: 'input[type=text]',

      visibility: 'local'

)

class NgModelDirective {

      @Inject(NgFormDirective)

       constructor(ngForm) { … }

}

 

@ng.DecoratorDirective(selector: 'input[type=text]')

class NgInputDirective {

      @Inject(NgModelDirective)

      constructor(ngModel) { … }

}

 

Cykl życia:

Każda dyrektywa może zaimplementować specjalne funkcje

  • attach - wywoływane raz po tym jak wszystkie settery są wywoływane przez odpowiadające im bindingi w szablonach
  • detach - gdy element dyrektywy jest niszczony

Dyrektywy komponent mogą zaimplementować funkcje

  • templateLoaded - wywoływane, gdy szablon komponentu zostanie załadowany i dodany do elementu komponentu

 

Zdarzenia:

Dyrektywy mogą odpalać niestandardowe zdarzenia przy użyciu HTMLElement.dispatchEvent i nasłuchiwać zdarzeń przy użyciu HTMLElement.addEventListener

Dyrektywy muszą deklarować wszystkie zdarzenia, które generują przy pomocy adnotacji.

@ng.DecoratorDirective(

        selector: 'dialog',

        events: ['close']

)

class Dialog {

        @Inject(window.HTMLElement)

        constructor(element) { ... }

 

        close() {

              var evt = new Event('close');

              this.element.dispatchEvent(evt);

              if (!evt.defaultPrevented()) {

                  //...

              }

        }

}

 

Szablony

Składnia

Binding do propercji:

<div [property-name]="expression">

Zparametryzowany binding do propercji (aktualnie tylko 1 parametr, ale może się to zmienić):

<div [ng-repeat|person]="people">

Interpolacja - może być umieszczona w atrybutach lub tagach tekstowych, postawienie na interpolację z ES6 zamiast na starą '{{}}

<div title="${title}">Hello ${name}</div>

co jest skrótem do:

<div [title]="title|stringify" [text|2]="name|stringify">
Hello <!--${name}--> </div>

Inny przykład:

<dialog title="Hello ${user}">

skrótem do:

<dialog [title]="'Hello ' + (user|stringify)">

Obsługa zdarzenia (handler wywoływany tylko gdy źródłem eventu jest dokładnie ten element, nie jego dzieci)

<button (click)="doSomething()">click me</button>

Handler zdarzenia wspierający bubbling  (w czasach jak zdarzyło się wykładać XAML były osoby co mówiły, że w HTML też są takie zdarzenia):

<div (^click)="doSomithing">
      <img src="..."><span>text</span>
</div>

Wywołanie metody na innym elemencie (w XAML mamy element binding,  od Blend 3 SDK wprowadzono zachowania, triggery i akcje i możliwość ich delegacji, kto pamięta dziś słynne przykłady z kontaktem i żarówką?)

<input #login-element type=text>
<button (click)="loginElement.focus()">

HTML import używany jest dla ładowania szablonów dla komponentów - ułatwia rozwiązywanie relatywnych ścieżek do powiązanych z szablonem zasobów np. stylów CSS.

Angular może załadować klasę komponentu na podstawie adresu szablonu, a także szablon na podstawie klasy komponentu:

<template ng-config="some-component">
       <some-component></some-component>
</template>

Ładowanie komponentu na podstawie taga w szablonie nie-Angulara

<link type="import" href="some-component.html">
<some-component></some-component>

Nie modyfikujemy struktury DOM bezpośrednio, zamiast tego używamy abstrakcji View / ViewPort / ViewFactory

Możliwość wydajnego zrealizowania translude bez Shadow DOM.

 

Data binding

Domyślne bindowanie do propercji zamiast do atrybutów

  • wszystkie atrybuty natywnych elementów mają odpowiadające im propercje
  • binding wykryje, które atrybuty nie mają odpowiadającej im propercji i zbinduje wtedy do atrybutu
  • lista mappingów między atrybutami i propercjami dla lepszej czytelności szablonu
  • niektóre propercje elementów nie mają atrybutu, ale łatwo się je powinno używać (np. indeterminate w checkbox-ach)
  • propercje elementów dla logicznych atrybutów mają zawsze wartość, chcemy rozróżnić bindowanie do logicznych atrybutów - dodawanie/usuwanie od bindowania do nielogicznych - ustawianie wartości
  • atrybuty są zawsze typu string, propercje mogą mieć dowolna wartość

Kontekstem wykonania każdy obiekt JS (dla komponentów instancja komponentu).

Wspierane będą dotychczas używane rodzaje bindingu w 1.x, także one-time.

Przy błędach w wyrażeniach będą wyrzucane błędy ?

Wszystkie propercje dyrektyw mogą być bindowane. Binding możemy skonfigurować przez opcjonalną adnotację @ng.PropertySet:

  • trigger - kiedy i z czym setter powinien być wywoływany
    • reference - kiedy zmienia się referencja, przekazywanie w referencji (domyślnie)
    • collection - jeśli zmianie uległ element kolekcji, przekazanie do kolekcji
    • deferred - przekazywanie do kompilowanego wyrażenia, do późniejszego wykorzystania przez dyrektywę
  • domOnly: setter zmienia tylko DOM, ale nie właściwość, która jest obserwowana
    • będzie wołane tylko raz podczas fazy zapisywania DOM
    • wartość domyślna: false

@ng.DecoratorDirective {

        selector: 'dialog'

}

class Dialog {

        constructor() {

             this._content = null;

        }

       get content(content) {

             return this._content;

       }

      @ng.PropertySet({domOnly: true})

      set content(content) {...}

}

<dialog content=”{{1+2}}”>

Microsyntax dla właściwości:

@TemplateDirective(

     selector: '[ng-repeat]'

     microsyntax: {

           'ng-repeat': '$item-name in $collection [track by $track-by]'

     })
class NgRepeat {


     @ng.PropertySet({trigger: 'reference'})

      set ngRepeatItemName() { … }


     @ng.PropertySet({trigger: 'collection'})

      set ngRepeatCollection() { … }

     

      @ng.PropertySet({trigger: 'deferred'})

      set ngRepeatTrackByFn;
}

Możliwe sposoby użycia:

<div ng-repeat="item in items track-by item.id">

<div ng-repeat-item-name="item" ng-repeat-collection="items" ng-repeat-track-by="item.id">

 

Atrybuty używamy tylko do inicjowania, bindujemy do propercji, co pozwala nam obsłużyć atrybuty logiczne, a także wartości, które nie mogą być zamieniane na string. Dzięki temu zmniejsza się też ilość potrzebnych dyrektyw do data bindingu w stosunku do 1.x. Nie potrzebujemy:

  • ng-bind - [text-content]
  • ng-bind-html - [inner-html]
  • ng-bind-template - text-content={{foo}} (-> [text-content] = "foo | stringify"
  • ng-class - [class], [class-list]
  • ng-hide, ng-show - [hidden]
  • ng-href - [href] = "wyrażenie z adresem"
  • ng-disabled - [disabled]

Binding nie powinien odróżniać natywnych elementów i web komponentów. Aby sprawić, by bindowanie do web komponentu zachodziło w odpowiednim momencie potrzebujemy escapować atrybuty w HTML.  Natywna obsługa zdarzeń typu onclick dostarcza problemy związane z bezpieczeństwem i globalnym kontekstem wykonania. Dlatego większość frameworków oferuje własne mechanizmy. W Angular 1.x mamy ng-click, jednak nie jest to skalowalne rozwiązanie na zdarzenia o dowolnej nazwach, jakie mogą być w web komponentach. Powinniśmy kodować nazwę zdarzenia podobnie do atrybutów, ale tak by je od nich odróżnić. Stąd się wzięły [] przy propercjach i () przy nazwach zdarzeń (większość głosów na takie oznaczenia).

Dyrektywy template takie jak ng-repeat, ng-if, ng-switch mogą mieć specjalną składnię lub taką jak inne dyrektywy. Mikroskładnia dla prostych rzeczy jest bardziej naturalna od kilku atrybutów czy parametryzowania ich, ale jest też trudniejsza dla narzędzi IDE do prawidłowego interpretowania. Dla pętli ng-repeat większość głosów poszła na:

<div [ng-repeat|person]="people">{{person}}</div>

Rob (nasz człowiek, to znaczy taki co zna XAML) ma obiekcje, co do braku wyrażania kierunkowości w składni data bindingu. Poza tym nie uważa, że szczegóły systemu szablonów powinny być scalane z data bindingiem. Nie jest zwolennikiem dodawania rzeczy do nazwy atrybutu. Wołałby mikroskładnię. Kompromisem może być zdefiniowanie języka do wyrażania opcji data bindingu tj. lokalna zmienna, kierunkowość, triggery itp.

 

Web komponenty

Web Components - zbiór standardów:

var Greeter = Object.create(HTMLElement.prototype, {

        name: {

              get: function() { return this.getAttribute('name'); },

              set: function(value) { this.setAttribute('name', value); }

        },

        greet: function() { ... }

});

document.registerElement('x-greeter', { prototype: Greeter });

 

var div = document.createElement('div');

div.innerHTML = '<greeter>';

var greeter = div.firstChild;

expect(greeter instanceOf Greeter).toBe(true);

greeter.name = 'world';

greeter.greet();

 

Angular może korzystać z web komponentów napisanych w inny sposób, z drugiej strony inne frameworki mogą korzystać z komponentów napisanych w Angularze.

Angular umożliwia eksport swoich komponentów jako web komponenty jak również import web komponentów jako komponentów Angular.

Każda aplikacja JS może utworzyć komponenty Angular, mieć dostęp do właściwości i obsługiwać zdarzenia w taki sam sposób jak do elementu DOM.

Angular może użytkować każdy niestandardowy element tak jak natywne elementy DOM.

Jeśli API Node.bind jest dostępne, data binding Angulara zintegruje się z nim.

Dyrektywy Angulara nie są web komponentami domyślnie. Tylko komponenty, które są reużywalne w innych aplikacjach powinny być wyesportowane jako web komponenty.

Web komponenty powinny być rejestrowane zanim wystartuje zasadnicza aplikacja w Angular. Jest tutaj pewien problem, ponieważ rejestracja, a zwłaszcza import odbywa się asynchronicznie.

Specyfikacja web komponentów nie określa jak można się dowiadywać o zmianach w stanie komponentu. Angular zakłada tutaj, że będzie to mechanizm zdarzeń, jako najbardziej naturalny. Proponowana składnia:

<x-prompt on-close="doSomething()">

Czy wartość zostanie zapisana do propercji czy do atrybutu będzie zależeć od heurystyki Angulara.

Przykład komponentu, gdzie wartości muszą być wyliczone w innym kontekście niż samego komponentu:

class MyApp {

       constructor() {

           this.name = 'World';

       }

       greet() {

           alert('Hello ' + this.name + '!');

       }

}

<div>

     <x-prompt ng-id="prompt"

          title="Your name?"

          bind-content="name"

          on-accepted="greet()"></x-prompt>

     <button on-click="prompt.open()">Greet</button>

</div>

Jak Angular nasłuchuje zmian?

Zmiany w DOM mogą być wykrywane za pomocą DOM Mutation Observers. Detekcja zmian w propercjach obiektów DOM jest znacznie trudniejsza, ponieważ są one proxy do natywnych obiektów (C++). Odczytytywanie propercji wymaga przejścia z VM do natywnego kodu, co jest wolne. Object.observe nie wspiera tego także z tego powodu (działa tylko dla propercji JavaScript). Jedyną opcją pozostaje słuchanie zdarzeń. Angular nie wie, które zdarzenia są powiązane z jakimi propercjami. Mapowania pomiędzy propercjami a zdarzeniami dla Angulara rozwiązują problem dla elementów natywnych i web komponentów.

Udostępnianie komponentów Angular jako web komponentów.

@ComponentDirective({

       selector: 'x-zippy'

       template: ...

})

class ZippyComponent {

      constructor(events) {

            this.events;

      }

 

     @Property('title')

     get title() ...;

     set title(value) ...;

 

     @Property('isOpen')

     get isOpen() ...;

     set isOpen(value) {

         if (value != this.isOpen)

                this.toggle();

     }

 

     @Publish('open')

     open() {

           this.isOpen = false;

           this.events.fire('open');

     }

 

     @Publish('close')

     close() {

           this.isOpen = true;

           this.events.fire('close');

      }

 

      @Publish('toggle')

       toggle() {

           value ? close() : open();

       }

}

<x-zippy title="abc">

     aaa

</x-tab>

Adnotacje @Property i @Publish powodują, że Angular udostępnia propercje i metody elementu tak, żeby były dostępne dla innych elementów bez żadnej wiedzy o Angularze. Elementy eksportowane z różnych dyrektyw są mergowane do jednego elementu.

Tak wyeksportowane komponenty mogą być poza scopem Angulara.

var injector = angular.createInjector(setOfModules);

var customElementFactory = injector.get(CustomElementFactory);

var Greeter = customElementFactory(GreeterDirective);

document.registerElement('x-greeter', { prototype: Greeter });

var div = document.createElement('div');

div.innerHTML = '<greeter>';

var greeter = div.firstChild;

expect(greeter instanceOf Greeter).toBe(true);

greeter.name = 'world';

greeter.greet();

Angular dostarczy CustomElementFactory, która stworzy fasadę pomiędzy API registerElement, a wnętrzami Angulara.

 

Natywne promises

Wsparcie i uzupełnienie pojawiających się w przeglądarkach natywnych promise.

Przykład z samym promise:

function doneCounting(val) {
       alert('Counted to ' + val);
       return val;
}

function couldNotCount(reason) {
       alert('Could not count');
       return reason;
}

function resolver(resolve, reject) {
       setTimeout(function doneCounting() {
         resolve(5);
      }, 5000);
}

var countToFive = new Promise(resolver);
countToFive.then(doneCounting, couldNotCount);

Angular zaproponuje tu dodatkowy obiekt Deferred dla łatwiejszego korzystania:

import {Deferred} from 'ngDeferred';

UserService.prototype.fetch = function () {
        var service = this;
        var deferred = new Deferred();
        var xhr = new XMLHttpRequest();
        xhr.open('get', '/users');
        xhr.send();
        xhr.onload = function (e) {
        if (e) {
             service.users = [];
             deferred.reject(e);
        }
       else {
            service.users = JSON.parse(this.responseText).users;
            deferred.resolve(service.users);
       }
};
return deferred.promise;

 

Dotykowe animacje i zdarzenia

Obsługa urządzeń dotykowych

  • implementacja jako natywne cechy przeglądarek (przynajmniej ich większości)
  • słuchanie zdarzeń dotykowych i odpowiednio szybka zmiana położenia elementu przy użyciu transformat na urządzeniach
  • wydajność: najszybciej jak się da (celem jest 60+ FPS)

Obecne implementacje karuzeli, infinity scrolling ... mają mało wspólnego podejścia. Większość implementacji nie używa natywnych zdarzeń do scrollowania (starsze przeglądarki i niektóre bieżące nie obsługują ich dobrze), przez co nie osiagamy optymalnej wydajności.

Rozważane rzeczy do implementacji:

  • integracja zdarzenia beforescroll w Chrome
  • fake scrolling - scrollowanie z nasłuchiwaniem zdarzeń dotykowych i przesuwaniem zawartości przez zmianę transform (element.style.transform)
  • scrollowanie z inercją
  • wirtualne scrollowanie (scrollowanie infinity jest jego szczególnym przypadkiem)
  • pull to refresh
  • karuzela
  • swipe to delete
  • sticky header
  • parallax scrolling

Platformy:

  • desktop: Chrome, Safari, IE10+, Firefox
  • Android: 2.3, 4-4.x, Chrome, Firefox
  • iOS: Safari (iOS6, iOS7)
  • Windows Phone 8: IE 10+

Scrollowanie z inercją - propozycje implementacji

  • Desktop: zdarzenia scroll, inercja zależy od urządzenia wejścia
  • iOS6+: nie wyrzuca zdarzenia scroll podczas inercji, workaround: wyłączenie natywnej implementacji, ręczna implementacja inercji przy użyciu transformat CSS, ręczne wyrzucanie w pętli requestAnimationFrame zdarzenia scroll w zależności od pozycji tranzycji
  • Android:
    • 2.3+: nie ma wsparcia dla scrollowania, fake scrolling
    • Chrome, Firefox: wyrzucanie zdarzeń jak to jest spodziewane
  • IE10 mobile - wyrzucanie zdarzeń jak to jest spodziewane

Snap points

Strategie:

  • 1 - modyfikacja inercji tak by scrollowanie kończyło się w snap point (możliwe, kiedy inercja jest kontrolowalna)
  • 2 - czekanie aż scrollowanie się zakończy i wtedy wykonanie tranzycji CSS do nowej pozycji

Implementacje:

  • Desktop: strategia 2
  • iOS6+: strategia 1 (inercja fake)
  • Android: 2.3+ strategia 1 (fake scrolling), chrome - strategia 2
  • IE10+: strategia 1 (natywna właściwość CSS -ms-scroll-snap-points)

Planowane API:

  • klasy CSS
  • zdarzenia
  • scollowanie: opakowanie zawartości w div (łatwe dla fake scrollingu)

Przykład:

<div class="scroll">

       <div class="content">

           ...

          <img class="snap">

           ...

       </div>

</div>

Dyrektywy dla Angular:

  • integracja z ng-view (strony będą mogły być swipe-owane)
  • wirtualne scrollowanie będzie łatwe do użycia tak jak ngRepeat
  • dyrektywy panelu dla aplikacji mobilnych do pokazywania menu

 

Dane - przesyłanie, persystencja, odpytywanie, tryb offline

Scenariusze

  • offline & synchronizacja
  • w zależności od połączenia z siecią lub jego rodzaju zróżnicowane zachowanie aplikacji
  • praca zespołowa, informacje o działaniach innych osób
  • zachowywanie stanu strony (po ponownym otworzeniu przeglądarki, przeładowaniu strony)
  • synchronizacja w czasie rzeczywistym jak w grach z więcej niż jednym graczem

Ważne obiekty (w różnych nowych modułach)

  • $http - podobny do starego, ale pisany w ES6,  mają pojawić się nowe metody tj.  $http.getBlob(), $http.getArrayBuffer()), rozważane API wspierające obiekt FormData
  • $resource - zostanie zastąpiony przez bardziej wysokopoziomową klasę Model, która dodatkowo ma wspierać stronicowanie, odpytywanie zasobów HTTP (także relacje)
  • $localDB - wysokopoziomowy asynchroniczny serwis do dostarczania danych klucz-wartość lub odpytywalnych, asynchroniczny nawet jeśli API $sessionStorage czy $localStorage jest synchroniczne. Przykładowe metody:
    • [get|set|update|unset]()  - wykorzystanie localStorage
    • [get|set|update|unset]Temp()  - wykorzystanie sessionStorage
    • [create|addTo|splice]Collection()
    • [create|addTo|splice]TempCollection()
    • find()
    • findTemp()

                     Niskopoziomowe serwisy:

    • $localStorage  - opakowanie localStorage, by zyskać na getterach/setterach z ES6, mieć prostsze API, walidację wejścia
    • $sessionStorage  - opakowanie sessionStorage  – // -
    • $indexedDB - oprócz bycia indexedDB wrapper na WebSQL w Safari, intensywna współpraca z serwisem Model, który ma wspierać odpytywanie w offline

              Wszystkie serwisy mają wspierać szyfrowanie.

  • $webSocket - nadbudowa na websocket, automatyczne/konfigurowalne ponowne łączenie
  • $serviceWorker - bardziej imperatywny następca app cache z HTML5,  rejestrowanie i instalacja skryptu w dokumencie do intercepcji i zarządzania wszystkimi żądaniami z sieci dla tego dokumentu (lub domeny), może zostać wykorzystany w $http i dependency injection, z uwagi na brak natywnego wsparcia dla service workerów w każdej przeglądarce nie każdy request do sieci będzie mógł być poddany intercepcji
  • $connection - ma dostarczać informacje o bieżącym stanie połączenia sieciowego, a także bardziej dokładnych danych o jakości połączenia (na podstawie czasu pobrania i ilości timeout-ów)
  • $appCache - rejestr zasobów i skryptów do cachowania, obejmujący dokumenty, media, dane i skrypty. Powinien luźno bazować na filozofii HTML5 ApplicationCache, ale z dodaną kontrolą nad dodawaniem zasobów do cache oraz czyszczeniem go. Deweloperzy nadal będą musieli utrzymywać cache manifest do załadowania bazowej strony i załadowania niektórych bibliotek i skryptów offline. DI nie powinno być zależne czy moduł jest pobierany z sieci czy z localCache - $appCache nadpisuje ES6 Module Loader

Rich Data

Widoki powinny bindować zarówno do obiektów JS jak i do instancji Model.

Model

  • klasa ES6
  • potrafi doczytywać porcje danych i je buforować co przyspiesza wyświetlanie dodatkowych danych przez ng-repeat czy dyrektywy do infinite scrolling
  • ograniczony zbiór operacji na danych: limit(), skip(), find(query), findById(), count(), order()
  • odczytywanie strumieni czy danych ulegających ciągłym zmianom
  • niejawny model lokalnie zmieniony nie będzie w zmienionej postaci przenoszony do serwera
  • ma "Widok" i "Cursor" i dostarcza danych w odpowiednim momencie, deweloperzy mniej sami zarządzają pobieraniem danych, cachowaniem, buforowaniem itp.

Szyfrowanie w storage

  • deweloperzy dostarczają jednorazowy klucz do serwisu $localdb per sesja w pamięci
  • możliwość wyboru algorytmu

$localDB.setEncryptionKey(key);

Persystencja danych zakłada inteligentny pre-fetching w zależności od parametrów sieci. Np. pobranie 20 wyników przed daną stroną wyników wyświetlającą 10 pozycji oraz 20 dalszych rekordów.

Ponieważ pojemność storage w przeglądarkach jest ograniczona, założeniem będzie rozsądne zarządzanie danymi przez dewelopera. Bardziej wysokopoziomowe abstrakcje na poziomie frameworka dostarczą inteligetnego zarządzania przestrzenią, dane używane jakiś czas temu będą okresowo usuwane (możliwość konfiguracji danych wygasających automatycznie).

 

Routing

Nawigacja w oparciu o stany, wsparcie dla autentykacji i autoryzacji, opcjonalne zapamiętywanie stanów.  Cały dokument o routingu przekreśloną czcionką ?

Brak komentarzy: