Creating an AngularJS Service

Partho Sarathi

01 Mar, 2018 · 8 minutes read

  • Share:

Pre-requisites

The first thing you are going to want to do is to start with a seed project. Read my articleon how to quickly get started using a seed project I created and then come back here.

If you completed the How do I start?section and finished executing the init.jssuccessfully, you would have the following things:-

  1. A new project directory with the seed files
  2. An empty GitHub repository for your new project
  3. A local Git repository that has the GitHub repository setup for remote push and pull
  4. Local changes to
    • bower.json– modified
    • build.conf.js– modified
    • package.json– modified
    • src/mx-angular-notify.js– renamed from your-project-name.js

    These changes are the name, description, repository URL & output filename for your project that you provided while executing the init.jsscript.

Project Structure

  • seeder
    • file.js
    • metadata-updater.js
    • repo-creator.js
  • src
    • services
      • mxngNotifyService.js
    • mx-angular-notify.js
  • .eslintrc.js
  • .gitignore
  • bower.json
  • build.conf.js
  • gulpfile.js
  • init.js
  • package.json
  • README.md
  • update-metadata.js

Steps

Open the project folder in your favorite IDE. I prefer Visual Studio Code.

code .

As mentioned in the pre-requisites section, the project now contains changes related to the new project’s name, description, URLs, etc.

Verify the changes and commit them with a message like “Updated package metadata”.

Now do a npm installto download the development dependencies. Install bower, gulp& eslintglobally as the postinstallscript of package.jsonis set to do a bower installfollowed by running the default task of gulp. Gulp itself runs eslintto check your JavaScript files for errors.

After this, you should have a bower_componentsdirectory in your project. Recall from the previous article that this was ignored using .gitignoreas these aren’t files we should commit (Somebody tell this to whoever manages the default ASP .NET template for Visual Studio. I have to go back in Git history, add the wwwroot/libfolder on .gitignore, delete the damn JQuery files and then ammend the commit.)

Open the srcdirectory where all your source JavaScript files will reside. You should a file name mx-angular-notify.jsin there. This is the JS file that declares your AngularJS module.

My naming convention for this file is as follows:-

  • mx – root namespace for all my projects/modules in JavaScript.
  • angular – Since it is an AngularJS module
  • notify – The name of the module

At this moment, the file looks like this:

(function () {
    "use strict";

    angular.module("mx-angular-notify", []);
})();

The entire JavaScript file is wrapped in an annonymous function that is also immediately executed. This is a way to implement namespaces and encapsulation in JavaScript. Read this excellent postby Vivin Paliathto understand how this works.

"use strict";

While JavaScript is a very powerful language, it is pretty easy to write really messy code in it; code that even you wouldn’t understand after a few months.

“Use Strict”; is an insurance that programmer will not use the loose or the bad properties of JavaScript

angular.module("mx-angular-notify", []);

Next, we declare our AngularJS module mx-angular-notify. Again, I am using mxAngularas a prefix to prevent clashes with other AngularJS modules. You can choose anything anything that seems unique to you or if your module is very unique and nobody has published anything similar on Bower you can omit the prefix. But, in this case, there are several toast notification modules available on Bower. My module is more of a wrapper around the toastrmodule that I intend to use across many of my projects.

Speaking of toastr, let’s add it as a dependency in our module.

angular.module("mx-angular-notify", ["toastr"]);

So, the entire file looks like:-

(function () {
        "use strict";

        angular.module("mx-angular-notify", ["toastr"]);
})();

The next step is to add a Bower dependency for the package angular-toastrwhich contains the toastrmodule we need. So, I added the following line to bower.jsonin the dependenciessection.

"angular-toastr": "2.1.1"

The Bower.json file now looks like:-

{
        "name": "mx-angular-notify",
        "description": "AngularJS module for showing toast notifications",
        "version": "1.0.0",
        "homepage": "https://github.com/maxotek/mx-angular-notify",
        "license": "MIT",
        "private": false,
        "dependencies": {
                "angular": "v1.6.9",
                "angular-toastr": "2.1.1"
        }
}

You may have to do a bower installfrom the command line. But, many IDEs automatically download bower dependencies when bower.jsonchanges. You bower_componentsfolder should now have a folder for angular-toastr, our new package.

It is time to add our AngularJS service. But, before that we need to create a directory to hold all of our services (well in this case there is only going to be one, but you get the idea). So create a servicesfolder under src.

