That’s official! JSweet allows using Java to write Angular 2 applications. It even was presented at JavaOne in the following talk:
In his presentation, Kito presents a sample JAX-RS client-server application using the PrimeNG component framework for Angular2. The code is available here: server and client. And the presentation’s slides are available here.
Following Kito’s example, in my post, I will go through a small overview on how to write your first Angular 2 application with Java. Note that I am using also the PrimeNG components. The full code is available here. A simpler template is available here. Follow the readme of these projects to get started by yourself.
Most Angular 2 quickstart projects derive from this cool Angular 2 quickstart project. This project uses npm
to configure full Angular 2 project with all the nice commands, and especially live browser reload. Simply use npm install
to configure all the project and npm start
to compile the TypeScript code within the app
folder and run it in a browser with live reload (tsc
is run with the watch mode, so that all the changes will automatically be compiled to JavaScript and reloaded in the browser).
The main application configuration is made in the systemjs.config.js file. The important part is the package
section, which defines the entry point for your application, here the app/Globals.js
file, which is expected to bootstrap the Angular 2 system and run the application.
In order to enable JSweet on this project, we simply convert it to a Java Maven project, and put all the Java source code in the src/main/java
directory. Then, JSweet is configured to generate the TypeScript source files in the app
directory.
Here is the JSweet configuration taken from the pom.xml
file.
[...] <plugin> <groupId>org.jsweet</groupId> <artifactId>jsweet-maven-plugin</artifactId> <version>1.2.0-SNAPSHOT</version> <configuration> <tsOut>.</tsOut> <module>commonjs</module> <tsOnly>true</tsOnly> </configuration> [...]
Here are the meanings of the given options:
tsOut
: tells JSweet to generate the TypeScript files in the root directory of the project, which means that the app
package will be generated in the app
directory.commonjs
: tells JSweet to use the Node module format (mandatory for Angular2). With this option, each Java file will become a commonjs module.tsOnly
: tells JSweet to only generate the TypeScript source code (no JavaScript files). The TypeScript compiler (tsc) will then generate the JavaScript file (in watch mode for live reload).Of course, you will also need to include the dependencies to the Angular2 candy (and eventually to other JS libraries you will be using). Check out the JSweet candies page for more information.
All you need to do then is to generate all the TypeScript files is mvn generate-sources
. When developing under Eclipse, you can also use the JSweet Eclipse plugin and have live reload of the application each time you save a modification in your Java source code.
The app/Globals.java
file contains the application’s entry point (by convention, in JSweet, Globals
classes are erased during the generation and hold global functions, variables, or code):
package app; import static def.angular.platform_browser_dynamic.Globals.platformBrowserDynamic; public class Globals { static { platformBrowserDynamic().bootstrapModule(AppModule.class); } }
And the app/AppModule.java
is defined as:
package app; import app.cars.CarService; import def.angular.core.NgModule; import def.angular.forms.FormsModule; import def.angular.http.HttpModule; import def.angular.platform_browser.BrowserModule; import def.primeng.primeng.ButtonModule; import def.primeng.primeng.DataTableModule; import def.primeng.primeng.DialogModule; import def.primeng.primeng.InputTextModule; @NgModule( // imports = { BrowserModule.class, FormsModule.class, HttpModule.class, InputTextModule.class, DataTableModule.class, ButtonModule.class, DialogModule.class }, // declarations = { AppComponent.class }, // bootstrap = { AppComponent.class }, // providers = { CarService.class }) public class AppModule {}
The AppModule
class declares with the @NgModule
decorator (a Java annotation in JSweet) all the modules that are needed to run the application (for instance the HttpModule
for http calls, but also all the UI modules for PrimeNG. It also declares all the application-level components and the entry point (AppComponent
). Finally it declares services providers (here, CarService
).
Services typically depend on DTOs (here a JSweet interface but it could be a class). A JSweet interface is similar to a TypeScript interface. It only provides a type. Here our service will use a Car
DTO, defined as follows:
package app.cars; import jsweet.lang.Interface; @Interface public class Car { public String vin; public int year; public String brand; public String color; }
One really cool thing with JSweet is the ability to share these data objects with the server, and thus allowing cross client-server refactoring in your favorite Java IDE.
The car service is annotated with an @Injectable
decorator, since it will be injected in the component (easy dependency injection is one of the key features of Angular 2). It provides an implementation to access a set of cars defined in a data source (here a simple JSON file).
package app.cars; import static jsweet.util.Globals.$get; import def.angular.core.Injectable; import def.angular.http.Http; import def.es6_promise.Promise; @Injectable() public class CarService { private Http http; public CarService(Http http) { this.http = http; } public Promise<Car[]> getCarsMedium() { return this.http.get("app/resources/data/cars-medium.json").toPromise() .thenOnFulfilledFunction(res -> (Car[]) $get(res.json(), "data")) // .thenOnFulfilledFunction(data -> { return data; }); } }
Note the JSweet API for promises, which names then
methods more specifically in order to disambiguate Java calls with generics. Also, note the use of the $get
helper, with allows untyped access to the data
field.
Finally, here is the AppComponent
that ties it all together (component logic + app.component.html
template + cars service). This component shows a table of cars, fetched through the car service in the ngOnInit()
method and stored in the cars
array.
@Component(templateUrl = "app/app.component.html", selector = "my-app") public class AppComponent { Array<Car> cars; private CarService carService; public AppComponent(CarService carService) { this.carService = carService; } void ngOnInit() { this.carService.getCarsMedium().thenOnFulfilledFunction(cars -> this.cars = array(cars)); } [...] }
The complete component also provides CRUD operations on that table of cars.
Of course, the really cool thing with Angular2 is the ability to have all components defined as modules, which can be integrated together to build complex apps.
JSweet supports all the language features to build Angular2 applications: modules, decorators, promises, etc. So far, you need to use the current snapshot (v1.2.0-SNAPSHOT) to have it work. Plus, the Angular2 candy is at an early stage of development (contributions are welcome). A stable release is planned by the end of the year.