Skip to content

Commit ed43fb1

Browse files
author
Dave Syer
committed
Fill out README a bit
1 parent 28a6b99 commit ed43fb1

File tree

4 files changed

+128
-29
lines changed

4 files changed

+128
-29
lines changed

README.md

Lines changed: 125 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
## Install npm Locally
1+
# Creating a New Application with Spring Boot and Angular
2+
3+
Spring Boot works great as a back end for an Angular application but it can be difficult to get the ball rolling. Most Spring users are comfortable with Java and the tools that are used to create and build the backend server. The front end can be written with plain old JavaScript as long as it is relatively simple, and you are willing to search for the rare examples and tutorials in this style. But these days you are much more likely to find documentation and tutorials that use tools like `Typescript`, `node.js`, `npm` and the Angular CLI.
4+
5+
This article shows you how to do that and keep your Spring Boot roots. Much of the advice would apply equally well to other front end frameworks (anything that can be built using `npm` or similar). We use Maven, but similar tools are available for Gradle users. The goal is to have a single application that has Spring Boot and Angular, that can be built and developed by anyone who has knowledge of either ecosystem, and does not feel awkward or unidiomatic to either.
6+
7+
## Install Npm Locally
8+
9+
Installing `npm` is fraught with issues, including but not limited to how to get it working as part of your build automation. We are going to use the excellent [Maven Frontend Plugin](https://github.com/eirslett/frontend-maven-plugin) from Eirik Sletteberg. The first step is to add it to our `pom.xml`:
210

311
```
412
<build>
@@ -31,26 +39,35 @@
3139
</build>
3240
```
3341

34-
then
42+
and then
3543

3644
```
3745
$ mvn generate-resources
3846
$ ls node*
3947
```
4048

49+
Loads of stuff has been installed in the top level directory. Once the downloaded files are cached in your local Maven repository, it won't take long to run this for every build.
50+
4151
## Install Angular CLI
4252

53+
To build an Angular app these days it really helps to use the CLI provided by the Angular team. We can install it using the `npm` that we just got using the plugin. First create a convenient script to run `npm` from the local installation (in case you have others on your path):
54+
4355
```
4456
$ cat > npm
4557
#!/bin/sh
4658
cd $(dirname $0)
4759
PATH="$PWD/node/":$PATH
4860
node "node/node_modules/npm/bin/npm-cli.js" "$@"
4961
$ chmod +x npm
62+
```
63+
64+
and then run it to install the CLI:
65+
66+
```
5067
$ ./npm install @angular/cli
5168
```
5269

53-
and run `mvn generate-resources` again.
70+
Then create a similar wrapper for the CLI itself, and test it quickly:
5471

5572
```
5673
$ cat > ng
@@ -73,6 +90,8 @@ os: linux x64
7390

7491
## Create an Angular App
7592

93+
The Angular CLI can be used to generate new application scaffolding, as well as other things. It's a useful starting point, but you could at this point grab any existing Angular app and put it in the same place. We want to work with the Angular app in a subdirectory of `src/main`, just to keep the source code tidy and make it look like a Maven build.
94+
7695
Create the app with the CLI and move it to `src/main`:
7796

7897
```
@@ -83,28 +102,25 @@ $ sed -i -e 's,dist,../../../target/classes/static,' src/main/client/.angular-cl
83102
$ mv ng npm src/main/client
84103
```
85104

105+
We discarded the node modules that the CLI installed because we want the frontend plugin to do that work for us in an automated build. We also edited the `.angular-cli.json` (a bit like a `pom.cxml` for the Angular CLI app) to point the output from the ANgular build to a location that will be packaged in our JAR file.
106+
86107
## Building
87108

88-
Add
109+
Add this to the frontend plugin configuration:
89110

90111
```
91-
<configuration>
92-
<workingDirectory>src/main/client</workingDirectory>
93-
</configuration>
112+
<workingDirectory>src/main/client</workingDirectory>
94113
```
95114

96-
and
115+
and add an execution to install the modules used in the application:
97116

98117
```
99-
<execution>
100-
<id>npm-install</id>
101-
<goals>
102-
<goal>npm</goal>
103-
</goals>
104-
<configuration>
105-
<arguments>install</arguments>
106-
</configuration>
107-
</execution>
118+
<execution>
119+
<id>npm-install</id>
120+
<goals>
121+
<goal>npm</goal>
122+
</goals>
123+
</execution>
108124
```
109125

110126
Install the modules again using `mvn generate-resources`.
@@ -135,7 +151,7 @@ os: linux x64
135151
typescript: 2.3.4
136152
```
137153

138-
And the tests work:
154+
At this point, the tests work:
139155

140156
```
141157
$ src/main/client/ng e2e
@@ -151,7 +167,7 @@ Executed 1 of 1 spec SUCCESS in 0.718 sec.
151167
[13:59:48] I/launcher - chrome #01 passed
152168
```
153169

154-
Add
170+
and if you add this as well:
155171

156172
```
157173
<execution>
@@ -165,19 +181,23 @@ Add
165181
</execution>
166182
```
167183

168-
and then the client app will be compiled during the Maven build.
184+
then the client app will be compiled during the Maven build.
185+
186+
## Development Time
169187

170188
You can build continuously with
171189

172190
```
173191
$ src/main/client/ng build --watch
174192
```
175193

176-
Updates are built (quickly) and pushed to `target/classes` where they can be picked up by Spring Boot.
194+
Updates are built (quickly) and pushed to `target/classes` where they can be picked up by Spring Boot. Your IDE might need to be tweaked to pick up the changes automatically (Spring Tool Suite does it out of the box).
195+
196+
That's it really, but we can quickly look into a couple of extra things that will get you off the ground quickly with Spring Boot and Angular.
177197

178198
## Adding Bootstrap
179199

180-
https://medium.com/codingthesmartway-com-blog/using-bootstrap-with-angular-c83c3cee3f4a
200+
You can add basic Twitter Bootstrap features to make the app look a bit less dull (taken from [this blog](https://medium.com/codingthesmartway-com-blog/using-bootstrap-with-angular-c83c3cee3f4a)):
181201

182202
```
183203
$ src/main/client/npm install bootstrap@3 jquery --save
@@ -195,3 +215,86 @@ and update `.angular-cli.json` to add the new content:
195215
"../node_modules/bootstrap/dist/js/bootstrap.min.js"
196216
],
197217
```
218+
219+
## Basic Angular Features
220+
221+
Some basic features are included in the default scaffolding app, including the HTTP client, HTML forms support and navigation using the `Router`. All of them are extremely well documented at [angular.io](https://angular.io), and there are thousands of examples out in the internet of how to use those features.
222+
223+
As an example, lets look at how to add an HTTP Client call, and hook it up to a Spring `@RestController`. In the front end `app-root` component we can add some placeholders for dynamic content:
224+
225+
app.component.html:
226+
```html
227+
<div style="text-align:center"class="container">
228+
<h1>
229+
Welcome {{title}}!
230+
</h1>
231+
<div class="container">
232+
<p>Id: <span>{{data.id}}</span></p>
233+
<p>Message: <span>{{data.content}}</span></p>
234+
</div>
235+
</div>
236+
```
237+
238+
so we are looking for a `data` object in the scope of the component:
239+
240+
app.component.ts:
241+
```javascript
242+
import { Component } from '@angular/core';
243+
import {HttpClient} from '@angular/common/http';
244+
245+
@Component({
246+
selector: 'app-root',
247+
templateUrl: './app.component.html',
248+
styleUrls: ['./app.component.css']
249+
})
250+
export class AppComponent {
251+
title = 'Demo';
252+
data = {};
253+
constructor(private http: HttpClient) {
254+
http.get('resource').subscribe(data => this.data = data);
255+
}
256+
}
257+
```
258+
259+
Notice how the `AppComponent` has an `HttpClient` injected into its constructor. In the module definition we need to import the `HttpClientModule` as well, to enable the dependency injection:
260+
261+
app.module.ts
262+
```javascript
263+
import { BrowserModule } from '@angular/platform-browser';
264+
import { HttpClientModule } from '@angular/common/http';
265+
266+
@NgModule({
267+
declarations: [
268+
AppComponent
269+
],
270+
imports: [
271+
BrowserModule,
272+
HttpClientModule
273+
],
274+
providers: [],
275+
bootstrap: [AppComponent]
276+
})
277+
export class AppModule { }
278+
```
279+
280+
In our Spring Boot application we need to service the `/resource` request and return an object with the right keys for the client:
281+
282+
DemoApplication.java:
283+
```java
284+
@SpringBootApplication
285+
@Controller
286+
public class DemoApplication {
287+
288+
@GetMapping("/resource")
289+
@ResponseBody
290+
public Map<String, Object> home() {
291+
Map<String, Object> model = new HashMap<String, Object>();
292+
model.put("id", UUID.randomUUID().toString());
293+
model.put("content", "Hello World");
294+
return model;
295+
}
296+
297+
...
298+
299+
}
300+
```

pom.xml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,6 @@
6363
<goals>
6464
<goal>npm</goal>
6565
</goals>
66-
<configuration>
67-
<arguments>install</arguments>
68-
</configuration>
6966
</execution>
7067
<execution>
7168
<id>npm-build</id>

src/main/client/src/app/app.component.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
<!--The content below is only a placeholder and can be replaced.-->
2-
<div style="text-align:center">
1+
<div style="text-align:center"class="container">
32
<h1>
43
Welcome {{title}}!
54
</h1>

src/main/client/src/app/app.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import {HttpClient} from '@angular/common/http';
77
styleUrls: ['./app.component.css']
88
})
99
export class AppComponent {
10-
title = 'app';
10+
title = 'Demo';
1111
data = {};
1212
constructor(private http: HttpClient) {
13-
http.get('resource').subscribe(data => {this.data = data;});
13+
http.get('resource').subscribe(data => this.data = data);
1414
}
1515
}

0 commit comments

Comments
 (0)