How do I create an Angular 4 client for a Java Project Reactor reactive Flux
API? The sample below has two APIs: a Mono
API; and, Flux
API. Both work from curl
; but in Angular 4 (4.1.2) only the Mono
API works; any ideas how to get Angular 4 to work with the Flux
API?
Here's a trivial Spring Boot 2.0.0-SNAPSHOT application with a Mono
API and a Flux
API:
@SpringBootApplication
@RestController
public class ReactiveServiceApplication {
@CrossOrigin
@GetMapping("/events/{id}")
public Mono<Event> eventById(@PathVariable long id) {
return Mono.just(new Event(id, LocalDate.now()));
}
@CrossOrigin
@GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Event> events() {
Flux<Event> eventFlux = Flux.fromStream(
Stream.generate(
()->new Event(System.currentTimeMillis(), LocalDate.now()))
);
Flux<Long> durationFlux = Flux.interval(Duration.ofSeconds(1));
return Flux.zip(eventFlux, durationFlux).map(Tuple2::getT1);
}
public static void main(String[] args) {
SpringApplication.run(ReactiveServiceApplication.class);
}
}
with a Lombok-ed event:
@Data
@AllArgsConstructor
public class Event {
private final long id;
private final LocalDate when;
}
These reactive APIs work from curl as I'd expect:
jan@linux-6o1s:~/src> curl -s http://localhost:8080/events/123
{"id":123,"when":{"year":2017,"month":"MAY","monthValue":5,"dayOfMonth":15,"dayOfWeek":"MONDAY","era":"CE","dayOfYear":135,"leapYear":false,"chronology":{"calendarType":"iso8601","id":"ISO"}}}
and similarly for the non-terminating Flux API:
jan@linux-6o1s:~/src> curl -s http://localhost:8080/events
data:{"id":1494887783347,"when":{"year":2017,"month":"MAY","monthValue":5,"dayOfMonth":15,"dayOfWeek":"MONDAY","era":"CE","dayOfYear":135,"leapYear":false,"chronology":{"calendarType":"iso8601","id":"ISO"}}}
data:{"id":1494887784348,"when":{"year":2017,"month":"MAY","monthValue":5,"dayOfMonth":15,"dayOfWeek":"MONDAY","era":"CE","dayOfYear":135,"leapYear":false,"chronology":{"calendarType":"iso8601","id":"ISO"}}}
data:{"id":1494887785347,"when":{"year":2017,"month":"MAY","monthValue":5,"dayOfMonth":15,"dayOfWeek":"MONDAY","era":"CE","dayOfYear":135,"leapYear":false,"chronology":{"calendarType":"iso8601","id":"ISO"}}}
...
The similarly trivial Angular 4 client with RxJS:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
title = 'app works!';
event: Observable<Event>;
subscription: Subscription;
constructor(
private _http: Http
) {
}
ngOnInit() {
this.subscription = this._http
.get("http://localhost:8080/events/322")
.map(response => response.json())
.subscribe(
e => {
this.event = e;
}
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
works fine for the Mono
API:
"http://localhost:8080/events/322"
but the Flux
API:
"http://localhost:8080/events"
never triggers the event handler, unlike curl
.
How to Integrate Angular with Spring Boot RESTful API. 1 1. Install Node.js for Angular. Download and install Node.js from their website. If the installation is successful, you would see the following items ... 2 2. Install Angular-CLI. 3 3. Create Angular Client Project. 4 4. Import Angular Client Project. 5 5. Generate Components. More items
Create Angular Client Project On the command line, navigate to your IDE’s workspace by entering, CD C:\Users\User\workspace (specify your workspace path here). Start a new Angular project by entering ng new angular4-client –routing. 4. Import Angular Client Project Import Angular client project into Spring Tool Suite.
? 5. Integrate Angular and Spring Boot Up to now, Angular4-Client and Spring Boot server worked independently on ports 8080 and 4200. The goal of the below integration is to ensure that client at 4200 will proxy any API requests to the server.
Spring Boot and Angular form a powerful tandem that works great for developing web applications with a minimal footprint. In this tutorial, we'll use Spring Boot for implementing a RESTful backend, and Angular for creating a JavaScript-based frontend. Learn how to create controllers using Spring MVC request annotation on Java interfaces.
Here's a working Angular 4 SSE example as Simon describes in his answer. This took a while to piece together so perhaps it'll be useful to others. The key piece here is Zone -- without Zone, the SSE updates won't trigger Angular's change detection.
import { Component, NgZone, OnInit, OnDestroy } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/map';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
event: Observable<MyEvent>;
private _eventSource: EventSource;
private _events: BehaviorSubject<MyEvent> = new BehaviorSubject<MyEvent>(null);
constructor(private _http: Http, private _zone: NgZone) {}
ngOnInit() {
this._eventSource = this.createEventSource();
this.event = this.createEventObservable();
}
private createEventObservable(): Observable<MyEvent> {
return this._events.asObservable();
}
private createEventSource(): EventSource {
const eventSource = new EventSource('http://localhost:8080/events');
eventSource.onmessage = sse => {
const event: MyEvent = new MyEvent(JSON.parse(sse.data));
this._zone.run(()=>this._events.next(event));
};
eventSource.onerror = err => this._events.error(err);
return eventSource;
}
}
The corresponding HTML is simply:
<b>Observable of sse</b>
<div *ngIf="(event | async); let evt; else loading">
<div>ID: {{evt.id}} </div>
</div>
<ng-template #loading>Waiting...</ng-template>
The event is trivial:
export class MyEvent {
id: number;
when: any;
constructor(jsonData) {
Object.assign(this, jsonData);
}
}
and since my TS does not include EventSource
or Callback
, I stubbed them in:
interface Callback { (data: any): void; }
declare class EventSource {
onmessage: Callback;
onerror: Callback;
addEventListener(event: string, cb: Callback): void;
constructor(name: string);
close: () => void;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With