Reputation: 15
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>
Upvotes: 0
Views: 45
Reputation: 3203
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 operator and async pipe you can achieve both things while simplifying the code and making it less error prone.
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.
Upvotes: 1