英文:
How to reload view without reload whole page?
问题
以下是您要翻译的内容:
"I have three components. A parent and two children.
One child (the add-friend) wants to update the parents view. So i created the method receiveReloadFriendLists()
.
I let the method write me an info into the console. So this works fine.
But the view don't gets updated. Now there should be a list of friends. What do i need to do for this?
I don't want to reload the page, because there is an animation running. Which would get killed.
I am sure i missed something in the parent or did something wrong.
I would be happy for help or links to a solution.
I followed the first answer of this question
But it didn't fulfill my question.
Parent:
@Component({
selector: 'app-friends',
templateUrl: './friends.component.html'
})
export class FriendsComponent implements OnInit {
requested$: Observable<RelationResponse[]>;
requested: RelationResponse[] = [];
constructor(
private store: Store,
private router: Router,
private appStore: Store<AppState>,
private _fb: FormBuilder
) {
this.requested$ = this store.select(selectRequestedRelationResponse);
}
ngOnInit(): void {
this.getRequestedRelations();
}
private getRequestedRelations() {
this.store.dispatch(invokeFindRequestedRelations({
userId: StorageUtil.getUserId(),
relationStatusRequest: RelationStatusRequest.REQUESTED
})
);
this.appStore.pipe(select(selectAppState)).subscribe((appState) => {
if (ApiUtil.ifApiStatusIsSuccessReset(appState, this.appStore)) {
this.requested$.subscribe((relationResponses) => {
this.requested = relationResponses;
})
}
}
);
}
receiveReloadFriendLists() {
console.log("reload");
this.getRequestedRelations();
}
}
Parent html:
<app-add-friend (reloadRelationsEvent)="receiveReloadFriendLists()" class="p-0"></app-add-friend>
<app-relation-list class="p-0"
[show]=hasFriendRequests()
showHeadline="Friend requests"
noShowHeadline="No Friend requests"
[relations]="requested"
[enableAccept]="true"
[enableBlock]="true"
[enableDelete]="true"></app-relation-list>
Child - Add Friend
@Component({
selector: 'app-add-friend',
templateUrl: './add-friend.component.html'
})
export class AddFriendComponent {
@Output() reloadRelationsEvent = new EventEmitter<void>();
@ViewChild('sendFriendRequestCallout') sendFriendRequestCallout!: CalloutComponent;
addFriendForm: FormGroup;
constructor(private _fb: FormBuilder,
private store: Store) {
this.addFriendForm = this.createAddFriendForm();
}
public createAddFriendForm(): FormGroup {
// ...
}
sendFriendRequest() {
// ...
}
reloadFriendLists() {
this.reloadRelationsEvent.emit();
}
}
Child - Show Friends
@Component({
selector: 'app-relation-list',
templateUrl: './relation-list.component.html'
})
export class RelationListComponent {
@Input() show: boolean = false;
@Input() showHeadline: string = '';
@Input() noShowHeadline: string = '';
@Input() relations: RelationResponse[] = [];
@Input() enableAccept: boolean = false;
@Input() enableBlock: boolean = false;
@Input() enableDelete: boolean = false;
// ...
}
Child - Show Friends - html
<div class="card p-3 mt-3">
<h5 class="card-title">{{ showHeadline }}</h5>
<ng-container *ngFor="let relation of relations">
{{relation.contactUsername }}
</ng-container>
</div>
英文:
I have three components. A parent and two children.
One child (the add-friend) wants to update the parents view. So i created the method receiveReloadFriendLists()
.
I let the method write me an info into the console. So this works fine.
But the view don't gets updated. Now there should be a list of friends. What do i need to do for this?
I don't want to reload the page, because there is an animation running. Which would get killed.
I am sure i missed something in the parent or did something wrong.
I would be happy for help or links to a solution.
I followed the first answer of this question
But it didn't fullfil my question.
Parent:
@Component({
selector: 'app-friends',
templateUrl: './friends.component.html'
})
export class FriendsComponent implements OnInit {
requested$: Observable<RelationResponse[]>;
requested: RelationResponse[] = [];
constructor(
private store: Store,
private router: Router,
private appStore: Store<AppState>,
private _fb: FormBuilder
) {
this.requested$ = this.store.select(selectRequestedRelationResponse);
}
ngOnInit(): void {
this.getRequestedRelations();
}
private getRequestedRelations() {
this.store.dispatch(invokeFindRequestedRelations({
userId: StorageUtil.getUserId(),
relationStatusRequest: RelationStatusRequest.REQUESTED
})
);
this.appStore.pipe(select(selectAppState)).subscribe((appState) => {
if (ApiUtil.ifApiStatusIsSuccessReset(appState, this.appStore)) {
this.requested$.subscribe((relationResponses) => {
this.requested = relationResponses;
})
}
}
);
}
receiveReloadFriendLists() {
console.log("reload");
this.getRequestedRelations();
}
}
Parent html:
<app-add-friend (reloadRelationsEvent)="receiveReloadFriendLists()" class="p-0"></app-add-friend>
<app-relation-list class="p-0"
[show]=hasFriendRequests()
showHeadline="Friend requests"
noShowHeadline="No Friend requests"
[relations]="requested"
[enableAccept]="true"
[enableBlock]="true"
[enableDelete]="true"></app-relation-list>
Child - Add Friend
@Component({
selector: 'app-add-friend',
templateUrl: './add-friend.component.html'
})
export class AddFriendComponent {
@Output() reloadRelationsEvent = new EventEmitter<void>();
@ViewChild('sendFriendRequestCallout') sendFriendRequestCallout!: CalloutComponent;
addFriendForm: FormGroup;
constructor(private _fb: FormBuilder,
private store: Store) {
this.addFriendForm = this.createAddFriendForm();
}
public createAddFriendForm(): FormGroup {
...
}
sendFriendRequest() {
....
}
reloadFriendLists() {
this.reloadRelationsEvent.emit();
}
}
Child - Show Friends
@Component({
selector: 'app-relation-list',
templateUrl: './relation-list.component.html'
})
export class RelationListComponent {
@Input() show: boolean = false;
@Input() showHeadline: string = '';
@Input() noShowHeadline: string = '';
@Input() relations: RelationResponse[] = [];
@Input() enableAccept: boolean = false;
@Input() enableBlock: boolean = false;
@Input() enableDelete: boolean = false;
...
}
Child - Show Friends - html
<div class="card p-3 mt-3">
<h5 class="card-title">{{ showHeadline }}</h5>
<ng-container *ngFor="let relation of relations">
{{relation.contactUsername }}
</ng-container>
</div>
答案1
得分: 1
以下是您要求的翻译:
你的代码中有一个非常奇怪的设计,值得重构。你正在创建一些疯狂的订阅,然后尝试使用 `Util` 函数对它们进行过滤。而且你并不关心取消订阅。这意味着即使组件被销毁,你的订阅仍然存在,这可能会导致一些奇怪的错误。
通过使用 [filter][1] 操作符和 [async][2] 管道,你可以在简化代码的同时实现这两个目标,同时降低错误发生的可能性。
```typescript
export class FriendsComponent implements OnInit {
requested$: Observable<RelationResponse[]>;
//requested: RelationResponse[] = []; // 不需要
constructor(
private store: Store,
private router: Router,
private appStore: Store<AppState>,
private _fb: FormBuilder
) { }
ngOnInit(): void {
// 不要在组件内部订阅,只需将可观察对象赋值给变量,并直接在模板中使用它
this.requested$ = this.store.select(selectRequestedRelationResponse).pipe(
// 使用 rxjs filter 操作符,只关心满足条件的更新(就像在订阅中所做的那样)
filter(() => ApiUtil.ifApiStatusIsSuccessReset(appState, this.appStore))
);
this.receiveReloadFriendLists();
}
receiveReloadFriendLists() {
// 仅使用此方法分派一个动作
console.log("reload");
this.store.dispatch(invokeFindRequestedRelations({
userId: StorageUtil.getUserId(),
relationStatusRequest: RelationStatusRequest.REQUESTED
}));
}
}
然后,在父 HTML 中进行一个小的更改,使用 async
管道将值分配给带有 requested$
可观察对象的关系。这样你就无需担心手动取消订阅(在你的代码中你没有这样做)。
<app-add-friend (reloadRelationsEvent)="receiveReloadFriendLists()" class="p-0"></app-add-friend>
<app-relation-list class="p-0"
[show]=hasFriendRequests()
showHeadline="Friend requests"
noShowHeadline="No Friend requests"
[relations]="requested$ | async"
[enableAccept]="true"
[enableBlock]="true"
[enableDelete]="true"
>
</app-relation-list>
解释我在评论中的问题的含义。你可以在子组件中直接获取存储的一部分,然后就不需要在子组件和父组件之间传递值了。对于 invokeFindRequestedRelations
动作也是同样适用的。它可以直接从子组件中分派。
@Component({
selector: 'app-relation-list',
templateUrl: './relation-list.component.html'
})
export class RelationListComponent implements OnInit {
relations$!: Observable<RelationResponse[]>;
@Input() show: boolean = false;
@Input() showHeadline: string = '';
@Input() noShowHeadline: string = '';
// relations 不再需要作为输入
@Input() enableAccept: boolean = false;
@Input() enableBlock: boolean = false;
@Input() enableDelete: boolean = false;
constructor(private appStore: Store<AppState>) {}
ngOnInit(): void {
this.relations$ = this.store.select(selectRequestedRelationResponse);
// 同样,使用它与异步管道
}
}
看看哪种方式更适合你,或者尝试两种方法以更好地理解它。
<details>
<summary>英文:</summary>
There's a very strange design in your code which is worth refactoring. You're making some crazy subscriptions and trying to filter them using `Util` functions. And you don't care about unsubscribing. This means that your subscriptions are alive even after the component was destroyed and this could lead to some strange errors.
Using a [filter][1] operator and [async][2] pipe you can achieve both things while simplifying the code and making it less error prone.
```lang-typescript
export class FriendsComponent implements OnInit {
requested$: Observable<RelationResponse[]>;
//requested: RelationResponse[] = []; // Not needed
constructor(
private store: Store,
private router: Router,
private appStore: Store<AppState>,
private _fb: FormBuilder
) { }
ngOnInit(): void {
// Don't subscribe within the component, just assign the observable to
// the variable and use it directly in the Template
this.requested$ = this.store.select(selectRequestedRelationResponse).pipe(
// Use rxjs filter operator to only care about updates which pass the condition (as you did within subscribe)
filter(() => ApiUtil.ifApiStatusIsSuccessReset(appState, this.appStore))
);
this.receiveReloadFriendLists();
}
receiveReloadFriendLists() {
// Use this method only to dispatch an action
console.log("reload");
this.store.dispatch(invokeFindRequestedRelations({
userId: StorageUtil.getUserId(),
relationStatusRequest: RelationStatusRequest.REQUESTED
});
}
}
And then one small change in the parent HTML, assign the value to relations with requested$
Observable using the async
pipe. This way you don't need to worry about manually unsubscribing (which you didn't do in your code)
<app-add-friend (reloadRelationsEvent)="receiveReloadFriendLists()" class="p-0"></app-add-friend>
<app-relation-list class="p-0"
[show]=hasFriendRequests()
showHeadline="Friend requests"
noShowHeadline="No Friend requests"
[relations]="requested$ | async"
[enableAccept]="true"
[enableBlock]="true"
[enableDelete]="true"
>
</app-relation-list>
To explain what I meant with my question in the comments. You can take a slice of the store directly in the child component and then you won't need to pass the values up and down between children and parent. Same applies to the
invokeFindRequestedRelations
action. It can be dispatched from the child directly.
@Component({
selector: 'app-relation-list',
templateUrl: './relation-list.component.html'
})
export class RelationListComponent implements OnInit {
relations$!: Observable<RelationResponse[]>;
@Input() show: boolean = false;
@Input() showHeadline: string = '';
@Input() noShowHeadline: string = '';
// relations are no longer needed as an Input
@Input() enableAccept: boolean = false;
@Input() enableBlock: boolean = false;
@Input() enableDelete: boolean = false;
constructor(private appStore: Store<AppState>) {}
ngOnInit(): void {
this.relations$ = this.store.select(selectRequestedRelationResponse)
// And once again, use it with the async pipe
}
}
See what suits you better or try both approaches to get better understanding of it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论