각도 + 스프링 WebFlux + 스프링 데이터 반응 Cassandra의 예

https://grokonez.com/reactive-programming/angular-spring-webflux-spring-data-reactive-cassandra-example-full-reactive-angular-http-client-spring-boot-restapi-server
이 자습서에서는 Spring WebFlux, Spring Data Responsive Cassandra가 백엔드에 사용되고 Angular, RxJS, EventSource가 클라이언트에 사용됩니다.
관련 직위:
  • SpringData Reactive Cassandra Repositories | SpringBoot
  • Introduction to RxJS – Extensions for JavaScript Reactive Streams
  • Angular 4 + Spring Boot + Cassandra CRUD example
  • 기술

  • Java 1.8
  • Maven 3.3.9
  • 스프링 키트 3.9.0.석방
  • 스프링 커버 2.0.0.석방
  • 각도 5
  • RxJS 5.1.0
  • 카산드라 3.9.0

    둘개술


    1. 풀스택 구조



    2. 반응 Spring 부팅 서버


    2.1 의존성
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
    </dependency>

  • org.springframework.시작
    spring boot starter webflux

    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 프로젝트 구조

  • 범주 Customer는 Customer collection의 문서에 해당합니다.
  • ReactiveCustomerRepository는 ReactiveCrudepository를 확장하는 인터페이스로 Customer Controller에 자동으로 연결되어 저장소 방법을 실현한다.
  • Customer Controller는 REST 컨트롤러로 REST 요청의 요청 매핑 방법을 가지고 있다. 예를 들어 getAll,create,update,deleteCustomers이다.

  • pom의 Spring Boot WebFlux 및 Spring 데이터 Cassandra의 종속성xml.
    1.2 의존성
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
    </dependency>

  • org.springframework.시작
    spring boot starter webflux

    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 installmvn spring-boot:run을 사용하여 Spring Boot 프로젝트를 구축하고 실행합니다.
  • 명령npm start을 사용하여 Angular 애플리케이션을 실행합니다.
  • URLhttp://localhost:4200/이 있는 브라우저를 열고 고객을 추가합니다.
  • '고객'옵션을 클릭하면 고객마다 하나씩 표시되고 지연 시간은 1s입니다.
  • 검색 탭을 클릭하고'27'을 검색한 결과 고객마다 하나씩 지연시간이 1s로 나타났다.

  • 4. 소스 코드

  • SpringDataReactiveCassandra
  • ReactiveAngularCassandra
  • https://grokonez.com/reactive-programming/angular-spring-webflux-spring-data-reactive-cassandra-example-full-reactive-angular-http-client-spring-boot-restapi-server

    좋은 웹페이지 즐겨찾기