The Optimistic Approach

Client-side basket/catalog relations and server-side syncronization

The Optimistic Approach - Demo

The Optimistic Approach - Concept

The Optimistic Approach - Implementation

// Basket update events
pubsub.publish('update:basket', [product, 1]);
pubsub.publish('update:basket', [product, -1]);
pubsub.publish('update:basket', [product, 4]);
pubsub.publish('update:basket', [product, -7]);

// Basket update handler
updateBasketHandler: function (product, delta) {
    queue.push({
        id: product.id,
        delta: delta
    });
    basket.push(product);
    syncBasket();
}

The Optimistic Approach - Implementation

// Syncronize basket to server
syncBasket: _.debounce(function () {
    if (!queue.length) {
        return;
    }
    $http.put('/api/basket', {queue: queue}).succes(function (data) {
        if (!queue.length) {
            pubsub.publish('synced:basket', [data.basket]);
        }
    });
    queue.length = 0;
}, 300)

The Optimistic Approach - Error Handling

The Optimistic Approach - UX Considerations

Transition Aware Routing

Catalog with deeplinking, history steps, scroll-positioning and transitions

Transition Aware Routing - Demo

Transition Aware Routing - Implementation

// Transition object and actions based on that
scope.$on("$routeChangeSuccess", function (event, current, previous) {
    transition.toPage = previous.params.page !== current.params.page;
    transition.toCategory = previous.params.category !== current.params.category;
    //...
});


if(transition.toPage){
    $window.scrollTo(0,0);
}
if(transition.toCategory){
    element.addClass('anim-catalog-fade');
}
//...

Transition Aware Routing - Implementation

// Utilizing promises to handle async environment
var deferred = $q.defer(),
    data = $http.get( { params: request } ),
    animationDelay = $timeout(angular.noop, Modernizr.cssanimations ? 300 : 0);

$q.all([data, animationDelay]).then(function(results) {
    deferred.resolve(results);
});

return deferred.promise;

Continuous Load & Async Pages

Continuously load and maintain results across multiple pages

Continuous Load & Async Pages - Demo

Continuous Load & Async Pages - Implementation

$scope.model.pageChunks = [];
$scope.addPageChunk = function(){
    var filters = catalog.getFilters();
    catalog.search({params: filters}).success(function(data){
        $scope.model.pageChunks.push(data.products);
    });
};


$window.bind('scroll', function (event) {
    var fold = $window.height() + $window.scrollTop(),
        offSetTop = element.offset().top;
    if (offSetTop < fold) {
        $scope.addPageChunk();
    }
});

Continuous Load & Async Pages - Implementation

// Set scroll position after rendering is done by using a timeout
// This pushes it to the end of the ui thread - except when it doesn't

if(previousScrollPosition){
    $timeout(function(){
        $window.scrollTo(0, previousScrollPosition);
    }, 10);
}


Http Interception

Keep session in sync with response headers

Http Interception - Demo

Http Interception - Implementation






Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-User: 541e115963974884bbb27eaeb2d1805e
X-Powered-By: ServiceStack/3,938 Win32NT/.NET
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET

Http Interception - Implementation

// Push http interceptors using config
.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.responseInterceptors.push('userContextInterceptor');
}])


var clientUserContext = currentContext.userId,
    serverUserContext = response.headers()['x-user'];

if(clientUserContext !== serverUserContext){
    window.location.reload(true);
}

Any Questions?

jsallthethings.com/talks/irma10