각도 + 스프링 WebFlux + 스프링 데이터 반응 Cassandra의 예
이 자습서에서는 Spring WebFlux, Spring Data Responsive Cassandra가 백엔드에 사용되고 Angular, RxJS, EventSource가 클라이언트에 사용됩니다.
관련 직위:
기술
둘개술
1. 풀스택 구조
2. 반응 Spring 부팅 서버
2.1 의존성
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
</dependency>
2.2 반응식 저장소
특정 유형에 대한 CRUD 작업을 수행하기 위해 확장
ReactiveCrudRepository
인터페이스를 만들기만 하면 됩니다.이 메모리 라이브러리는 반응성 범례를 따르고 프로젝트 반응기 유형Flux
,Mono
을 사용하여 반응 흐름 위에 구축한다.
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import reactor.core.publisher.Flux;
public interface ReactiveCustomerRepository extends ReactiveCrudRepository{
Flux findByLastname(String lastname);
@Query("SELECT * FROM customer WHERE firstname = ?0 and lastname = ?1")
Mono findByFirstnameAndLastname(String firstname, String lastname);
// for deferred execution
Flux findByLastname(Mono lastname);
Mono findByFirstnameAndLastname(Mono firstname, String lastname);
}
2.3 패시브 스프링 데이터 Cassandra 활성화@EnableReactiveCassandraRepositories
주석을 사용하여 패시브 스프링 데이터에 대한 지원을 활성화합니다.
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.AbstractReactiveCassandraConfiguration;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.repository.config.EnableReactiveCassandraRepositories;
@Configuration
@EnableReactiveCassandraRepositories
public class CassandraReactiveConfig extends AbstractReactiveCassandraConfiguration {
@Override
protected String getKeyspaceName() {
return "javasampleapproach";
}
@Override
public SchemaAction getSchemaAction() {
return SchemaAction.RECREATE;
}
}
2.4 호출 반응 저장소Spring Web reactive에서 제공한 반응 매개변수를 리포팅하여 저장소에 가져와
Flux
/Mono
로 되돌린 다음 반응 방식으로 결과를 처리할 수 있습니다.
import java.util.UUID;
import com.datastax.driver.core.utils.UUIDs;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@CrossOrigin(origins = "http://localhost:4200")
@RestController
public class CustomerController {
@Autowired
ReactiveCustomerRepository customerRepository;
@GetMapping("/customers")
public Flux getAllCustomers() {
return customerRepository.findAll();
}
@PostMapping("/customers/create")
public Mono createCustomer(@Valid @RequestBody Customer customer) {
customer.setId(UUIDs.timeBased());
customer.setActive(false);
return customerRepository.save(customer);
}
@PutMapping("/customers/{id}")
public Mono> updateCustomer(@PathVariable("id") UUID id, @RequestBody Customer customer) {
return customerRepository.findById(id).flatMap(customerData -> {
customerData.setName(customer.getName());
customerData.setAge(customer.getAge());
customerData.setActive(customer.isActive());
return customerRepository.save(customerData);
}).map(updatedcustomer -> new ResponseEntity<>(updatedcustomer, HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
@DeleteMapping("/customers/{id}")
public ResponseEntity deleteCustomer(@PathVariable("id") UUID id) {
try {
customerRepository.deleteById(id).subscribe();
} catch (Exception e) {
return new ResponseEntity<>("Fail to delete!", HttpStatus.EXPECTATION_FAILED);
}
return new ResponseEntity<>("Customer has been deleted!", HttpStatus.OK);
}
@DeleteMapping("/customers/delete")
public ResponseEntity deleteAllCustomers() {
try {
customerRepository.deleteAll().subscribe();
} catch (Exception e) {
return new ResponseEntity<>("Fail to delete!", HttpStatus.EXPECTATION_FAILED);
}
return new ResponseEntity<>("All customers have been deleted!", HttpStatus.OK);
}
@GetMapping("/customers/findbyage")
public Flux findByAge(@RequestParam int age) {
return customerRepository.findByAge(age);
}
}
@RequestMapping
에 설명된rest 컨트롤러 방법에서 우리는 autowired repository의 몇 가지 방법을 사용했는데 이런 방법은 인터페이스ReactiveCrudRepository
에 의해 실현되었다.public interface ReactiveCrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> Mono<S> save(S entity);
Mono<T> findById(ID id);
Flux<T> findAll();
Mono<Void> deleteById(ID id);
Mono<Void> deleteAll();
// ...
}
그리고 findByAge
인터페이스 ReactiveCustomerRepository에서 만든 방법:public interface ReactiveCustomerRepository extends ReactiveCrudRepository<Customer, UUID>{
Flux<Customer> findByAge(int age);
}
다른 포트에 설치된 클라이언트 애플리케이션에서 백엔드에 연결하려면 @CrossOrigin
노트를 사용하여 CORS를 설정해야 합니다.3. 반응형 고객
3.1 대응 서비스
이 서비스는 서버에서 보낸 이벤트와 백엔드 상호작용을 사용합니다.
import { Injectable, NgZone } from '@angular/core';
import { HttpClient, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import * as EventSource from 'eventsource';
import { Customer } from './customer';
@Injectable()
export class CustomerService {
private baseUrl = 'http://localhost:8080/api/customers';
private customersList: Customer[] = new Array();
private customersListSearch: Customer[] = new Array();
constructor(private http: HttpClient, private zone: NgZone) {
}
createCustomer(customer: Object): Observable<Object> {
return this.http.post(`${this.baseUrl}` + `/create`, customer);
}
updateCustomer(id: string, value: any): Observable<Object> {
return this.http.put(`${this.baseUrl}/${id}`, value);
}
deleteCustomer(id: string): Observable<any> {
return this.http.delete(`${this.baseUrl}/${id}`, { responseType: 'text' });
}
getCustomersList(): Observable<any> {
this.customersList = new Array();
return Observable.create((observer) => {
const eventSource = new EventSource(`${this.baseUrl}`);
eventSource.onmessage = (event) =>
this.zone.run(() => {
console.log('eventSource.onmessage: ', event);
const json = JSON.parse(event.data);
this.customersList.push(new Customer(json['id'], json['name'], json['age'], json['active']));
observer.next(this.customersList);
});
eventSource.onerror = (error) => observer.error('eventSource.onerror: ' + error);
return () => eventSource.close();
});
}
deleteAll(): Observable<any> {
return this.http.delete(`${this.baseUrl}` + `/delete`, { responseType: 'text' });
}
findCustomers(age): Observable<any> {
this.customersListSearch = new Array();
return Observable.create((observer) => {
const eventSource = new EventSource(`${this.baseUrl}` + `/findbyage?age=` + age);
eventSource.onmessage = (event) =>
this.zone.run(() => {
console.log('eventSource.onmessage: ', event);
const json = JSON.parse(event.data);
this.customersListSearch.push(new Customer(json['id'], json['name'], json['age'], json['active']));
observer.next(this.customersListSearch);
});
eventSource.onerror = (error) => observer.error('eventSource.onerror: ' + error);
return () => eventSource.close();
});
}
}
우리가 event
대상을 통해 수신EventSource
을 받을 때마다 호출onmessage()
됩니다.이것이 바로 우리가 데이터를 분석하고 프로젝트 목록을 업데이트하는 곳이다.RxJS Observable 객체를 사용하면 우리가 만든 Observable에 가입한 모든 관찰자는 프로젝트 목록이 업데이트될 때
observer.next(...)
이벤트를 받을 수 있습니다.우리는 Angular가 변경될 때 알림을 보낼 수 있도록
NgZone
하나를 실례화하고 zone.run(callback)
호출해야 한다.RxJS에 대한 자세한 내용은 다음을 참조하십시오.
Introduction to RxJS – Extensions for JavaScript Reactive Streams
3.2 무공분량
이 구성 요소는 위의 서비스를 호출하고 결과를
Observable
대상에 저장합니다.
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { CustomerService } from '../customer.service';
import { Customer } from '../customer';
@Component({
selector: 'customers-list',
templateUrl: './customers-list.component.html',
styleUrls: ['./customers-list.component.css']
})
export class CustomersListComponent implements OnInit {
customers: Observable;
constructor(private customerService: CustomerService) { }
ngOnInit() {
this.reloadData();
}
deleteCustomers() {
this.customerService.deleteAll()
.subscribe(
data => console.log(data),
error => console.log('ERROR: ' + error)
);
}
reloadData() {
this.customers = this.customerService.getCustomersList();
}
}
HTML 템플릿에 async
파이프가 추가되어 새 이벤트가 발생할 때마다 Observable and update 구성 요소에 가입합니다.<div *ngFor="let customer of customers | async">
<customer-details [customer]='customer'></customer-details>
</div>
<div>
<button type="button" class="button btn-danger" (click)='deleteCustomers()'>Delete All</button>
</div>
연습
0. 카산드라 설치
Cassandra CQL 셸을 열려면 다음과 같이 하십시오.
javasampleapproach라는 이름의 Cassandra 키 공간을 만들려면 다음과 같이 하십시오.
create keyspace javasampleapproach with replication={'class':'SimpleStrategy', 'replication_factor':1};
javasampleapproach 키 공간을 위한 클라이언트 테이블 만들기:
use javasampleapproach;
id timeuuid 메인 키,
이름 텍스트,
나이 지능,
활성 부울
);
age
필드에 Repository finder 방법을 사용하기 때문에 age
열에 색인을 만들어야 합니다.
CREATE INDEX ON javasampleapproach.customer (age);
1. 반응식 Spring 부트 서버
1.1 프로젝트 구조
getAll
,create
,update
,delete
Customers이다.pom의 Spring Boot WebFlux 및 Spring 데이터 Cassandra의 종속성xml.
1.2 의존성
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
</dependency>
1.3 데이터 모델
package com.javasampleapproach.reactive.cassandra.model;
import java.util.UUID;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;
@Table
public class Customer {
@PrimaryKey
private UUID id;
private String name;
private int age;
private boolean active;
public Customer() {
}
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
@Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + ", age=" + age + ", active=" + active + "]";
}
}
1.4 반응식 저장소
package com.javasampleapproach.reactive.cassandra.repo;
import java.util.UUID;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import com.javasampleapproach.reactive.cassandra.model.Customer;
import reactor.core.publisher.Flux;
public interface ReactiveCustomerRepository extends ReactiveCrudRepository{
Flux findByAge(int age);
}
1.5 반응 스프링 데이터 Cassandra 활성화
package com.javasampleapproach.reactive.cassandra.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.AbstractReactiveCassandraConfiguration;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.repository.config.EnableReactiveCassandraRepositories;
@Configuration
@EnableReactiveCassandraRepositories
public class CassandraReactiveConfig extends AbstractReactiveCassandraConfiguration {
@Override
protected String getKeyspaceName() {
return "javasampleapproach";
}
@Override
public SchemaAction getSchemaAction() {
return SchemaAction.RECREATE;
}
}
1.6 휴식 컨트롤러
package com.javasampleapproach.reactive.cassandra.controller;
import java.time.Duration;
import java.util.UUID;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.javasampleapproach.reactive.cassandra.model.Customer;
import com.javasampleapproach.reactive.cassandra.repo.ReactiveCustomerRepository;
import com.datastax.driver.core.utils.UUIDs;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping(value = "/api")
public class CustomerController {
@Autowired
ReactiveCustomerRepository customerRepository;
@GetMapping("/customers")
public Flux getAllCustomers() {
System.out.println("Get all Customers...");
return customerRepository.findAll().delayElements(Duration.ofMillis(1000));
}
@PostMapping("/customers/create")
public Mono createCustomer(@Valid @RequestBody Customer customer) {
System.out.println("Create Customer: " + customer.getName() + "...");
customer.setId(UUIDs.timeBased());
customer.setActive(false);
return customerRepository.save(customer);
}
@PutMapping("/customers/{id}")
public Mono> updateCustomer(@PathVariable("id") UUID id, @RequestBody Customer customer) {
System.out.println("Update Customer with ID = " + id + "...");
return customerRepository.findById(id).flatMap(customerData -> {
customerData.setName(customer.getName());
customerData.setAge(customer.getAge());
customerData.setActive(customer.isActive());
return customerRepository.save(customerData);
}).map(updatedcustomer -> new ResponseEntity<>(updatedcustomer, HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
@DeleteMapping("/customers/{id}")
public ResponseEntity deleteCustomer(@PathVariable("id") UUID id) {
System.out.println("Delete Customer with ID = " + id + "...");
try {
customerRepository.deleteById(id).subscribe();
} catch (Exception e) {
return new ResponseEntity<>("Fail to delete!", HttpStatus.EXPECTATION_FAILED);
}
return new ResponseEntity<>("Customer has been deleted!", HttpStatus.OK);
}
@DeleteMapping("/customers/delete")
public ResponseEntity deleteAllCustomers() {
System.out.println("Delete All Customers...");
try {
customerRepository.deleteAll().subscribe();
} catch (Exception e) {
return new ResponseEntity<>("Fail to delete!", HttpStatus.EXPECTATION_FAILED);
}
return new ResponseEntity<>("All customers have been deleted!", HttpStatus.OK);
}
@GetMapping("/customers/findbyage")
public Flux findByAge(@RequestParam int age) {
return customerRepository.findByAge(age).delayElements(Duration.ofMillis(1000));
}
}
결과가 효력을 발생시키기 위해서 우리는 delayElements()
를 사용한다.그것은 두 사건 사이의 시간을 지연시킬 수 있다.2. 반응형 고객
2.1 사용자 인터페이스
2.2 프로젝트 구조
이 예에서 우리는 다음과 같다.
– 4개의 구성 요소: 고객 목록, 고객 세부 정보, 고객 만들기, 고객 검색.
- 4개의 모듈: FormsModule, HttpClientModule, AppRoutingModule
-고객님.ts: 유형 고객(id, 이름, 나이, 활동).
-고객님.서비스ts:
HttpClient
방법에 대한 서비스를 제공합니다.2.3 애플리케이션 모듈
응용 프로그램.모듈ts
import { AppRoutingModule } from './app-routing.module';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { CreateCustomerComponent } from './customers/create-customer/create-customer.component';
import { CustomerDetailsComponent } from './customers/customer-details/customer-details.component';
import { CustomersListComponent } from './customers/customers-list/customers-list.component';
import { SearchCustomersComponent } from './customers/search-customers/search-customers.component';
import { CustomerService } from './customers/customer.service';
@NgModule({
declarations: [
AppComponent,
CreateCustomerComponent,
CustomerDetailsComponent,
CustomersListComponent,
SearchCustomersComponent
],
imports: [
BrowserModule,
FormsModule,
AppRoutingModule,
HttpClientModule
],
providers: [CustomerService],
bootstrap: [AppComponent]
})
export class AppModule { }
2.4 모델손님?ts
export class Customer {
id: string;
name: string;
age: number;
active: boolean;
constructor(id?: string, name?: string, age?: number, active?: boolean) {
this.id = id;
this.name = name;
this.age = age;
this.active = active;
}
}
2.5 서비스손님?서비스ts
import { Injectable, NgZone } from '@angular/core';
import { HttpClient, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import * as EventSource from 'eventsource';
import { Customer } from './customer';
@Injectable()
export class CustomerService {
private baseUrl = 'http://localhost:8080/api/customers';
private customersList: Customer[] = new Array();
private customersListSearch: Customer[] = new Array();
constructor(private http: HttpClient, private zone: NgZone) {
}
createCustomer(customer: Object): Observable<Object> {
return this.http.post(`${this.baseUrl}` + `/create`, customer);
}
updateCustomer(id: string, value: any): Observable<Object> {
return this.http.put(`${this.baseUrl}/${id}`, value);
}
deleteCustomer(id: string): Observable<any> {
return this.http.delete(`${this.baseUrl}/${id}`, { responseType: 'text' });
}
getCustomersList(): Observable<any> {
this.customersList = new Array();
return Observable.create((observer) => {
const eventSource = new EventSource(`${this.baseUrl}`);
eventSource.onmessage = (event) =>
this.zone.run(() => {
console.log('eventSource.onmessage: ', event);
const json = JSON.parse(event.data);
this.customersList.push(new Customer(json['id'], json['name'], json['age'], json['active']));
observer.next(this.customersList);
});
eventSource.onerror = (error) => observer.error('eventSource.onerror: ' + error);
return () => eventSource.close();
});
}
deleteAll(): Observable<any> {
return this.http.delete(`${this.baseUrl}` + `/delete`, { responseType: 'text' });
}
findCustomers(age): Observable<any> {
this.customersListSearch = new Array();
return Observable.create((observer) => {
const eventSource = new EventSource(`${this.baseUrl}` + `/findbyage?age=` + age);
eventSource.onmessage = (event) =>
this.zone.run(() => {
console.log('eventSource.onmessage: ', event);
const json = JSON.parse(event.data);
this.customersListSearch.push(new Customer(json['id'], json['name'], json['age'], json['active']));
observer.next(this.customersListSearch);
});
eventSource.onerror = (error) => observer.error('eventSource.onerror: ' + error);
return () => eventSource.close();
});
}
}
2.6 구성 요소2.6.1 고객 세부 사항 구성 요소
고객 세부 정보.구성 요소ts
import { Component, OnInit, Input } from '@angular/core';
import { CustomerService } from '../customer.service';
import { Customer } from '../customer';
import { CustomersListComponent } from '../customers-list/customers-list.component';
@Component({
selector: 'customer-details',
templateUrl: './customer-details.component.html',
styleUrls: ['./customer-details.component.css']
})
export class CustomerDetailsComponent implements OnInit {
@Input() customer: Customer;
constructor(private customerService: CustomerService, private listComponent: CustomersListComponent) { }
ngOnInit() {
}
updateActive(isActive: boolean) {
this.customerService.updateCustomer(this.customer.id,
{ name: this.customer.name, age: this.customer.age, active: isActive })
.subscribe(
data => {
console.log(data);
this.customer = data as Customer;
},
error => console.log(error)
);
}
deleteCustomer() {
this.customerService.deleteCustomer(this.customer.id)
.subscribe(
data => {
console.log(data);
this.listComponent.reloadData();
},
error => console.log(error)
);
}
}
고객 세부 정보.구성 요소html<div *ngIf="customer">
<div>
<label>Name: </label> { { customer.name}}
</div>
<div>
<label>Age: </label> { { customer.age}}
</div>
<div>
<label>Active: </label> { { customer.active}}
</div>
<span class="button is-small btn-primary" *ngIf='customer.active' (click)='updateActive(false)'>Inactive</span>
<span class="button is-small btn-primary" *ngIf='!customer.active' (click)='updateActive(true)'>Active</span>
<span class="button is-small btn-danger" (click)='deleteCustomer()'>Delete</span>
<hr/>
</div>
2.6.2 고객 목록 구성 요소고객 명단.구성 요소ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { CustomerService } from '../customer.service';
import { Customer } from '../customer';
@Component({
selector: 'customers-list',
templateUrl: './customers-list.component.html',
styleUrls: ['./customers-list.component.css']
})
export class CustomersListComponent implements OnInit {
customers: Observable;
constructor(private customerService: CustomerService, private router: Router) { }
ngOnInit() {
this.reloadData();
}
deleteCustomers() {
this.customerService.deleteAll()
.subscribe(
data => {
console.log(data);
this.navigateToAdd();
},
error => console.log('ERROR: ' + error)
);
}
reloadData() {
this.customers = this.customerService.getCustomersList();
}
navigateToAdd() {
this.router.navigate(['add']);
}
}
고객 명단.구성 요소html<br/>
<div *ngFor="let customer of customers | async" style="width: 300px;">
<customer-details [customer]='customer'></customer-details>
</div>
<div>
<button type="button" class="button btn-danger" (click)='deleteCustomers()'>Delete All</button>
</div>
2.6.3 CreateCustomerComponent고객을 만들다.구성 요소ts
import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Customer } from '../customer';
import { CustomerService } from '../customer.service';
@Component({
selector: 'create-customer',
templateUrl: './create-customer.component.html',
styleUrls: ['./create-customer.component.css']
})
export class CreateCustomerComponent implements OnInit {
customer: Customer = new Customer();
submitted = false;
constructor(private customerService: CustomerService) { }
ngOnInit() {
}
newCustomer(): void {
this.submitted = false;
this.customer = new Customer();
}
save() {
this.customerService.createCustomer(this.customer)
.subscribe(data => console.log(data), error => console.log(error));
this.customer = new Customer();
}
onSubmit() {
this.submitted = true;
this.save();
}
}
고객을 만들다.구성 요소html<h3>Create Customer</h3>
<div [hidden]="submitted" style="width: 300px;">
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">Name</label> <input type="text"
class="form-control" id="name" required [(ngModel)]="customer.name"
name="name">
</div>
<div class="form-group">
<label for="age">Age</label> <input type="text"
class="form-control" id="age" required [(ngModel)]="customer.age"
name="age">
</div>
<button type="submit" class="btn btn-success">Submit</button>
</form>
</div>
<div [hidden]="!submitted">
<h4>You submitted successfully!</h4>
<button class="btn btn-success" (click)="newCustomer()">Add</button>
</div>
2.6.4 고객 구성 요소 검색고객을 검색합니다.구성 요소ts
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { CustomerService } from '../customer.service';
import { Customer } from '../customer';
@Component({
selector: 'search-customers',
templateUrl: './search-customers.component.html',
styleUrls: ['./search-customers.component.css']
})
export class SearchCustomersComponent implements OnInit {
customers: Observable;
age: number;
constructor(private customerService: CustomerService) { }
ngOnInit() {
this.age = 0;
}
search() {
this.customers = this.customerService.findCustomers(this.age);
}
}
고객을 검색합니다.구성 요소html<h3>Find Customers By Age</h3>
<input type="text" [(ngModel)]="age" placeholder="enter age" class="input">
<button class="btn btn-success" (click)="search()">Search</button>
<hr />
<ul>
<li *ngFor="let customer of customers | async">
<h5> { { customer.name}} - Age: { { customer.age}} - Active: { { customer.active}}</h5>
</li>
</ul>
2.7 승인 모듈응용 프로그램 라우팅모듈ts
import { CreateCustomerComponent } from './customers/create-customer/create-customer.component';
import { CustomersListComponent } from './customers/customers-list/customers-list.component';
import { SearchCustomersComponent } from './customers/search-customers/search-customers.component';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{ path: '', redirectTo: 'customers', pathMatch: 'full' },
{ path: 'customers', component: CustomersListComponent },
{ path: 'add', component: CreateCustomerComponent },
{ path: 'search', component: SearchCustomersComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
2.8 애플리케이션 구성 요소응용 프로그램.구성 요소ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'JavaSampleApproach';
description = 'Reactive-Angular-Cassandra';
constructor() { }
}
응용 프로그램.구성 요소html<div class="container-fluid">
<div style="color: blue;">
<h1> { { title}}</h1>
<h3> { { description}}</h3>
</div>
<nav>
<a routerLink="customers" class="btn btn-primary active" role="button" routerLinkActive="active">Customers</a>
<a routerLink="add" class="btn btn-primary active" role="button" routerLinkActive="active">Add</a>
<a routerLink="search" class="btn btn-primary active" role="button" routerLinkActive="active">Search</a>
</nav>
<router-outlet></router-outlet>
</div>
3. 실행 및 결과 검토
명령줄
mvn clean install
과 mvn spring-boot:run
을 사용하여 Spring Boot 프로젝트를 구축하고 실행합니다.npm start
을 사용하여 Angular 애플리케이션을 실행합니다.http://localhost:4200/
이 있는 브라우저를 열고 고객을 추가합니다.4. 소스 코드
Reference
이 문제에 관하여(각도 + 스프링 WebFlux + 스프링 데이터 반응 Cassandra의 예), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/loizenai/angular-spring-webflux-spring-data-reactive-cassandra-example-j5g텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)