Creating Inheritance in Angular Services

Estimated Reading Time: 12 minutes

Inheritance in JavaScript can be a tricky thing.  Some say you should use plain old prototypical inheritance while others believe we should try to emulate classical inheritance.  Ever since the creation of the class keyword in JavaScript, the latter theory has become much stronger.  Here at InfoTrust, however, we feel sticking with prototypical inheritance is the way to go.  For an explanation, read this series on David Walsh’s blog.

angularjs.jpg

This way of developing relationships includes our work when using the AngularJS front-end framework.  To interact with our API for our product Analyze.ly, we used prototypical inheritance to create a base API service that would hold most of the functionality which would then be inherited by other services for specific endpoints.

To demonstrate this, I will show you how to do this in Angular by making a simple application that grabs a user’s profile, shows that information, and also grabs a random set of public Github Gists and displays three of them.  The completed code can be seen on Bitbucket.  The demo can be seen here. Let’s get to it!

We need to set up our project first.  Create a new directory for this project and inside it create a file called index.html.  Put the following code in our new file.


<!DOCTYPE html>
<html lang="en" ng-app="inheritanceTest">
<head>
    <meta charset="UTF-8">
    <title>Angular Service Inheritance Blog</title>
    <base href="/">
    <style>
    body {
        width: 1000px;
        margin: 0 auto;
        text-align: center;
    }

    [ng:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
      display: none !important;
    }

    img {
        width: 250px;
        height: auto;
    }

    #user, #gists {
        display: inline-block;
        float: left;
        width: 48%;
    }
    </style>
</head>
<body ng-cloak>
    <div ng-controller="fooController">
        <div id="user">
        </div>

        <div id="gists">
        </div>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-rc.0/angular.min.js"></script>
    <script src="app.js"></script>
</body>
</html>

This is just a basic HTML page with some simple styles.  I also have added the markup Angular will use to set up our application.  We have called our application “inheritanceTest” and have added a controller to our main div.  It is called “fooController.”  Lastly, we have included Angular using a CDN and then included our application file.

Now, let’s create our main application file and lay the groundwork for our application.  Create a file called app.js and put the following in it.


(function() {
    'use strict';

    var testModule = angular.module('inheritanceTest', []);

    testModule.controller('fooController', ['$scope',
        function($scope) {
            $scope.user = {};
            $scope.gists = [];
        },
    ]);
})();

This creates our application and then adds a controller to the application.  This controller simply takes in the $scope and initializes some fields on it.  Let’s go ahead and update our index.html template to use these fields.  These fields will be populated from Github.  We can use the Github API documentation to find out the field names that will be returned for each respective call.

{% raw %}


<div id="user">
    <img ng-src="" alt="">
    <h1></h1>
    <h2>Member since: </h2>
</div>

<div id="gists">
    <div class="gist" ng-repeat="gist in gists | limitTo: 3">
        <div data-gist=""></div>
    </div>
</div>

{% endraw %}

Here we have output some user information that comes from the user we stored in $scope.  We also loop through each of the gists and use an Angular filter to limit the amount to just three gists.  When iterating over each gist, we pass the gist ID into a custom directive.  This directive will display the gist by creating an iframe and putting it into the div.  Let’s create this directive in our app.js file.  Most of the code for this directive was repurposed from a gist on Github by tleunen.


testModule.directive('gist', function() {
    return function(scope, elm, attrs) {
        var gistId = attrs.gist;

        var iframe = document.createElement('iframe');
        iframe.setAttribute('width', '100%');
        iframe.setAttribute('frameborder', '0');
        iframe.id = "gist-" + gistId;
        elm[0].appendChild(iframe);

        var iframeHtml = '<html><head><base target="_parent"><style>table{font-size:12px;}</style></head><body onload="parent.document.getElementById('' + iframe.id + '').style.height=document.body.scrollHeight + 'px'"><scr' + 'ipt type="text/javascript" src="https://gist.github.com/' + gistId + '.js"></sc'+'ript></body></html>';

        var doc = iframe.document;
        if (iframe.contentDocument) doc = iframe.contentDocument;
        else if (iframe.contentWindow) doc = iframe.contentWindow.document;

        doc.open();
        doc.writeln(iframeHtml);
        doc.close();
    };
});

Now that we have laid the groundwork for our application, we need to get the data from Github when our controller is loaded.  We can use factories to get this data for us.  To keep from repeating a bunch of code, we can use prototypical inheritance to create a factory that contains the code to get the data and then have factories that inherit this functionality.  Let’s see this in action.


testModule.factory('GithubEndpoint', ['$http', '$q',
    function($http, $q) {
        var rootEndpoint = 'https://api.github.com';

        function makeRequest(method, url, params, data, cache) {
            cache = cache !== false;
            var deferred = $q.defer();

            $http({
                method: method,
                url: url,
                params: params,
                data: data,
                cache: cache,
            })
            .then(function makeRequestSuccess(resp) {
                deferred.resolve(resp.data);
            }, function makeRequestFailed(resp) {
                deferred.reject(resp.data);
            });

            return deferred.promise;
        }

        return {
            endpoint: '/',
            getAll: function(params, cache) {
                return makeRequest('GET', rootEndpoint + this.endpoint, params, null, cache);
            },
            getSingle: function(id, params, cache) {
                return makeRequest('GET', rootEndpoint + this.endpoint + '/' + id, params, null, cache);
            },
        };
    },
]);

The code here is not too complex.  We are setting up a new factory in our application called “GithubEndpoint.”  It will inject into our factory the $http module for ajax and the $q module so we can return promises from our methods.  The factory function first defines the base endpoint for all calls that will be made to the Github API.  We create a method called “makeRequest” that simply makes a request to the endpoint we tell it to go to using the given method and returns a promise that will be resolved or rejected based on the response received from Github.  Lastly, we return an object that contains an endpoint attribute and two methods.  These two methods, as we will see in a minute, can be called by the child factories to make calls to the Github API.  They each just call and return the output of the makeRequest function.  I have kept this code to only making GET requests, but other types of requests could easily be added.  Now let’s see how to create the child factories we will be using to interact with Github.


testModule.factory('GithubUsers', ['GithubEndpoint',
    function(GithubEndpoint) {
        var extended = Object.create(GithubEndpoint);
        extended.endpoint = '/users';
        return extended;
    },
]);

testModule.factory('GithubGists', ['GithubEndpoint',
    function(GithubEndpoint) {
        var extended = Object.create(GithubEndpoint);
        extended.endpoint = '/gists/public';
        return extended;
    },
]);

This code starts out like the GithubEndpoint factory.  We create two factories, GithubUsers and GithubGists.  They each require the GithubEndpoint factory.  The first piece of code in the factory function is key.  We use the Object.create method to create a new object and have its prototype point to the GithubEndpoint object.  We then add an endpoint attribute to the new object and return it.  These endpoints coincide to the endpoints on the Github API.  Now, when we make a request using these factories, they will send requests to the correct endpoint on the Github API.  Let’s see how we would use this in our controller.  

Change the creation of the controller to inject our new API factories.


