Java 如何设置弹簧模型以支持复杂对象的角度编辑组件?

Java 如何设置弹簧模型以支持复杂对象的角度编辑组件?,java,json,spring,angular,Java,Json,Spring,Angular,我正在用Spring和Angular编写我的第一个web应用程序。我使用PHP大约10年了,然后在过去10年中发现并使用了Rails,并且做了几个ASP项目来启动,所以我对web开发并不陌生。我正在尝试创建我的第一套完整的CRUD操作,但是我找不到关于如何为具有父对象和/或子对象的复杂对象创建编辑表单的文档。官方的《角度指南》离这还差一点,我在互联网上找不到一本全面介绍这方面的教程。在我的示例中,我需要一个产品的编辑组件,可以在其中更改当前选定的引擎 在Spring中,我为两个初始模型配置了类、

我正在用Spring和Angular编写我的第一个web应用程序。我使用PHP大约10年了,然后在过去10年中发现并使用了Rails,并且做了几个ASP项目来启动,所以我对web开发并不陌生。我正在尝试创建我的第一套完整的CRUD操作,但是我找不到关于如何为具有父对象和/或子对象的复杂对象创建编辑表单的文档。官方的《角度指南》离这还差一点,我在互联网上找不到一本全面介绍这方面的教程。在我的示例中,我需要一个产品的编辑组件,可以在其中更改当前选定的引擎

在Spring中,我为两个初始模型配置了类、存储库和控制器,如下所示:

引擎父级->产品子级

Product.java:

Engine.java代码段:

ProductRepository.java:

product-edit.component.ts:

product-edit.component.html:

击中http://localhost:8080/products/1/engine 工作也一样http://localhost:8080/engines/1/products. 因此,模型工作正常,关联正确,路由按预期工作。是什么阻止我将引擎作为产品的一部分丢弃


另外,如果我解决了这个问题,并将引擎节与产品JSON一起获得,这会使编辑表单工作吗?这会自动用当前关联的引擎填充引擎下拉列表吗?

所以我已经讨论了Reddit,人们引导我远离使用Spring REST的想法。我被指向了,根据它的自述,这是Spring中各种项目的一个长期例子。在我读到这一点的过程中,我从未偶然发现过这一点

从该项目中的pom.xml中,我看到Spring数据JPA和Spring Web的组合提供了REST服务——所有这些都是由它自己提供的——这正是我认为我需要Spring REST的原因。我似乎仍然需要Jackson@JsonIdentityInfo注释来防止双向一对多关系上的堆栈溢出递归循环,但我似乎不需要back或managed引用注释


最后,从spring前端子项目中的组件和表单中,我还可以看到,我必须在下拉菜单select q.v中手动设置一个已定义的关联对象实例,然后在将其发送到后端的update函数之前,将该选择与服务中的父对象重新组合。这是我习惯于为我处理Rails的东西,我还没有看到Angular是否也会为我这样做的例子。答案似乎是否定的。

我建议使用DTO和spring hateoas。您将对公开数据的外观有更多的控制,好吧,我很不愿意再添加一个层来实现这一点,但是这个Q/A似乎符合我的需要,尽管我还不知道如何在我的应用程序中实现它:
package com.xyz.cddm_ng;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.*;
import java.time.LocalDateTime;
import static javax.persistence.GenerationType.IDENTITY;

@Entity
@Getter @Setter
@NoArgsConstructor
@ToString @EqualsAndHashCode
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Product {
    @Id
    @GeneratedValue(strategy=IDENTITY)
    Long id;

    @NonNull String title;

    String note;

    @CreationTimestamp
    LocalDateTime createDateTime;

    @UpdateTimestamp
    LocalDateTime updateDateTime;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "engine_id")
    @JsonManagedReference
    Engine engine;
}
@OneToMany(fetch = FetchType.LAZY, mappedBy = "engine")
@JsonBackReference
Collection<Product> products;
package com.xyz.cddm_ng;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("products")
@CrossOrigin(origins = "http://localhost:4200")
class ProductController { }
package com.xyz.cddm_ng;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.web.bind.annotation.CrossOrigin;

@RepositoryRestResource
@CrossOrigin(origins = "http://localhost:4200")
interface ProductRepository extends JpaRepository<Product, Long> { }
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

export class ProductService {

  public API = '//localhost:8080';
  public PRODUCT_API = this.API + '/products';

  constructor(private http: HttpClient) { }

  getAll(): Observable<any> {
    return this.http.get(this.PRODUCT_API);
  }

  get(id: string) {
    return this.http.get(this.PRODUCT_API + '/' + id);
  }

  save(product: any): Observable<any> {
    let result: Observable<Object>;
    if (product['href']) {
      result = this.http.put(product.href, product);
    } else {
      result = this.http.post(this.PRODUCT_API, product);
    }
    return result;
  }

  remove(href: string) {
    return this.http.delete(href);
  }

}
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { ActivatedRoute, Router } from '@angular/router';
import { ProductService } from '../shared/product/product.service';
import { EngineService } from '../shared/engine/engine.service';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-product-edit',
  templateUrl: './product-edit.component.html',
  styleUrls: ['./product-edit.component.css']
})
export class ProductEditComponent implements OnInit, OnDestroy {
  prod: any = { };

  sub: Subscription;

  engines: any[] = [ ];

  constructor(private route: ActivatedRoute,
              private router: Router,
              private productService: ProductService,
              private engineService: EngineService) { }

  ngOnInit() {
    this.sub = this.route.params.subscribe( params => {
      const id = params['id'];
      if (id) {
        this.productService.get(id)
          .subscribe(
            data => {
              this.prod = data;
            },
            error => {
              console.log(`Product with '${id}' not found, returning to list. Error was '${error}'.`);
              this.gotoList();
            });
        this.engineService.getAll().subscribe(
          data => {
            this.engines = data;
          });
      }
    });
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }

  gotoList() {
    this.router.navigate(['/product-list']);
  }

  save(form: NgForm) {
    this.productService.save(form).subscribe(result => {
      this.gotoList();
    }, error => console.error(error));
  }

  remove(href) {
    this.productService.remove(href).subscribe(result => {
      this.gotoList();
    }, error => console.error(error));
  }

}
<form #f="ngForm" (ngSubmit)="save(f.value)">

  <div class="form-group">
    <label class="form-control-label" for="title">Title:</label>
    <input type="text" class="form-control" id="title" required [(ngModel)]="prod.title" name="title">
  </div>

  <div class="form-group">
    <label class="form-control-label" for="note">Note:</label>
    <input type="text" class="form_control" id="note" required [(ngModel)]="prod.note" name="note">
  </div>

  <div class="form-group">
    <label class="form-control-label" for="engine" #selectedValue>Engine</label>
    <select class="form-control" id="engine" name="engine" [(ngModel)]="prod.engine">
      <option [value]="null"></option>
      <option *ngFor="let engine of engines" [ngValue]="engine">{{engine.name}}</option>
    </select>
  </div>

  <button type="submit" class="btn btn-success">Submit</button>

</form>
{
  "id" : 1,
  "title" : "eowir",
  "note" : "noerw",
  "createDateTime" : "2018-08-22T16:10:07.349752",
  "updateDateTime" : "2018-08-22T16:10:07.349752",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/products/1"
    },
    "product" : {
      "href" : "http://localhost:8080/products/1"
    },
    "engine" : {
      "href" : "http://localhost:8080/products/1/engine"
    }
  }
}