我的项目包含4个组件:“课程列表”,“课程详细信息”,“课程播放”,“课程测验”,方案如下: 数据是这样传递的:每个课程都有部分数组,每个部分都有问题数组。我从后端服务器(Ruby on Rails API项目)获得的所有数据均正确传递。
这是每个数据结构的接口:
export interface ICourse {
id: number;
title: string;
autor: string;
segments: ISegment[];
}
export interface ISegment {
id: number;
unit_id: number;
unit_title: string;
name: string;
type: string;
data: string;
questions: IQuestion[];
}
export interface IQuestion {
id: number;
question: string;
answer1: string;
answer2: string;
answer3: string;
answer4: string;
correct: number;
}
问题:
This is the project in stackblitz。该项目无法正常工作,因为stackblitz无法与sass一起工作。我还将在此处添加相关代码:
app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { CourseListComponent } from './courses/course-list/course-list.component';
import { CourseDetailComponent } from './courses/course-detail/course-detail.component';
import { CoursePlayComponent } from './courses/course-play/course-play.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { CourseQuizComponent } from './courses/course-play/course-quiz/course-quiz.component';
// Routing array - set routes to each html page
const appRoutes: Routes = [
{ path: '', redirectTo: '/courses', pathMatch: 'full', runGuardsAndResolvers: 'always' },
{ path: 'courses', component: CourseListComponent, pathMatch: 'full', runGuardsAndResolvers: 'always' },
{ path: 'courses/:id', component: CourseDetailComponent, pathMatch: 'full', runGuardsAndResolvers: 'always' },
{ path: 'courses/:id/segments/:id', component: CoursePlayComponent, pathMatch: 'full', runGuardsAndResolvers: 'always',
children: [{ path: 'questions/:id', component: CourseQuizComponent }]
},
{ path: '**', component: PageNotFoundComponent, pathMatch: 'full', runGuardsAndResolvers: 'always' }];
@NgModule({
imports: [RouterModule.forRoot(appRoutes, { onSameUrlNavigation: 'reload' })],
exports: [RouterModule]
})
export class AppRoutingModule { }
course-play.component.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 { DomSanitizer } from '@angular/platform-browser';
import { ICourse } from '../course';
import { ISegment } from '../course';
import { CourseService } from '../course.service';
// Couse-play decorator
@Component({
selector: 'lg-course-play-course-play',
templateUrl: './course-play.component.html',
styleUrls: ['./course-play.component.sass']
})
export class CoursePlayComponent implements OnInit {
errorMessage: string;
course: ICourse;
courseId: number;
public currentSegment: ISegment;
public showChildren: boolean = false;
constructor(private courseService: CourseService,
private route: ActivatedRoute,
private router: Router,
public sanitizer: DomSanitizer) { }
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(this.courseId).subscribe(
course => {
this.course = course;
// get the current segment id to use it on the html file
const id = +this.route.snapshot.paramMap.get('id');
this.getCurrentSegment(id);
},
error => this.errorMessage = <any>error);
}
// search in course single segment by id and save it in currentSegment
// to use it in the html file
getCurrentSegment(id: number){
this.currentSegment = this.course.segments.find(d => d.id === id);
}
changeShowChildren() {
this.showChildren = !this.showChildren;
}
}
course-play.component.html
<div class="row content" *ngIf="course">
<!-- Side nav-bar -->
<div class="col-md-3">
<!-- Image Logo -->
<div id="head_sidebar">
<img src="./assets/images/lg-white.png" class="d-inline-block align-top logo" alt="" routerLink="/courses" style="outline: none">
<h3>{{course.title}}</h3>
</div>
<div class="col-md-12 sidenav">
<!-- Menu elemets -->
<div class="nav nav-pills nav-stacked" *ngFor="let unit of course.segments | groupBy: 'unit_title'; let i = index">
<h6 class="course_play_title">Unit {{ i+1 }}: {{ unit.key }} </h6>
<ul>
<li class="course_play_item" *ngFor="let lesson of unit.value">
<a class="nav-link" [routerLink]="['/courses', course.id, 'segments', lesson.id]" (click)=getCurrentSegment(lesson.id)>{{lesson.name}}</a>
</li>
</ul>
</div>
</div>
</div>
<!-- Body -->
<div class="col-md-9 no-gutters" *ngIf="currentSegment">
<!-- Video Div -->
<div class="col-md-12 course_play_body text-center" *ngIf="currentSegment.segment_type === 'Video'">
<h1>{{currentSegment.name}}</h1>
<p class="small-text" *ngIf="course.segments?.length > 0">lesson {{currentSegment.id}} of {{course.segments?.length}}</p>
<hr>
<iframe frameborder="0" allowfullscreen="true" [src]='currentSegment.data | safe'></iframe>
<button type="button" class="prev btn btn-primary btn-lg" *ngIf="currentSegment.id > 1" [routerLink]="['/courses', course.id, 'segments', currentSegment.id-1]" (click)=getCurrentSegment(currentSegment.id-1)>Previous</button>
<button type="button" class="next btn btn-primary btn-lg" *ngIf="currentSegment.id < course.segments?.length" [routerLink]="['/courses', course.id, 'segments', currentSegment.id+1]" (click)=getCurrentSegment(currentSegment.id+1)>Next</button>
</div>
<!-- Quiz Div -->
<div class="col-md-12 course_play_body" *ngIf="currentSegment.segment_type === 'Quiz'">
<div class="col-md-12 course_play_body text-center" *ngIf="showChildren === false">
<h1>{{currentSegment.name}}</h1>
<p class="text-left"> Now that you've finished this unit, It's time to take a short quiz and see what you learned so far!
You'll need to choose one out of four answers which you think is correct.
After you've finished the quiz, you'll get your grade. feel free to re-take this quiz as much as you like.
Good Luck!
</p>
<p class="text-left big-text"> {{currentSegment.questions.length}} questions </p>
<a (click) = "showChildren = true"><h4>Start Quiz</h4></a>
<button style="margin-top: 30%" type="button" class="prev btn btn-primary btn-lg" *ngIf="currentSegment.id > 1" [routerLink]="['/courses', course.id, 'segments', currentSegment.id-1]" (click)=getCurrentSegment(currentSegment.id-1)>Previous</button>
<button style="margin-top: 30%" type="button" class="next btn btn-primary btn-lg" *ngIf="currentSegment.id < course.segments?.length" [routerLink]="['/courses', course.id, 'segments', currentSegment.id+1]" (click)=getCurrentSegment(currentSegment.id+1)>Next</button>
</div>
<quiz-course *ngIf="showChildren === true" [items]="currentSegment?.questions"></quiz-course>
</div>
</div>
course-quiz.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute, Router, Routes, NavigationEnd } from '@angular/router';
import { ICourse } from '../../course';
import { ISegment } from '../../course';
import { IQuestion } from '../../course';
import { CourseService } from '../../course.service';
import { PagerService } from '../../pager.service';
import * as _ from 'underscore';
@Component({
selector: 'quiz-course',
templateUrl: './course-quiz.component.html',
styleUrls: ['./course-quiz.component.sass']
})
export class CourseQuizComponent implements OnInit {
// the data I get from course-play component
@Input() items: IQuestion[];
// variables for pagination
pagedItems: IQuestion[];
pager: any = {};
public userAnswers = ['0', '0', '0', '0']; // array of user answers
public index: int; // index of userAnswers
public checkedAnswer: int; // the checked answer the user choose
public correctAnswer: boolean = false; // true if his answer correct, else false
public sum: int; // sum of the questions answered. needed to check if user finished quiz
constructor(private courseService: CourseService,
private route: ActivatedRoute,
private router: Router,
private pagerService: PagerService) { }
ngOnInit() {
this.setPage(1);
this.checkedAnswer = 0;
this.index = 0;
this.sum = 0;
}
// change pages
setPage(page: number) {
if (page < 1 || page > this.pager.totalPages) {
return;
}
// get pager object from service
this.pager = this.pagerService.getPager(this.items.length, page);
// get current page of items
this.pagedItems = this.items.slice(this.pager.startIndex, this.pager.endIndex + 1);
}
isChecked(value){
this.checkedAnswer = value;
}
// get value of the checked answer, check if it's correct and save it to the answer array
submitAnswer(correct) {
// if the user answer all the questions, go to finished page
if (sum == this.items.length) {
}
if (this.checkedAnswer == 0) {
// do something to notify user that an answer need to be checked
}
else if (this.checkedAnswer == correct) {
this.correctAnswer = true;
this.userAnswers[this.index] = "correct";
}
else {
this.correctAnswer = false;
this.userAnswers[this.index] = "incorrect";
}
this.index = this.index + 1;
this.sum = this.sum + 1;
}
}
course-quiz.component.html
<div class="container" *ngIf="sum < 4">
<div class="text-left quiz-body" *ngFor="let item of pagedItems">
<form>
<!-- items being paged -->
<h3>Question {{item.id}}/{{items.length}}</h3>
<h6>Question {{item.question}}</h6>
<ul class="items">
<li><input type="radio" id="answer1" name="answer" value="1" (click)="isChecked(1)"><label for="answer1">1. {{item.answer1}}</label></li>
<li><input type="radio" id="answer1" name="answer" value="2" (click)="isChecked(2)"><label for="answer2">2. {{item.answer2}}</label></li>
<li><input type="radio" id="answer1" name="answer" value="3" (click)="isChecked(3)"><label for="answer3">3. {{item.answer3}}</label></li>
<li><input type="radio" id="answer1" name="answer" value="4" (click)="isChecked(4)"><label for="answer4">4. {{item.answer4}}</label></li>
</ul>
<button type="submit" class="btn btn-primary mb-2" (click)="submitAnswer(item.correct)">Submit</button>
<!-- Submit Buttom -->
<!-- pager -->
<ul *ngIf="pager.pages && pager.pages.length" class="pagination">
<li class="page-item" [ngClass]="{disabled:pager.currentPage === 1}">
<a class="page-link" (click)="setPage(1)">First</a>
</li>
<li class="page-item" [ngClass]="{disabled:pager.currentPage === 1}">
<a class="page-link" (click)="setPage(pager.currentPage - 1)">Previous</a>
</li>
<li class="page-item" *ngFor="let page of pager.pages" [ngClass]="{active:pager.currentPage === page}">
<a class="page-link" (click)="setPage(page)">{{page}}</a>
</li>
<li class="page-item" [ngClass]="{disabled:pager.currentPage === pager.totalPages}">
<a class="page-link" (click)="setPage(pager.currentPage + 1)">Next</a>
</li>
<li class="page-item" [ngClass]="{disabled:pager.currentPage === pager.totalPages}">
<a class="page-link" (click)="setPage(pager.totalPages)">Last</a>
</li>
</ul>
</form>
</div>
</div>
<!-- If the user finished the quiz, this div will displaying instead -->
<div class="container" *ngIf="sum == 4">
<h3> You have just finished the quiz! </h3>
</div>