testModule.controller('fooController', ['$scope', 'GithubUsers', 'GithubGists',
    function($scope, GithubUsers, GithubGists) {

Next, use the factories to get a user’s information and a bunch of random public gists from Github.


GithubUsers.getSingle('searsaw')
    .then(function(res) {
        $scope.user = res;
    });

GithubGists.getAll()
    .then(function(res) {
        $scope.gists = res;
    });

Let’s walk through the first one.  We are calling “getSingle” from the GithubUsers factory and passing it the user ID, “searsaw,” which is my user ID.  Since “getSingle” doesn’t exist on the GithubUsers object directly, it goes up it’s prototype chain until it finds the “getSingle” method on the GithubEndpoint factory and invokes that one.  However, that method uses “this.endpoint.”  “this” in this example refers to our GithubUsers object.  So the value of “this.endpoint” is “/users” not “/”.  That method returns a promise.  So we use “then” to pass a callback to be invoked when the promise is resolved.  Once it is resolved, we save the response to the user object on our $scope.  The same thing happens with GithubGists.getAll.  Once the data is put on our $scope, the view updates automatically to display the information.  It’s really just that simple!

I hope this has clarified prototypical inheritance a bit for you.  Using it can really help keep your code much cleaner and DRY’er.  Give us a shout out in the comments with any questions.

Author

Facebook
Twitter
LinkedIn
Email
Originally Published: January 4, 2016

Subscribe To Our Newsletter

May 12, 2023
Originally published on January 4, 2016

Other Articles You Will Enjoy

Is It Time to Upgrade? 4 Signs Your Organization Needs Google Analytics 4 360

Is It Time to Upgrade? 4 Signs Your Organization Needs Google Analytics 4 360

As VP of Partnerships at InfoTrust, I’ve had the opportunity to talk with hundreds of decision-makers about their interest in upgrading to Google Analytics…

4-minute read
How Does BigQuery Data Import for Google Analytics 4 Differ from Universal Analytics?

How Does BigQuery Data Import for Google Analytics 4 Differ from Universal Analytics?

All Google Analytics 4 (GA4) property owners can now enable ‌data export to BigQuery and start to utilize the raw event data collected on…

2-minute read
Google Tag Best Practices for Google Analytics 4

Google Tag Best Practices for Google Analytics 4

After collaborating with several of my colleagues at InfoTrust including Bryan Lamb, Head of Capabilities, Corey Chapman, Senior Tag Management Engineer, Chinonso Emma-Ebere, Tech…

4-minute read
Tracking User Behavior with Events in Google Analytics 4: Examples and Use Cases

Tracking User Behavior with Events in Google Analytics 4: Examples and Use Cases

So you’ve created your Google Analytics 4 (GA4) properties, created your data stream(s), and followed all the necessary steps to configure your property. Now…

5-minute read
App Install Attribution in Google Analytics 4: What You Need to Know

App Install Attribution in Google Analytics 4: What You Need to Know

App install attribution in Google Analytics for Firebase (GA4) is a feature that helps you understand how users discover and install your app. It…

6-minute read
Leveraging Attribution Models in Google Analytics 4 to Improve Your Marketing Strategy: Tips and Best Practices

Leveraging Attribution Models in Google Analytics 4 to Improve Your Marketing Strategy: Tips and Best Practices

In the dynamic landscape of digital marketing, understanding the customer journey is crucial for optimizing strategies and maximizing ROI. Google Analytics 4 (GA4) introduces…

5-minute read
Leveraging Custom Dimensions and Metrics in Google Analytics 4 for Content Performance Measurement: Best Practices and Real-World Examples

Leveraging Custom Dimensions and Metrics in Google Analytics 4 for Content Performance Measurement: Best Practices and Real-World Examples

In today’s digital landscape where content reigns supreme, understanding how your audience interacts with your content is paramount for success. For news and media…

5-minute read
Predictive Analytics in Google Analytics 4: How to Use Machine Learning to Forecast User Behavior and Outcomes

Predictive Analytics in Google Analytics 4: How to Use Machine Learning to Forecast User Behavior and Outcomes

Google Analytics 4 (GA4) is embracing the power of machine learning by incorporating predictive analytics within the platform so that you can use your…

7-minute read
Advanced Analysis Techniques in Google Analytics 4: How to Use AI-Powered Insights and Predictive Analytics for Effective Marketing

Advanced Analysis Techniques in Google Analytics 4: How to Use AI-Powered Insights and Predictive Analytics for Effective Marketing

AI-powered insights and predictive analytics are revolutionary tools reshaping the modern marketing landscape. These advanced analytics techniques, particularly prominent in Google Analytics 4 (GA4),…

8-minute read

Get Your Assessment

Thank you! We will be in touch with your results soon.
{{ field.placeholder }}
{{ option.name }}

Talk To Us

Talk To Us

Receive Book Updates

Fill out this form to receive email announcements about Crawl, Walk, Run: Advancing Analytics Maturity with Google Marketing Platform. This includes pre-sale dates, official publishing dates, and more.

Search InfoTrust

Leave Us A Review

Leave a review and let us know how we’re doing. Only actual clients, please.