I’m encountering the error
ContestDetailsModalComponent.html:21 ERROR TypeError: Cannot assign to read only property 'winner' of object '[object Object]' at ContestDetailsModalComponent.onWinner (contest-details-modal.component.ts:88:24) at Object.eval [as handleEvent] (ContestDetailsModalComponent.html:21:17) at handleEvent (core.js:43993:77) at callWithDebugContext (core.js:45632:1) at Object.debugHandleEvent [as handleEvent] (core.js:45247:1) at dispatchEvent (core.js:29804:1) at core.js:42925:1 at HTMLButtonElement.<anonymous> (platform-browser.js:2668:1) at ZoneDelegate.invokeTask (zone-evergreen.js:399:1) at Object.onInvokeTask (core.js:39680:1)
when I try to make the assignment
this.contest.winner = winnerId;
you see in the following code snippet:
public onWinner(winnerId: number): void { const post: Post = new Post(); post.title = `The winner of the contest has been elected: ${this.contest.title}`; post.description = `Tizio has been elected winner of the contest and ${this.user.name} gives him the most welcome compliments`; post.user = this.user; post.contest = this.contest; post.type = Type.WINNER; post.level = Level.SUCCESS; this.contest.winner = winnerId; this.store.dispatch(PostActions.savePost({post})); this.store.dispatch(ContestActions.saveContest({contest: this.contest})); }
I’ve done this kind of assignment with other classes around the project and they never bothered me.
I enclose the contest class and post if it is useful:
export class Contest { public id: number; public title: string; public description: string; public rules: string; public startDate: Date; public endDate: Date; public status: Status; public winner: number; public bannedUser: number[]; } export class Post { public id: number; public title: string; public description: string; public level: Level; public type: Type; public user: User; public publishDate: Date; public contest: Contest; }
I also tried some solutions always found on this forum such as:
Object.assign(target, source);
But he rightly tells me that this.contest.winner is null or undefined.
I hope for your help. Thank you
Edit:
This is the entire component.ts where onWinner() is present.
@Component({ selector: 'app-contest-details-modal', templateUrl: './contest-details-modal.component.html', styleUrls: ['./contest-details-modal.component.scss'], }) export class ContestDetailsModalComponent implements OnInit, OnDestroy, AfterViewChecked { private readonly subscriptions: Subscription = new Subscription(); public id: number; public user: User; public contest: Contest; public images: Image[]; public hasVoted: boolean; constructor( private readonly bsModalRef: BsModalRef, private readonly modalService: BsModalService, private readonly store: Store<AppState>, private cdRef: ChangeDetectorRef ) { } public ngOnInit(): void { this.subscriptions.add(this.store.pipe(select(AuthSelectors.getUser)).subscribe((user: User) => { if (user) { this.user = user; } })); this.subscriptions.add(this.store.pipe(select(ContestSelectors.getById)).subscribe((contest: Contest) => { if (contest) { this.contest = contest; } })); this.subscriptions.add(this.store.pipe(select(ImageSelectors.getImages)).subscribe((images: Image[]) => { if (images.length) { this.images = images; } })); this.subscriptions.add(this.store.pipe(select(UserSelectors.check)).subscribe((ack: Ack) => { if (ack) { this.store.dispatch(AuthActions.updatedUser({userId: this.user.id})); this.modalService.show(UploadImageModalComponent); this.bsModalRef.hide(); } })); this.subscriptions.add(this.store.pipe(select(ImageSelectors.check)).subscribe((ack: Ack) => { if (ack) { this.bsModalRef.hide(); } })); } public ngOnDestroy(): void { this.store.dispatch(UserActions.clean()); this.store.dispatch(ContestActions.clean()); this.subscriptions.unsubscribe(); } public onWinner(winnerId: number): void { const post: Post = new Post(); post.title = `The winner of the contest has been elected: ${this.contest.title}`; post.description = `Tizio has been elected winner of the contest and ${this.user.name} gives him the most welcome compliments`; post.user = this.user; post.contest = this.contest; post.type = Type.WINNER; post.level = Level.SUCCESS; this.contest.winner = winnerId; this.store.dispatch(PostActions.savePost({post})); this.store.dispatch(ContestActions.saveContest({contest: this.contest})); } public onJoin(): void { this.store.dispatch(UserActions.saveUser({idUser: this.user.id, id: this.contest.id})); } public onVote(image: Image): void { let vote: number = image.vote; vote = vote + 1; this.store.dispatch(ImageActions.updateImage({photoId: image.id, votes: vote, userId: this.user.id})); this.store.dispatch(AuthActions.updatedUser({userId: this.user.id})); } public isContain(contestId: number): boolean { if (this.user.myContest) { for (const i of this.user.myContest) { if (contestId === i) { return true; } } } return false; } public isImageVoted(id: number): boolean { if (this.user.favouritePhoto) { for (const imageVoted of this.user.favouritePhoto) { if (id === imageVoted) { this.hasVoted = true; return true; } } return false; } } public onBan(image: Image): void { this.modalService.show(BanModalComponent, {initialState : image}); this.bsModalRef.hide(); } public isBan(contestId: number): boolean { if (this.user.whereBanned) { for (const contestBanned of this.user.whereBanned) { if (contestId === contestBanned) { return true; } } return false; } } public ngAfterViewChecked(): void { this.cdRef.detectChanges(); } }
Advertisement
Answer
contest
contains a reference to an Observable emission, and all references emitted by Observables are readonly. Either clone contest
:
this.contest = { ...contest };
Or, better, leave it as an Observable and consume it as such, generally via the async
pipe. Also, if you’re using NgRx, you want to be using store.select()
:
this.contest$ = this.store.select(ContestSelectors.getById);