Go back to the table of contents for
Developing a Contact Management Application with Angular 1.5X and Java
When you develop a web application, you have multiple reasons to want to isolate the development of the front side (html, javascript, css) from which one from the back side (logic, persistence) such as ;
– people who develop the two parts are not necessarily the same
– you develop a backend-less application
– business rules and or persistence implementation are not created yet or are not stabilized
– you want to have a fast feedback of the development of your application (very common in any efficient development methodology).
We will see how to achieve that with Angular.
Here is the list of tasks we will perform :
- Presenting the API provided by Angular for HTTP requests mocking
- Adding the required configuration in the Angular application to enable the mocking
- Making a very simple mocking example to check it works
- Configuring the build of the project to include or exclude the mocking facility
- Running the application
- Downloading the source code
Presenting the API provided by Angular for HTTP requests mocking
Angular provides a ready to use API to address this need.
We have indeed $httpBackend, a service in the module ngMockE2E that allows to fake HTTP backend implementation.
The $httpBackend provides functions to mock each kind of request : get, post, delete, etc… and more.
For example :
– whenGET(url, [headers], [keys])
– whenPOST(url, [data], [headers], [keys])
– whenDELETE(url, [headers], [keys])
The $httpBackend provides also a generic function where you specify the suitable method as a parameter : when(method, url, [data], [headers], [keys])
We will favor request-specific functions over the generic function because it makes code more readable.
Adding the required configuration in the Angular application to enable mocking
Where put the code to configure and enable backend mocking ?
We don’t want to configure the http request mocking in app.js which the main goal is bootstraping the angular application because the mocking facility should not be available in production.
A possible solution would be to have two app.js files in the source code: one without the mocking facility and another one with the mocking facility and we could include the suitable file according to the target environment of the build of the WAR.
But we should avoid doing it since it forces us to duplicate a important part of app.js file.
A more suitable way would be to have a distinct angular file that contains the mocking configuration and that we include only in the war for not production environments .
Consequently, we will create a new file : mockServices.js. It will be placed at the same level than app.js in the source code.
How to enable or disable the mocking facility without rebuilding the application ?
When we work in development environment, sometimes we want to call the effective backend to check that the whole chain works as expected but other times we want to call the mocked services because we know that the backend is not ready or not stable enough.
So, in development environment, it would be interesting to not have to build our application each time we want to switch to one of the both modes : mocked backend or real backend.
We will introduce a query parameter to enable the mocked backend. By default, the mocked backend is disabled and if the url contains the text « mock », we enable the mocked backend.
We could as first instructions of mockServices.js do the check on the url in plain JS before loading anything related to backend mocking:
var regexMock = new RegExp("/\?.+mock"); if (!document.URL.match(regexMock)){ return; } |
Here is mockServices.js :
(function() { 'use strict'; //IIFE if (!document.URL.match("/\?mock")){ return; } var myAppDev = angular.module('myContactApp') .config(function($provide){ $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator); }) .run(function ($httpBackend, $http) { }); // run end })(); //END IIFE |
Now as soon as an url contains the character ? and the mock word, the $httpBackend decorator is configured in myContactApp module and for any request we have defined a stubbed result in the run() function , the call to the request will be handled by the mock that will return a defined stubbed result.
We will see it in the next point.
Making a very simple mocking example to check it works
The scenario of mocking is the following : suppose we have not developed yet the backend and we have not think about REST services to expose.
So we want just to check that backend mocking works.
In a some way, it is also the case that we are encountering."backend/test"
is the relative url we will use to check that the backend may be mocked.
Here is the updated version of mockServices.js :
(function() { 'use strict'; //IIFE if (!document.URL.match("/\?mock")){ return; } var myAppDev = angular.module('myContactApp') .config(function($provide){ $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator); }) .run(function ($httpBackend, $http ) { $httpBackend.whenGET(/\.html.*$/).passThrough(); var data = {'value':'test'}; // GET backend/test $httpBackend.whenGET("backend/test").respond(data); // test the mock return $http.get("backend/test") .then( function(response){ var valueFaked = response.data; console.info("mocked backend with value="+valueFaked.value); } ); }); // run end })(); //END IIFE |
The mocking behavior is ready simple to understand.
This call $httpBackend.whenGET("backend/test").respond(data)
records the expected behavior when a GET request is called with the relative url :
"backend/test"
. Instead of sending the request to the specified url and waiting for the response, we want that the variable data be directly returned as the response (so without sending the request to the back end).
data is a very simple json object we defined just before : var data = {'value':'test'}
To test the mock, after defining the mock, we send a htttp GET request to the backend/test URL and we log on the browser console a message where we display the data field retrieved by the request. It all is correctly configuration, we should not have reponse error but a log in the console indicating :
"mocked backend with value=test"
.
Configuring the build of the project to include or exclude the mocking facility
So far we have created the mocking configuration in a separated javascript file. We also have provide the possibility to enable or disable the backend mocking by specifying or don’t specifying in the URL a particular pattern that contains the word mock.
It is very well but a thing is missing if we want to be able to not enable mocking in production mode. Indeed, the mockServices.js file should not be present in the WAR when we build an application that targets the production environment.
Removing manually the file from source code before building or from the WAR after building is a very bad idea because it is error-prone. Besides, the file is referenced in the index.html file.So, a 404 error will be displayed in the client console, it is not clean.
Now, we will explain how we will proceed.
If we have mockServices.js declared in the index.html file like that :
<script src="mockServices.js"></script> |
we could not exclude the resource at compile time. We have to replace this declaration by the use of a variable. We will use the Maven filtering mechanism that does very well the job.
So we add the ${mockServices} Maven variable in the head declaration of the index.html file.
Here is the updated version of index.html :
<!DOCTYPE html> <html ng-app="myContactApp"> <head> <title>Contacts and Groups Management (Angular JS)</title> <style> </style> <!-- JQUERY --> <script src="lib-js/jquery-2.2.3.js"></script> <!-- bootstrap JS v3.3.6 --> <script src="lib-js/bootstrap.js"></script> <!-- bootstrap CSS v3.3.6 > --> <link rel="stylesheet" type="text/css" href="css/bootstrap.css"> <!-- angular lib --> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-messages.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-mocks.js"></script> <!-- angular extension lib --> <script src="lib-js/angular-ui-router.js"></script> <!-- app --> <link rel="stylesheet" type="text/css" href="app.css"> <script src="app.js"></script> <!-- mock services --> ${mockServices} <!-- contact management components --> <script src="components/manageContacts/contactsManagementView.js"></script> <!-- contact creation components --> <script src="components/createContact/contactCreationView.js"></script> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-md-2 border1PxWithNoPadding"> <ul class="nav nav-pills nav-stacked"> <li ui-sref-active="active"><a ui-sref="manageContacts">Manage existing contacts</a></li> <li ui-sref-active="active"><a ui-sref="createContacts">Create a new contact</a></li> </ul> </div> <div class="col-md-10"> <ui-view></ui-view> </div> </div> </div> </body> </html> |
When you use Spring Boot, you may want to keep our application compatible with different modes of packaging and deploying (packaged in a WAR, packaged in a JAR or runnable from a embedded server). To achieve it, you should avoid sticking to some very specific practices from the 3 ways. For example, according to Spring Boot documentation :
Do not use the src/main/webapp directory if your application will be packaged as a jar. Although this directory is a common standard, it will only work with war packaging and it will be silently ignored by most build tools if you generate a jar.
That’s why static resources are declared in src/main/resources/static since according to the documentation it is one of directories to use for serving static content :
By default Spring Boot will serve static content from a directory called /static (or /public or /resources or /META-INF/resources) in the classpath or from the root of the ServletContext. It uses the ResourceHttpRequestHandler from Spring MVC so you can modify that behavior by adding your own WebMvcConfigurerAdapter and overriding the addResourceHandlers method.
In the pom.xml of the webapp module, we have overriden the Maven Resources Plugin default configuration to enable the filtering of resources but also to exclude mockServices.js of the resources processed during the process-resources phase of the lifecyle.
The resources filtering allows to index.html file to have its ${mockServices} Maven properties filled with the expected value.
In the default build of the pom, we handle the case for no development build.
So, we set the mockServices property with a empty value and we configure the Maven Resources Plugin to exclude mockServices.js :
. . . <properties> <mockServices></mockServices> </properties> . . . <build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <excludes> <exclude>static/mockServices.js</exclude> </excludes> </resource> </resources> . . . </build> |
In the Maven dev profile, we handle the case for development build.
So, we set the mockServices property with the script declaration of mockServices.js and we configure the Maven Resources Plugin with default configuration that performs no exclusion of files. So mockServices.js will be processed.
<profiles> <profile> <properties> <mockServices><script src="mockServices.js"></script></mockServices> </properties> <id>dev</id> <build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> . . . </build> </profile> </profiles> |
It is not a WordPress issue (I say it because I have many issues with code formatting…). The value of the mockServices Maven property cannot use directly characters considered as illegal in XML. So we replace them by XML entities. Here is the full pom.xml of the web module :
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>contact-webapp-task3</artifactId> <packaging>war</packaging> <parent> <groupId>davidhxxx.example.rest.springbootangular</groupId> <artifactId>contact-parent-task3</artifactId> <version>1.0-SNAPSHOT</version> </parent> <dependencies> <!-- Spring dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-actuator</artifactId> </dependency> </dependencies> <properties> <mockServices></mockServices> </properties> <build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <excludes> <exclude>static/mockServices.js</exclude> </excludes> </resource> </resources> <plugins> <!-- ignore web.xml missing since not required --> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.0.0</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <!-- Required if we want to run without profile --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${springboot.version}</version> </plugin> </plugins> </build> <profiles> <profile> <id>dev</id> <properties> <mockServices><script src="mockServices.js"></script></mockServices> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <defaultGoal>clean spring-boot:run</defaultGoal> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${springboot.version}</version> <configuration> <!-- <addResources>true</addResources> --> <jvmarguments> -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 </jvmarguments> <profiles> <profile>dev</profile> </profiles> </configuration> </plugin> </plugins> </build> </profile> </profiles> </project> |
We could now test the application
Running the application
- By including the mocking facility to the application
From the command line, under the contact-webapp directory, execute the mvn -Pdev command to include the mock facility to the application.
Visit any page with the mocking backend disabled. For example : http://localhost:8095/contact-webapp/#/manageContacts
Look the the console of the browser : you don’t see any log referring mocking .
Now, visit the the page with the mocking backend enabled. For example : http://localhost:8095/contact-webapp/#/manageContacts?anyString&mock
Look the the console of the browser : you see the expected log.
- By excluding the mocking facility to the application
From the command line, under the contact-webapp directory, execute the mvn clean spring-boot:run command to exclude the mock facility to the application.
Visit any page with the mocking backend disabled. For example : http://localhost:8095/contact-webapp/#/manageContacts
Look the the console of the browser : you don’t see any log referring mocking .
Now, visit the the page with the mocking backend enabled. For example : http://localhost:8095/contact-webapp/#/manageContacts?anyString&mock
Look the the console of the browser : you don’t see any log referring mocking either
If you look into the source of the page you will see that the mockServices.js angular script is not included in the index.html page :
Downloading the source code
You can download the source code here :
[sdm_download id= »2318″ fancy= »1″]
Next part : Implementing client-side the contact creation (4/9)