Fork me on GitHub

That’s official! JSweet allows using Java to write Angular 2 applications. It even was presented at JavaOne in the following talk:

Full-Stack Java with JSweet, Angular 2, PrimeNG, and JAX-RS, by Kito D. Mann. JavaOne 2016.

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.

Project configuration

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.

Project entry point

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).

Defining DTOs

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.

Defining Angular2 services

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.

Defining Angular2 components

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.

Conclusion

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.

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *