我有一个用户和课程的项目,如果用户看了课或完成了测验,我会更新数据库。修补程序之后,我可以看到数据库正确更新,但是当我在更新后调用get来获取新数据时,数据保持与更新前相同。只有刷新页面后,我才能看到更改。
course-play.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router, Routes, NavigationEnd } from '@angular/router';
import { MatSidenavModule } from '@angular/material/sidenav';
import { LocalStorage } from '@ngx-pwa/local-storage';
import { ICourse, IUnit, IVideo, IQuiz } from '../course';
import { CourseService } from '../course.service';
import { UserProgressService } from '../../users/user-progress.service';
export class CoursePlayComponent implements OnInit {
// configurations
errorMessage: string;
course: ICourse;
public current: IVideo;
currentUnitQuiz: IQuiz;
courseId: number;
totalLessons: number; // all lessons in the course
totalQuizzes: number; // all quizzes in the course
// ... more configs not important for now ...
constructor(private courseService: CourseService,
private route: ActivatedRoute,
private router: Router,
private userProgressService: UserProgressService) {
userProgressService.connectUser();
}
ngOnInit() {
// ...
// save this course id from course-detail and get http request from the service
this.courseId = JSON.parse(localStorage.getItem("courseId"));
this.getCourse(this.courseId);
}
// Get course detail by id
getCourse(id: number) {
this.courseService.getCourse(id).subscribe(
course => {
this.course = course;
// .. more code here ..
},
error => this.errorMessage = <any>error);
}
// calculate which quiz is now and save it to currentUnitQuiz
getQuiz(currentUnitPosition: number) {
// ...
}
// calculate which lesson is now and save it to current
getCurrent(unitPosition: number, lessonPosition: number) {
// ...
}
// .. more functions ...
// EventEmitter from course-lesson component and send the data I need to update
watched(state) {
this.userProgressService.addToCompletedLessons(this.course.id, this.current.id,
this.totalLessons, this.totalQuizzes);
}
// EventEmitter from course-quiz component and send the data I need to update
finishedQuiz(state) {
this.userProgressService.addToCompletedQuizzes(this.course.id, this.currentUnitQuiz.id,
this.totalLessons, this.totalQuizzes);
}
}
play.html
<mat-sidenav-container fullscreen *ngIf="course">
<mat-sidenav class="app-sidenav">
<!-- more code that's not important for now -->
</mat-sidenav>
<mat-toolbar id="appToolbar">
<!-- .... -->
</mat-toolbar>
<div class="body">
<course-lesson *ngIf="showLesson == true" [lessonId]="current?.id" [lessonName]="current?.name" [lessonData]="current?.data" [totalLessons]="totalLessons" (clicked)="watched($event)"></course-lesson>
<quiz-course *ngIf="showQuiz == true" [quiz]="currentUnitQuiz" [minCorrectAnswers]="currentUnitQuiz?.min_correct_answers" (passed)="finishedQuiz($event)"></quiz-course>
<router-outlet></router-outlet>
</div>
</mat-sidenav-container>
user-progress.service
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { BehaviorSubject } from 'rxjs';
import { Observable, throwError } from 'rxjs';
import { catchError, groupBy } from 'rxjs/operators';
import { LocalStorage } from '@ngx-pwa/local-storage';
import { UserService } from './user.service';
import { AuthUserService } from './auth-user.service';
import { IUser, IUserCourses } from './user';
export class UserProgressService {
private user: IUser;
private errorMessage: string;
constructor(private authUser: AuthUserService, private userService: UserService) { }
// get user from local store
connectUser() {
this.user = JSON.parse(localStorage.getItem('user'));
}
// update user after changes - call the backend again for GET
updateUser() {
this.userService.getUser(this.user.id).subscribe(
user => {
this.user = user;
localStorage.setItem('user', JSON.stringify(this.user));
console.log(this.user); // <--- print the old data, before changes
},
error => this.errorMessage = <any>error
);
}
// update complete lessons of a certain course
addToCompletedLessons(course_id: number, lesson_id: number, totalLessons: number,
totalQuizzes: number) {
let userCourses = this.user.user_courses;
let userLessons;
// .. actions to know if we need to update or the lesson is already in database
// add the lesson to database
const updatedLessons = userLessons + `,${lesson_id}`;
this.userService.updateCompletedLessons(this.user.id, course_id,
updatedLessons).subscribe(
() => {
console.log("PATCH call successful value returned in body");
},
error => this.errorMessage = <any>error
);
// get from backend updated data
this.updateUser();
}
// update complete lessons of a certain course
addToCompletedQuizzes(course_id: number, quiz_id: number, totalLessons: number,
totalQuizzes: number) {
let userCourses = this.user.user_courses;
let userQuizzes;
// .. actions to know if we need to update or the quiz is already in database
// add the quiz to the list
const updatedQuizzes = userQuizzes + `,${quiz_id}`;
this.userService.updateCompletedQuizzes(this.user.id, course_id,
updatedQuizzes).subscribe(
() => {
console.log("PATCH call successful value returned in body");
},
error => this.errorMessage = <any>error
);
// get from backend updated data
this.updateUser();
}
user.service
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http';
import { BehaviorSubject } from 'rxjs';
import { Observable, throwError } from 'rxjs';
import { catchError, groupBy, tap } from 'rxjs/operators';
import { IUser, IUserCourses } from './user';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable()
export class UserService {
private url = 'http://localhost:3000/users';
constructor(private http: HttpClient) { }
// Get Single user by id. will 404 if id not found
getUser(id: number): Observable<IUser> {
const detailUrl = `${this.url}/${id}` + '.json';
return this.http.get<IUser>(detailUrl)
.pipe(catchError(this.handleError));
}
// call mark_lesson_as_completed in backend
updateCompletedLessons(user_id: number, course_id: number, param: any): Observable<any> {
const userUrl = `${this.url}/${user_id}` + '.json';
let body = JSON.stringify({
course_id: course_id,
completed_lessons: param
});
return this.http.patch(userUrl, body, httpOptions)
.pipe(
tap(_ => console.log(`updated user ${user_id} with this entry: ${param}`)),
catchError(this.handleError)
);
}
// call mark_quiz_as_completed in backend
updateCompletedQuizzes(user_id: number, course_id: number, param: any): Observable <any> {
const userUrl = `${this.url}/${user_id}` + '.json';
let body = JSON.stringify({
course_id: course_id,
completed_quizzes: param
});
return this.http.patch(userUrl, body, httpOptions)
.pipe(
tap(_ => console.log(`updated user ${user_id} with this entry: ${param}`)),
catchError(this.handleError)
);
}
// // Handle Any Kind of Errors
private handleError(error: HttpErrorResponse) {
// A client-side or network error occured. Handle it accordingly.
if (error.error instanceof ErrorEvent) {
console.error('An error occured:', error.error.message);
}
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong.
else {
console.error(
'Backend returned code ${error.status}, ' +
'body was ${error.error}');
}
// return an Observable with a user-facing error error message
return throwError(
'Something bad happend; please try again later.');
}
}
答案 0 :(得分:0)
在我的一个朋友向我解释后,我解决了它。 看起来PATCH和任何添加或编辑数据库的请求都需要花费时间,并且在程序继续执行其他操作的同时,因此在PATCH请求完成之前调用了我从数据库中获取新数据的更新功能。
我所做的就是在订阅内部调用了update函数,就像这样:
this.userService.updateCompletedLessons(this.user.id, course_id, updatedLessons).subscribe(
() => {
console.log("PATCH call successful value returned in body");
// get from backend updated data
this.updateUser();
},
error => this.errorMessage = <any>error
);