Angular, RxJs, 初始化组件的子变量
Angular, RxJs, Initializing a Component's Child Variable
在 UserEditComponent 中初始化 JavaScript 变量 user.person 的最佳方法是什么?我读到不推荐嵌套订阅。我还能如何获得人员字段 firstName 和 lastName?以下是与我的问题相关的所有 Angular 代码。
我已经创建了一个组件、一个服务和 JPA 实体。
{{user.person.firstName}}
抛出错误,因为 user.person 为空。
调用http://localhost:8080/users/ returns:
[{"id":"951bcd7e-a69c-4143-abc7-c2475ee249d2","version":0,"password":null,"username":null,"isActive":false,"createTime":1598588747997,"deactivatedTime":0,"person":{"id":"4340c6fc-8288-434c-a5ea-2497b72034a0","version":0,"firstName":"Amar","middleName":null,"lastName":"Patel","ssn":null}}]
调用 userService.get(id) 或 http://localhost:8080/users/951bcd7e-a69c-4143-abc7-c2475ee249d2 returns:
{
"password" : null,
"username" : null,
"isActive" : false,
"createTime" : 1598588747997,
"deactivatedTime" : 0,
"_links" : {
"self" : {
"href" : "http://localhost:8080/users/951bcd7e-a69c-4143-abc7-c2475ee249d2"
},
"user" : {
"href" : "http://localhost:8080/users/951bcd7e-a69c-4143-abc7-c2475ee249d2"
},
"person" : {
"href" : "http://localhost:8080/users/951bcd7e-a69c-4143-abc7-c2475ee249d2/person"
}
}
}
这是对http://localhost:8080/users/951bcd7e-a69c-4143-abc7-c2475ee249d2/person的调用returns:
{
"firstName" : "Amar",
"middleName" : null,
"lastName" : "Patel",
"ssn" : null,
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons/4340c6fc-8288-434c-a5ea-2497b72034a0"
},
"person" : {
"href" : "http://localhost:8080/persons/4340c6fc-8288-434c-a5ea-2497b72034a0"
},
"user" : {
"href" : "http://localhost:8080/persons/4340c6fc-8288-434c-a5ea-2497b72034a0/user"
}
}
}
Angular 组件:
@Component({
selector: 'app-user-edit',
templateUrl: './user-edit.component.html',
styleUrls: ['./user-edit.component.css']
})
export class UserEditComponent implements OnInit, OnDestroy {
user: any = {};
sub: Subscription;
constructor(private route: ActivatedRoute,
private router: Router,
private userService: UserService) {
}
ngOnInit(): void {
this.sub = this.route.params.subscribe(params => {
const id = params.id;
if (id) {
this.userService.get(id).subscribe((user: any) => {
if(user) {
this.user = user;
this.user.href = user._links.self.href;
} else {
console.log(`User with id '${id}' not found, returning to list`);
this.gotoList();
}
});
}
});
}
Angular 服务:
@Injectable({
providedIn: 'root'
})
export class UserService {
public API = '//localhost:8080';
public USER_API = this.API + '/users';
constructor(private http: HttpClient) {
}
getAll(): Observable<any> {
return this.http.get(this.API + '/users');
}
get(id: string) {
return this.http.get(this.USER_API + '/' + id);
}
save(user: any): Observable<any> {
let result: Observable<any>;
if(user.href) {
result = this.http.put(user.href, user);
} else {
result = this.http.post(this.USER_API, user);
}
return result;
}
remove(href: string) {
return this.http.delete(href);
}
}
JPA 用户对象:
@Entity
@NoArgsConstructor
public class User extends BaseEntity {
@Basic
private String password;
@Basic
@Column(unique = true, length = 100, columnDefinition = "VARCHAR(100)")
private String username;
@OneToOne
@JoinColumn(name = "person_id")
private Person person;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
JPA Person 对象:
@Entity
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class Person extends BaseEntity {
@Basic(optional = false)
@Column(nullable = false, columnDefinition = "VARCHAR(255)")
private String firstName;
@Basic(optional = false)
@Column(nullable = false, columnDefinition = "VARCHAR(255)")
private String lastName;
@OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
@JsonIgnore
private User user;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
对象初始化:
- 您基本上可以像这样用虚拟数据初始化您的对象:
user: any = {
person: {
firstName: 'john',
...
},
...
};
- 更好的方法是让您对象未定义
user: any;
并检查模板中值的存在性 {{ user?.person.firstName }}
不会抛出未定义的错误
嵌套订阅:
为避免嵌套订阅,您可以像这样使用 rxjs operator switchMap:
ngOnInit(): void {
this.sub = this.route.params.pipe(
switchMap(
params => {
const id = params.id;
if (id) {
return this.userService.get(id);
} else {
return of(null);
}
}
).subscribe((user: any) => {
if(user) {
this.user = user;
this.user.href = user._links.self.href;
} else {
console.log(`User with id '${id}' not found, returning to list`);
this.gotoList();
}
});
}
有关 switchMap 工作的更多信息:https://www.learnrxjs.io/learn-rxjs/operators/transformation/switchmap
必须做出一些关于架构的基本决定。
首先,您应该介绍User和Person的类型定义。为 User
命名 3 个可能的定义:
- 一个用户对象总是包含一个人对象:
interface User {
...
person: Person;
...
}
- 一个用户对象可能包含一个人对象:
interface User {
...
person?: Person;
...
}
- 用户对象包含人员对象的 ID:
interface User {
...
_links: {
person: string
};
...
}
User
类型的设计影响了谁负责获取人员数据的问题,即调用 http://localhost:8080/users/{id}/person
。如果您选择选项 1,那么它应该是 UserService 的任务,并且 UserEditComponent 永远不会看到没有初始化 Person 的 User 对象。使用选项 3,UserEditComponent 将为用户和人员提供两个单独的属性,组件的任务是显式获取人员。选项2位于中间,两种初始化方式都是合理的。
此外,this.user.href = user._links.self.href;
似乎也不是一个组件应该做的事情。服务负责转换从后端接收到的数据。
我希望这个答案朝着正确的方向发展。欢迎在评论中进一步讨论。
在 UserEditComponent 中初始化 JavaScript 变量 user.person 的最佳方法是什么?我读到不推荐嵌套订阅。我还能如何获得人员字段 firstName 和 lastName?以下是与我的问题相关的所有 Angular 代码。
我已经创建了一个组件、一个服务和 JPA 实体。
{{user.person.firstName}}
抛出错误,因为 user.person 为空。
调用http://localhost:8080/users/ returns:
[{"id":"951bcd7e-a69c-4143-abc7-c2475ee249d2","version":0,"password":null,"username":null,"isActive":false,"createTime":1598588747997,"deactivatedTime":0,"person":{"id":"4340c6fc-8288-434c-a5ea-2497b72034a0","version":0,"firstName":"Amar","middleName":null,"lastName":"Patel","ssn":null}}]
调用 userService.get(id) 或 http://localhost:8080/users/951bcd7e-a69c-4143-abc7-c2475ee249d2 returns:
{
"password" : null,
"username" : null,
"isActive" : false,
"createTime" : 1598588747997,
"deactivatedTime" : 0,
"_links" : {
"self" : {
"href" : "http://localhost:8080/users/951bcd7e-a69c-4143-abc7-c2475ee249d2"
},
"user" : {
"href" : "http://localhost:8080/users/951bcd7e-a69c-4143-abc7-c2475ee249d2"
},
"person" : {
"href" : "http://localhost:8080/users/951bcd7e-a69c-4143-abc7-c2475ee249d2/person"
}
}
}
这是对http://localhost:8080/users/951bcd7e-a69c-4143-abc7-c2475ee249d2/person的调用returns:
{
"firstName" : "Amar",
"middleName" : null,
"lastName" : "Patel",
"ssn" : null,
"_links" : {
"self" : {
"href" : "http://localhost:8080/persons/4340c6fc-8288-434c-a5ea-2497b72034a0"
},
"person" : {
"href" : "http://localhost:8080/persons/4340c6fc-8288-434c-a5ea-2497b72034a0"
},
"user" : {
"href" : "http://localhost:8080/persons/4340c6fc-8288-434c-a5ea-2497b72034a0/user"
}
}
}
Angular 组件:
@Component({
selector: 'app-user-edit',
templateUrl: './user-edit.component.html',
styleUrls: ['./user-edit.component.css']
})
export class UserEditComponent implements OnInit, OnDestroy {
user: any = {};
sub: Subscription;
constructor(private route: ActivatedRoute,
private router: Router,
private userService: UserService) {
}
ngOnInit(): void {
this.sub = this.route.params.subscribe(params => {
const id = params.id;
if (id) {
this.userService.get(id).subscribe((user: any) => {
if(user) {
this.user = user;
this.user.href = user._links.self.href;
} else {
console.log(`User with id '${id}' not found, returning to list`);
this.gotoList();
}
});
}
});
}
Angular 服务:
@Injectable({
providedIn: 'root'
})
export class UserService {
public API = '//localhost:8080';
public USER_API = this.API + '/users';
constructor(private http: HttpClient) {
}
getAll(): Observable<any> {
return this.http.get(this.API + '/users');
}
get(id: string) {
return this.http.get(this.USER_API + '/' + id);
}
save(user: any): Observable<any> {
let result: Observable<any>;
if(user.href) {
result = this.http.put(user.href, user);
} else {
result = this.http.post(this.USER_API, user);
}
return result;
}
remove(href: string) {
return this.http.delete(href);
}
}
JPA 用户对象:
@Entity
@NoArgsConstructor
public class User extends BaseEntity {
@Basic
private String password;
@Basic
@Column(unique = true, length = 100, columnDefinition = "VARCHAR(100)")
private String username;
@OneToOne
@JoinColumn(name = "person_id")
private Person person;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
JPA Person 对象:
@Entity
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class Person extends BaseEntity {
@Basic(optional = false)
@Column(nullable = false, columnDefinition = "VARCHAR(255)")
private String firstName;
@Basic(optional = false)
@Column(nullable = false, columnDefinition = "VARCHAR(255)")
private String lastName;
@OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
@JsonIgnore
private User user;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
对象初始化:
- 您基本上可以像这样用虚拟数据初始化您的对象:
user: any = {
person: {
firstName: 'john',
...
},
...
};
- 更好的方法是让您对象未定义
user: any;
并检查模板中值的存在性{{ user?.person.firstName }}
不会抛出未定义的错误
嵌套订阅: 为避免嵌套订阅,您可以像这样使用 rxjs operator switchMap:
ngOnInit(): void {
this.sub = this.route.params.pipe(
switchMap(
params => {
const id = params.id;
if (id) {
return this.userService.get(id);
} else {
return of(null);
}
}
).subscribe((user: any) => {
if(user) {
this.user = user;
this.user.href = user._links.self.href;
} else {
console.log(`User with id '${id}' not found, returning to list`);
this.gotoList();
}
});
}
有关 switchMap 工作的更多信息:https://www.learnrxjs.io/learn-rxjs/operators/transformation/switchmap
必须做出一些关于架构的基本决定。
首先,您应该介绍User和Person的类型定义。为 User
命名 3 个可能的定义:
- 一个用户对象总是包含一个人对象:
interface User {
...
person: Person;
...
}
- 一个用户对象可能包含一个人对象:
interface User {
...
person?: Person;
...
}
- 用户对象包含人员对象的 ID:
interface User {
...
_links: {
person: string
};
...
}
User
类型的设计影响了谁负责获取人员数据的问题,即调用 http://localhost:8080/users/{id}/person
。如果您选择选项 1,那么它应该是 UserService 的任务,并且 UserEditComponent 永远不会看到没有初始化 Person 的 User 对象。使用选项 3,UserEditComponent 将为用户和人员提供两个单独的属性,组件的任务是显式获取人员。选项2位于中间,两种初始化方式都是合理的。
此外,this.user.href = user._links.self.href;
似乎也不是一个组件应该做的事情。服务负责转换从后端接收到的数据。
我希望这个答案朝着正确的方向发展。欢迎在评论中进一步讨论。