Skip to content

Commit ffb83db

Browse files
committed
Lesson 9: Developing Web Applications
1 parent e827336 commit ffb83db

File tree

139 files changed

+21010
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

139 files changed

+21010
-0
lines changed

livelessons-web/README.adoc

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
:compat-mode:
2+
= Lesson 9: Developing Web Applications
3+
4+
_Basics of developing web applications to consume your Microservices._
5+
6+
- link:livelessons-web-resources[Resources]
7+
- link:livelessons-web-resources-angular[Resources (Angular)]
8+
- link:livelessons-web-templates[Templating]
9+
- link:livelessons-web-transforms[Transforms]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
:compat-mode:
2+
= Lesson 9: Developing Web Applications (Resources)
3+
4+
== Introduction
5+
Takes https://scotch.io/tutorials/creating-a-single-page-todo-app-with-node-and-angular[a popular TODO tutorial]
6+
and serves it from Spring Boot.
7+
8+
== Building and running the sample
9+
Use the following commands to build run the application:
10+
11+
```
12+
$ mvn clean package
13+
$ java -jar target/livelessons-web-resources-angular-1.0.0-SNAPSHOT.jar
14+
```
15+
16+
== Understanding the code
17+
Angular and Bootstrap are served from WebJars and the application JavaScript source is
18+
contained in `src/main/resources/static`.
19+
20+
This example also shows how you can call a rest service from Angular. This is fairly
21+
typical in a microservice where the rich web application calls a REST edge service which
22+
ultimately coordinates different microservices. In this example we just call a dummy
23+
endpoint which has been mocked up in the `ToDoController` class.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) {{{year}}} {{{fullname}}}
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>livelessons</groupId>
7+
<artifactId>livelessons-web</artifactId>
8+
<version>1.0.0-SNAPSHOT</version>
9+
</parent>
10+
<artifactId>livelessons-web-resources-angular</artifactId>
11+
<properties>
12+
<main.basedir>../..</main.basedir>
13+
</properties>
14+
<dependencies>
15+
<dependency>
16+
<groupId>org.springframework.boot</groupId>
17+
<artifactId>spring-boot-starter-web</artifactId>
18+
</dependency>
19+
<dependency>
20+
<groupId>org.webjars</groupId>
21+
<artifactId>angularjs</artifactId>
22+
<version>1.3.11</version>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.webjars</groupId>
26+
<artifactId>bootstrap</artifactId>
27+
<version>3.3.2</version>
28+
</dependency>
29+
<dependency>
30+
<groupId>org.webjars</groupId>
31+
<artifactId>font-awesome</artifactId>
32+
<version>4.3.0-1</version>
33+
</dependency>
34+
</dependencies>
35+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package demo;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
6+
public class ToDo {
7+
8+
@JsonProperty("_id")
9+
private int id;
10+
11+
private final String text;
12+
13+
@JsonCreator
14+
public ToDo(@JsonProperty("text") String text) {
15+
this.text = text;
16+
}
17+
18+
private ToDo(String text, int id) {
19+
this.text = text;
20+
this.id = id;
21+
}
22+
23+
public int getId() {
24+
return id;
25+
}
26+
27+
public String getText() {
28+
return this.text;
29+
}
30+
31+
public ToDo withId(int id) {
32+
return new ToDo(this.text, id);
33+
}
34+
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package demo;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
import org.springframework.web.bind.annotation.PathVariable;
7+
import org.springframework.web.bind.annotation.RequestBody;
8+
import org.springframework.web.bind.annotation.RequestMapping;
9+
import org.springframework.web.bind.annotation.RequestMethod;
10+
import org.springframework.web.bind.annotation.RestController;
11+
12+
/**
13+
* Sample REST controller just for demonstration purposes. NOTE: this code is not thread
14+
* safe so will not work if multiple clients are connected. In a real example this might
15+
* be an edge service (e.g. using zuul).
16+
*/
17+
@RestController
18+
@RequestMapping("/api/todos")
19+
public class ToDoController {
20+
21+
private List<ToDo> todos = new ArrayList<>();
22+
23+
private int id;
24+
25+
@RequestMapping(method = RequestMethod.GET)
26+
public List<ToDo> get() {
27+
return this.todos;
28+
}
29+
30+
@RequestMapping(method = RequestMethod.POST)
31+
public List<ToDo> post(@RequestBody ToDo todo) {
32+
this.todos.add(todo.withId(this.id++));
33+
return this.todos;
34+
}
35+
36+
@RequestMapping(value = "{id}", method = RequestMethod.DELETE)
37+
public List<ToDo> delete(@PathVariable int id) {
38+
this.todos.removeIf(todo -> todo.getId() == id);
39+
return this.todos;
40+
}
41+
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package demo;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class WebResourcesRestApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(WebResourcesRestApplication.class, args);
11+
}
12+
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<!doctype html>
2+
3+
<!-- ASSIGN OUR ANGULAR MODULE -->
4+
<html ng-app="scotchTodo">
5+
<head>
6+
<!-- META -->
7+
<meta charset="utf-8">
8+
<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->
9+
10+
<title>Node/Angular Todo App</title>
11+
12+
<!-- SCROLLS -->
13+
<link rel="stylesheet" href="/webjars/bootstrap/3.3.2/css/bootstrap.min.css"><!-- load bootstrap -->
14+
<link rel="stylesheet" href="/webjars/font-awesome/4.3.0/css/font-awesome.min.css">
15+
<style>
16+
html { overflow-y:scroll; }
17+
body { padding-top:50px; }
18+
#todo-list { margin-bottom:30px; }
19+
#todo-form { margin-bottom:50px; }
20+
</style>
21+
22+
<!-- SPELLS -->
23+
<script src="/webjars/angularjs/1.3.11/angular.js"></script><!-- load angular -->
24+
25+
<script src="js/controllers/main.js"></script> <!-- load up our controller -->
26+
<script src="js/services/todos.js"></script> <!-- load our todo service -->
27+
<script src="js/core.js"></script> <!-- load our main application -->
28+
29+
</head>
30+
<!-- SET THE CONTROLLER -->
31+
<body ng-controller="mainController">
32+
<div class="container">
33+
34+
<!-- HEADER AND TODO COUNT -->
35+
<div class="jumbotron text-center">
36+
<h1>I'm a Todo-aholic <span class="label label-info">{{ todos.length }}</span></h1>
37+
</div>
38+
39+
<!-- TODO LIST -->
40+
<div id="todo-list" class="row">
41+
<div class="col-sm-4 col-sm-offset-4">
42+
43+
<!-- LOOP OVER THE TODOS IN $scope.todos -->
44+
<div class="checkbox" ng-repeat="todo in todos">
45+
<label>
46+
<input type="checkbox" ng-click="deleteTodo(todo._id)"> {{ todo.text }}
47+
</label>
48+
</div>
49+
50+
<p class="text-center" ng-show="loading">
51+
<span class="fa fa-spinner fa-spin fa-3x"></span>
52+
</p>
53+
54+
</div>
55+
</div>
56+
57+
<!-- FORM TO CREATE TODOS -->
58+
<div id="todo-form" class="row">
59+
<div class="col-sm-8 col-sm-offset-2 text-center">
60+
<form>
61+
<div class="form-group">
62+
63+
<!-- BIND THIS VALUE TO formData.text IN ANGULAR -->
64+
<input type="text" class="form-control input-lg text-center" placeholder="I want to buy a puppy that will love me forever" ng-model="formData.text">
65+
</div>
66+
67+
<!-- createToDo() WILL CREATE NEW TODOS -->
68+
<button type="submit" class="btn btn-primary btn-lg" ng-click="createTodo()">Add</button>
69+
</form>
70+
</div>
71+
</div>
72+
</div>
73+
74+
</body>
75+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
angular.module('todoController', [])
2+
3+
// inject the Todo service factory into our controller
4+
.controller('mainController', ['$scope','$http','Todos', function($scope, $http, Todos) {
5+
$scope.formData = {};
6+
$scope.loading = true;
7+
8+
// GET =====================================================================
9+
// when landing on the page, get all todos and show them
10+
// use the service to get all the todos
11+
Todos.get()
12+
.success(function(data) {
13+
$scope.todos = data;
14+
$scope.loading = false;
15+
});
16+
17+
// CREATE ==================================================================
18+
// when submitting the add form, send the text to the node API
19+
$scope.createTodo = function() {
20+
21+
// validate the formData to make sure that something is there
22+
// if form is empty, nothing will happen
23+
if ($scope.formData.text != undefined) {
24+
$scope.loading = true;
25+
26+
// call the create function from our service (returns a promise object)
27+
Todos.create($scope.formData)
28+
29+
// if successful creation, call our get function to get all the new todos
30+
.success(function(data) {
31+
$scope.loading = false;
32+
$scope.formData = {}; // clear the form so our user is ready to enter another
33+
$scope.todos = data; // assign our new list of todos
34+
});
35+
}
36+
};
37+
38+
// DELETE ==================================================================
39+
// delete a todo after checking it
40+
$scope.deleteTodo = function(id) {
41+
$scope.loading = true;
42+
43+
Todos.delete(id)
44+
// if successful creation, call our get function to get all the new todos
45+
.success(function(data) {
46+
$scope.loading = false;
47+
$scope.todos = data; // assign our new list of todos
48+
});
49+
};
50+
}]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
angular.module('scotchTodo', ['todoController', 'todoService']);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
angular.module('todoService', [])
2+
3+
// super simple service
4+
// each function returns a promise object
5+
.factory('Todos', ['$http',function($http) {
6+
return {
7+
get : function() {
8+
return $http.get('/api/todos');
9+
},
10+
create : function(todoData) {
11+
return $http.post('/api/todos', todoData);
12+
},
13+
delete : function(id) {
14+
return $http.delete('/api/todos/' + id);
15+
}
16+
}
17+
}]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"directory" : "src/main/resources/static/lib"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
:compat-mode:
2+
= Lesson 9: Developing Web Applications (Resources)
3+
4+
== Introduction
5+
Shows how you can serve static resources from a Spring Boot application.
6+
7+
== Building and running the sample
8+
Use the following commands to build run the application:
9+
10+
```
11+
$ mvn clean package
12+
$ java -jar target/livelessons-web-resources-1.0.0-SNAPSHOT.jar
13+
```
14+
15+
== Understanding the code
16+
The `src/main/resources` folder includes `public`, `resources` and `static` subfolders
17+
which can all be used to serve content.
18+
19+
=== WebJars
20+
This example also shows how it is possible to serve JavaScript and CSS resources from
21+
http://webjars.org[webjars]. This is automatically configured by Spring Boot so all
22+
you need to do is declare WebJar dependencies in your `pom.xml`
23+
24+
=== Bower
25+
If you need something more complicated that WebJars you might consider a full JavaScript
26+
build-chain. This example includes an example using the popular Bower dependency manager.
27+
To regenerate the bower sources type the following (assuming that you have installed
28+
bower):
29+
30+
```
31+
$ bower install
32+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "livelessons-web-resources",
3+
"version": "1.0.0.SNAPSHOT",
4+
"private": true,
5+
"ignore": [
6+
"**/.*",
7+
"node_modules",
8+
"bower_components",
9+
"test",
10+
"tests"
11+
],
12+
"dependencies": {
13+
"jquery": "~2.1.3"
14+
}
15+
}

0 commit comments

Comments
 (0)