Pre-requisites

The first thing you are going to want to do is to start with a seed project. Read my article on 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.js successfully, 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.js script.

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 install to download the development dependencies. Install bower, gulp & eslint globally as the postinstall script of package.json is set to do a bower install followed by running the default task of gulp. Gulp itself runs eslint to check your JavaScript files for errors.

After this, you should have a bower_components directory in your project. Recall from the previous article that this was ignored using .gitignore as 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/lib folder on .gitignore, delete the damn JQuery files and then ammend the commit.)

Open the src directory where all your source JavaScript files will reside. You should a file name mx-angular-notify.js in 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:

1
2
3
4
5
(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 post by Vivin Paliath to 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 mxAngular as 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 toastr module 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:-

1
2
3
4
5
(function () {
        "use strict";
 
        angular.module("mx-angular-notify", ["toastr"]);})();

The next step is to add a Bower dependency for the package angular-toastr which contains the toastr module we need. So, I added the following line to bower.json in the dependencies section.

"angular-toastr": "2.1.1"

The Bower.json file now looks like:-

1
2
3
4
5
6
7
8
9
10
11
12
{
        "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 install from the command line. But, many IDEs automatically download bower dependencies when bower.json changes. You bower_components folder 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 services folder under src.

Now add our AngularJS service file. We’ll call it mxngNotifyService.js. The naming convention is <your-service>.js. As per the default ESLint rules set by plugin:angular/johnpapa the 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 services directory, we know it is a service so there’s no need for naming conventions like <your-service>.service.js.

Paste the following code in this file:-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
"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 toastr service.

mxngNotifyService.$inject = ["toastr"];
 
function mxngNotifyService(toastr) {

Setting the dependency with $inject and a string reference for toastr helps us during minification which will rename the toastr argument to the mxngNotifyService function. No matter what it’s name is after minification, it will passed a reference to the toastr service 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 $inject and the parameters inside mxngNotifyService otherwise 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.js will remove any temporary files generated in the dist folder when we initially ran npm install.

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

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

No votes yet.
Please wait...