Now add our AngularJS service file. We’ll call it mxngNotifyService.js. The naming convention is .js. As per the default ESLintrules set by plugin:angular/johnpapathe name of the file must match that of our service. In order to keep our service name unique across the world, I prefixed it with mxng(my chosen prefix made out of Maxotek and Angular). Just because the file is in the servicesdirectory, we know it is a service so there’s no need for naming conventions like . service.js.

Paste the following code in this file:-

"use strict";

(function () {
    angular
        .module("mx-angular-notify")
        .factory("mxngNotifyService", mxngNotifyService);

    mxngNotifyService.$inject = ["toastr"];

    function mxngNotifyService(toastr) {
        return {
            displaySuccess: displaySuccess,
            displayError: displayError,
            displayWarning: displayWarning,
            displayInfo: displayInfo,
            handleDataError: handleDataError
        };

        function displaySuccess(message) {
            toastr.success(message);
        }

        function displayError(error) {
            if (Array.isArray(error)) {
                error.forEach(function (err) {
                    toastr.error(err);
                });
            } else {
                toastr.error(error, "Error");
            }
        }

        function displayWarning(message) {
            toastr.warning(message);
        }

        function displayInfo(message) {
            toastr.info(message);
        }

        function handleDataError(response) {
            console.log(response);

            if (!response.data)
                return displayError("Unable to connect to API server");

            var error = response.data.message || response.data.error_description;
            if (error)
                displayError(error);
            else
                displayError(response.statusText);
        }
    }
})();

Here, we are registering our service. Again, we use the global prefixing here to avoid clash with AngularJS services we might use from other projects.

angular
        .module("mx-angular-notify")
        .factory("mxngNotifyService", mxngNotifyService);

Next, we are using dependency injection to get a reference to the toastrservice.

mxngNotifyService.$inject = ["toastr"];

function mxngNotifyService(toastr) {

Setting the dependency with $inject and a string reference for toastrhelps us during minification which will rename the toastrargument to the mxngNotifyServicefunction. No matter what it’s name is after minification, it will passed a reference to the toastrservice because the string inside ["toastr"]will not be renamed during minification. This is used by AngularJS framework to pass the correct service.

One thing to note here is that the order of the services must be identical inside $injectand the parameters inside mxngNotifyServiceotherwise you’ll end up with wrong services being passed to your function parameters!!!

I know its a bit of extra work this way. But, you get the benefit of there being no issues during minification which is a must for production.

Next, we expose some functions through our service by returning them in this object:-

return {
        displaySuccess: displaySuccess,
        displayError: displayError,
        displayWarning: displayWarning,
        displayInfo: displayInfo,
        handleDataError: handleDataError
};

I prefer returning the object upfront so that it is clear right at the top, what properties/functions this service is exposing. But, you could return this at the very end as well.

The next section comprises of defining these exposed functions which are just wrappers that I use to handle various errorneous situations with things like HTTP request failures.

function displaySuccess(message) {
        toastr.success(message);
}

function displayError(error) {
        if (Array.isArray(error)) {
                error.forEach(function (err) {
                        toastr.error(err);
                });
        } else {
                toastr.error(error, "Error");
        }
}

function displayWarning(message) {
        toastr.warning(message);
}

function displayInfo(message) {
        toastr.info(message);
}

function handleDataError(response) {
        console.log(response);

        if (!response.data)
                return displayError("Unable to connect to API server");

        var error = response.data.message || response.data.error_description;
        if (error)
                displayError(error);
        else
                displayError(response.statusText);
}

Finally, we enclose our module in the annonymous function that does encapsulation while executing it immediately.

})();

Now, let us commit the changes below with a message like Added notification service

  • bower.json– changed
  • src/mx-angular-notify.js– changed
  • src/services/mxngNotifyService.js– new

Finally, let us update the README.MD file with details of the new project and commit it.

Next go to the terminal and run gulp clean default.

The clean task declared in gulpfile.jswill remove any temporary files generated in the distfolder when we initially ran npm install.

The default task itself just calls the lintand scriptstasks which inspect the JavaScript files for errors, combines them and puts them under the distfolder as: mx-angular-notify.js. Furthermore, the scripttask uglifies this file by renaming locals, removing whitespace, etc and generates the mx-angular-notify.min.jswhich is meant to be used in production. If we did not use the $injectproperty in our code, dependency injection would fail when we tried using the mx-angular-notify.min.jsfile in our projects because the parameter toastrwould be renamed to something obscure. Dependency injection in AngularJS works by matching names.

So, that’s it folks. We have created our AngularJS service.

  • Share:

The cleanest blogging platform


2024 © Maxotek. All rights reserved.