Java 如何设置弹簧模型以支持复杂对象的角度编辑组件?
我正在用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. 因此,模型工作正常,关联正确,路由按预期工作。是什么阻止我将引擎作为产品的一部分丢弃Java 如何设置弹簧模型以支持复杂对象的角度编辑组件?,java,json,spring,angular,Java,Json,Spring,Angular,我正在用Spring和Angular编写我的第一个web应用程序。我使用PHP大约10年了,然后在过去10年中发现并使用了Rails,并且做了几个ASP项目来启动,所以我对web开发并不陌生。我正在尝试创建我的第一套完整的CRUD操作,但是我找不到关于如何为具有父对象和/或子对象的复杂对象创建编辑表单的文档。官方的《角度指南》离这还差一点,我在互联网上找不到一本全面介绍这方面的教程。在我的示例中,我需要一个产品的编辑组件,可以在其中更改当前选定的引擎 在Spring中,我为两个初始模型配置了类、
另外,如果我解决了这个问题,并将引擎节与产品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"
}
}
}