Friday, March 13, 2015

Developing without the backend: Working with 3rd Party Scripts

In my last post Developing without the backend. Simple Web Socket development., I talked about how to work offline with a simple Web Socket server so you can write your front-end in peace.  That is cool, but what if I have to deal with a 3rd party JS library that requires access to the Internet, login info to their site, actually running on their site, etc.  Essentially anything that requires you to run outside of your development environment.

So how do you deal with these 3rd party JavaScript API's using angular and work without their backend?  What if you need the cloud and you are offline?  What do you do?
Not that kind of cloud.  
Angular has the built in capability to use this pattern when necessary to add, remove, or completely replace the functionality of a provider by using the $provide.decorator function.

Angular has 2 major phases.  A run phase where it is actually executing the code, and a config phase where it wires up your application.  The decorator function comes in handy during the config phase that runs before the run phase.  In this phase you can access the $provider service, as well as built-in providers for your services, factories, etc.  So first you want to create a service wrapper for the 3rd party library that you are using.  Whether or not you just return the 3rd party object or create wrappers for all the functions, I'll leave up to you, but this will work for either method.  Next, you can do is create a config for your module (don't worry you can have multiple config calls) that uses the $provide.decorator function to overwrite or completely replace the service we just created.  For more information on these take a look at these posts on angular dependency injection and extending the $q method.  They are excellent reads.

I created a sample on jsFiddle to show you how it will work.  In this example I will use the underscore library in my application and then use the function above to overwrite one of the functions.  Outside of this tutorial, the next step would be to update your grunt (or whatever build) tasks to remove the mock config file from your project during the build process so you would actually use the real library.

To start I will create a simple service to wrap the underscore library, so I can write better unit tests and develop disconnected.

factory('underscoreService', function(){
    return _;
})

No magic there.  Next I will refer to that service in a controller object and call a couple of methods on it.

controller('ctrl' ,['$scope', 'underscoreService', function ($scope, underscoreService){
    $scope.list = ['Mercury', 'Venus', 'Earth', 'Mars', 
                   'Jupiter', 'Saturn', 'Uranus', 
                   'Neptune', 'Pluto'];
    
    $scope.sampleList = underscoreService.sample($scope.list, 3);   
    $scope.shuffleList = underscoreService.shuffle($scope.list);

}])

No magic here.  I have a list that creates 2 additional lists with method calls from underscore.  Now I want to swap out the logic from the service before it ever gets used.  This is where the $provider.decorator comes in. I will overwrite the logic for shuffle with a function that just returns the array passed in.

config(['$provide', function($provide){
    // override the function. you can uncomment it if you just want it to shuffle
    $provide.decorator('underscoreService', function($delegate){
        $delegate.shuffle = function(list){return list};
        return $delegate;
    });
}]

In the decorator function you receive a parameter called "$delegate" which is the return value from the service, factory, etc, before it gets injected anywhere.  So you can either overwrite the methods there, or you could return your own object instead!!!1

Here is the end result:


Just remember to update your build process to remove the config file!
Cheers!!

No comments:

Post a Comment