I am testing a web app of firebase login with angular Dart in Chrome browser at localhost, but while clicking on the sign-in the popup for login gets disappeared within 1 sec and doesn't result with successful login. While testing it on Chromium it's working.
Can anyone tell what should I do to fix this?
firebase_service.dart
import 'dart:async';
import 'package:angular/angular.dart';
import 'package:firebase/firebase.dart' as fb;
#Injectable()
class FirebaseService {
fb.User user;
fb.Auth _fbAuth;
fb.GoogleAuthProvider _fbGoogleAuthProvider;
FirebaseService() {
fb.initializeApp(
apiKey: "AIzaSyBOShlCgUeqTL99n32bjWdNlkH1RPk9Xx4",
authDomain: "my-app.firebaseapp.com",
databaseURL: "https://my-app.firebaseio.com",
storageBucket: "my-app.appspot.com",
);
_fbGoogleAuthProvider = new fb.GoogleAuthProvider();
_fbAuth = fb.auth();
_fbAuth.onAuthStateChanged.listen(_authChanged);
}
void _authChanged(fb.User event) {
user = event;
}
Future signIn() async {
try {
await _fbAuth.signInWithPopup(_fbGoogleAuthProvider);
}
catch (error) {
print("$runtimeType::login() -- $error");
}
}
void signOut() {
_fbAuth.signOut();
}
}
app_component.html
<header class="material-header shadow">
<div class="material-header-row">
<material-button icon
class="material-drawer-button" (trigger)="drawer.toggle()">
<material-icon icon="menu"></material-icon>
</material-button>
<span class="material-header-title">{{appTitle}}</span>
<div class="material-spacer"></div>
<nav class="material-navigation">
<material-button class="material-drawer-button" icon
materialTooltip="Notifications"
(trigger)="">
<material-icon icon="notifications"></material-icon>
</material-button>
<div id="popup-sign-out" class="popup-user" *ngIf="fbService.user != null">
<material-button class="material-drawer-button" icon
popupSource
#source="popupSource"
materialTooltip="More options"
(trigger)="basicPopupVisible = !basicPopupVisible">
<material-icon icon="more_vert"></material-icon>
</material-button>
<material-popup defaultPopupSizeProvider
enforceSpaceConstraints
[source]="source"
[(visible)]="basicPopupVisible">
<div header class="custom-header">
<img id="popup-user-image" [src]="fbService.user?.photoURL">
<div class="user-detail">
<div id="user-name">{{fbService.user?.displayName}}</div>
<div id="email">{{fbService.user?.email}}</div>
</div>
</div>
<div group class="custom-popup-body">
<material-list class="popup-list" size="3">
<div group>
<material-list-item class="sign-out-button"
*ngIf="fbService.user != null"
(trigger)="fbService.signOut()">
<material-icon icon="exit_to_app" [style.color]="iconColor"
class="material-list-item-primary" aria-hidden="true">
</material-icon>
Sign out
</material-list-item>
</div>
</material-list>
</div>
</material-popup>
</div>
</nav>
<nav class="material-navigation">
<div id="sign-in" class="user" *ngIf="fbService.user == null">
<material-button raised class="sign-in-button" (trigger)="fbService.signIn()">
<img class="google-icon" src="g-logo.png"/>
Sign In
</material-button>
</div>
<div id="sign-out" class="user" *ngIf="fbService.user != null">
<img id="header-user-image" [src]="fbService.user?.photoURL">
</div>
</nav>
</div>
</header>
app_component.dart
import 'package:angular/angular.dart';
import 'package:angular_components/angular_components.dart';
import 'package:yns_app/services/firebase_service.dart';
#Component(
selector: 'my-app',
styleUrls: const [
'app_component.css',
'package:angular_components/src/components/app_layout/layout.scss.css',
],
templateUrl: 'app_component.html',
directives: const [
materialDirectives,
DeferredContentDirective,
MaterialButtonComponent,
MaterialIconComponent,
MaterialPersistentDrawerDirective,
MaterialToggleComponent,
MaterialListComponent,
MaterialListItemComponent,
ClickableTooltipTargetDirective,
DarkThemeDirective,
MaterialIconTooltipComponent,
MaterialInkTooltipComponent,
MaterialPaperTooltipComponent,
MaterialPopupComponent,
MaterialTooltipDirective,
MaterialTooltipTargetDirective,
MaterialTooltipSourceDirective,
NgIf,
Search,
NameGenerator,
],
providers: const [
materialProviders,
popupBindings,
DefaultPopupSizeProvider,
],
)
class AppComponent {
final FirebaseService fbService;
bool basicPopupVisible = false;
bool end = false;
String get tooltipMsg => 'All the best messages appear in tooltips.';
String get longString => 'Learn more about web development with AngularDart '
'here. You will find tutorials to get you started.';
AppComponent(FirebaseService this.fbService);
}
#Injectable()
PopupSizeProvider createPopupSizeProvider() {
return const PercentagePopupSizeProvider();
}
#Directive(selector: '[defaultPopupSizeProvider]', providers: const [
const Provider(PopupSizeProvider, useFactory: createPopupSizeProvider)
])
class DefaultPopupSizeProvider {}
connect the domain or the sub-domain to the hosted project, under the Hosting section.
Related
I have products in my database and I have created controller on my backend app that I tested and works really good, so now I need to implement that to my frontend. I have product.component.ts file that looks like this
import { Component, OnInit } from "#angular/core";
import { FormControl, FormGroup } from "#angular/forms";
import { debounceTime, switchMap } from "rxjs";
import { ProductService } from "./product.service";
#Component({
selector: 'app-product',
templateUrl: './product.component.html'
})
export class ProductComponent implements OnInit {
products: any[] = [];
productSearchForm!: FormGroup;
page: number = 0;
size: number = 4;
constructor(
private productService: ProductService
) { }
loadMore() {
this.page = this.page+1;
this.productSearchForm.get('searchTerm')?.valueChanges
.pipe(
debounceTime(500),
switchMap(value => {
return this.productService.searchByTermPageable(value, this.page, this.size);
})
).subscribe(data => {
this.products = data;
}, error => {
console.log(error);
// this.products = [];
});
}
ngOnInit(): void {
this.initializeForm();
this.productSearchForm.get('searchTerm')?.valueChanges
.pipe(
debounceTime(500),
switchMap(value => {
return this.productService.searchByTermPageable(value, this.page, this.size);
})
).subscribe(data => {
this.products = data;
}, error => {
console.log(error);
// this.products = [];
});
}
private initializeForm(): void {
this.productSearchForm = new FormGroup({
searchTerm: new FormControl(null)
});
}
}
searchTerm is a query param that is used to find products with name starting with that term. I call function from file product.service.ts that looks like this
import { HttpClient } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { Observable } from "rxjs";
import { environment } from "src/environments/environment";
#Injectable({ providedIn: 'root'})
export class ProductService {
constructor(private httpClient: HttpClient) { }
searchByTerm(searchTerm: string): Observable<any> {
const url = `${environment.apiUrl}product/search/by-name-or-desc?term=${searchTerm}`;
return this.httpClient.get(url);
}
searchByTermPageable(searchTerm: string, page: number, size: number): Observable<any> {
const url = `${environment.apiUrl}product/search/by-name-or-desc?term=${searchTerm}&page=${page}&size=${size}`;
return this.httpClient.get(url);
}
}
When I click the button load more I want to load next 4 products from database, but keep the first 4 products on my html page, so what is the best way to do this? This is my html page, and also I am using Bulma as my css framework
<div class="mt-5">
<form>
<div class="field" [formGroup]="productSearchForm">
<div class="control has-icons-left">
<input class="input" type="text" formControlName="searchTerm" placeholder="Find products">
<span class="icon is-small is-left">
<i class="fas fa-search"></i>
</span>
</div>
</div>
<hr />
</form>
</div>
<div class="columns">
<div class="column" *ngFor="let product of products">
<div class="card">
<div class="card-content">
<div class="media">
<div class="media-content">
<p class="title is-4">{{ product.name }}</p>
<p class="subtitle is-6">{{ product.category?.name}}</p>
</div>
</div>
<div class="content">
{{ product.description }}
</div>
</div>
</div>
</div>
</div>
<button type="button" class="button is-primary" (click)="loadMore()">Load more...</button>
You can to have a new function in your service that return the elements of the page plus the other elements search. If you has a function that return the paginate elements
searchByTermPageable(search:string,page:number,size:number)
{
...
}
Some like
product:any[]=[] //define an empty array where you store the product
searchOld:string=null //and a variable to store the search
getData(search:string,page:number,size:number)
{
if (search!=this.searchOld) //if is a new search
{
this.searchOld=search
this.product=[]
}
return this.searchByTermPageable(search,page,size).pipe(
map((res:any[])=>[...this.product,...res]), //concat the before store product
tap((res:any[])=>{
this.product=res //store in the product the new array
})
)
}
Allow you some like
<div *ngFor="let product of product$|async">
{{product.name}}
</div>
<button (click)="more()">more</button>
pageIndex:number=-1;
product$:Observable<any[]>
more()
{
this.pageIndex++
this.product$=this.dataService.getData("",this.pageIndex,4)
}
The other solution is search all the product and simple use slice pipe
<div *ngFor="let product of allProduct$|async |slice:0:(index+1)*pageSize">
{{product.name}}
</div>
<button (click)="index=index+1">more</button>
a litle stackblitz
I upload less than 2 page pdf document, the pdf viewer is working and load all page like 2 pages quickely. but upload more than 2 pages (ex: 51 pages) the pdf viewer is working but load first two pages only, remaining pages are shown loading icon. help to solve this problem.
My ng2-pdf-viewer version is 7.0.1.
And also used ng2-pdf-viewer#7.0.2.
This is my app.module.ts File
...
import { NgbModule } from '#ng-bootstrap/ng-bootstrap';
import { PdfViewerModule } from 'ng2-pdf-viewer';
...
#NgModule({
declarations: [
.....
],
imports: [
...
NgbModule,
PdfViewerModule
...
],
providers: [],
bootstrap: [AppComponent]
})
This is my HTML File
<input type="file" class="fileInput" id="file_select_id" (change)="pdfDocSelect($event)" accept=".pdf, .eps">
<div class="preview_label" (click)="openPdf(PDF)" id="label_id">
<img class="previewimg"src="assets\img\preview.svg">
<span class="preview_text">Preview</span>
</div>
<ng-template #PDF let-modal id="PdfViewer">
<div class="ModalMainBox">
<div class="modal-content ModalContent">
<div class="modal-header">
<div class="PdfTitle">PDFName</div>
<button type="button" class="PdfClose" aria-label="Close" (click)="modal.dismiss('Cross click')">
<span class="modal_close_icon" aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body PdfSize">
<pdf-viewer class="pdfSrc" [src]="File" [rotation]="0" [fit-to-page]="true" [original-size]="true" [show-all]="true"></pdf-viewer>
</div>
</div>
</div>
</ng-template>
This my TS File
import { NgbModal } from '#ng-bootstrap/ng-bootstrap';
Fie: any;
constructor(private modalService: NgbModal){}
PdfViewerFn(e: any) {
let Pdf: any = document.getElementById('file_select_id');
if (typeof (FileReader) !== 'undefined') {
let reader = new FileReader();
reader.onload = (e) => {
this.File = e.target?.result
}
reader.readAsArrayBuffer(Pdf.files[0]);
}
}
// PDF View for Thesis Upload document
openPdf(pdf: any) {
if (this.File) {
this.modalService.open(pdf, { ariaLabelledBy: 'PdfViewer' }).result;
}
}
pdfDocSelect(event: any): void {
console.log(event.target.files[0]);
this.PdfViewerFn(event)
}
I'm trying to display data without using a ngFor loop. It works perfectly but shows all of the quote information from multiple customers. The CSS is laid out in a way that has the discount div next to the customerinfo div Here is the HTML
<hr />
<div class="info">
<div id="CustomerInfoInline" *ngIf="quotes" >
<div *ngFor="let q of quotes">
<h6>Name: {{q.firstName}} {{q.lastName}}</h6>
<h6>Address: {{q.address}}</h6>
<h6>City, State, Zip: {{q.city}}, {{q.state}}, {{q.zip}}</h6>
<h6>SSN: {{q.SSN}}</h6>
<h6>DOB: {{q.dateOfBirth}}</h6>
<h6>Email: {{q.email}}</h6>
<h6>Prev. Carrier: {{q.previousCarrier}}</h6>
<h1>-----------------------------------------------------------------------------------------------</h1>
</div>
</div>
<div *ngIf="quotes">
<div id="CustomerDiscountsInline" *ngFor="let q of quotes">
<h6 id="customerBold">Customer Discounts</h6>
<h4 id="DiscountsID">discounts will be applied here</h4>
</div>
</div>
</div>
<hr />
and the respective TS
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { environment } from '#environments/environment';
import { Quote } from '#app/_models/quote';
import { Router } from '#angular/router';
#Component({
selector: 'app-quote-summary',
templateUrl: './quote-summary.component.html',
styleUrls: ['./quote-summary.component.css']
})
export class QuoteSummaryComponent implements OnInit {
apiUrl: string = environment.apiUrl
quotes: Quote[]
//TODO: implement submitted quote view later
//submittedQuotes: Quote[]
constructor(private http: HttpClient, private router: Router) { }
ngOnInit(): void {
this.getQuotes()
}
// #region API Calls
getQuotes() {
var httpRequest = this.http.get<Quote[]>(`${this.apiUrl}/quotes`)
httpRequest.subscribe(returnedQuotes => {
this.quotes = returnedQuotes
})
}
}
If you need to show only one customer you can use indexer for quotes like quotes[0]:
Don't forgot to check quotes.length > 0:
<div class="info">
<div id="CustomerInfoInline"
<div *ngIf="quotes && quotes.length > 0">
<h6>Name: {{quotes[0].firstName}} {{quotes[0].lastName}}</h6>
<h6>Address: {{quotes[0].address}}</h6>
....
</div>
</div>
</div>
I am learning MEAN stack. I've deployed my application here. Right now If you click on any card i.e. Read more button, it will automatically take you to a division where contenets are displayed. But I want to show all that content on a separate route or page because now I am planning to provide some more useful options such as Like, Report, Upvote,Donwvote`, etc. So I made some changes. Please look at them.
articles.component.html
<div class="row mt-5">
<div class="col-md-4 mb-3" *ngFor="let article of articles; let i = index;">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title">{{article.title}}</h5>
<a (click)="onPress(i)" class="btn btn-primary">Read More</a>
</div>
<div class="card-footer text-muted">
{{article.date}}
</div>
</div>
</div>
</div>
articles.component.ts
import { Component, OnInit } from '#angular/core';
import { ArticlesService } from '../articles.service'; <---- SERVICE FOR READING FROM MONGODB
import {Router, ActivatedRoute }from '#angular/router';
#Component({
...
})
export class ArticlesComponent implements OnInit {
articles=[]
constructor(private _articleService: ArticlesService, private router: Router) { }
ngOnInit() {
this._articleService.getAllArticles()
.subscribe(
res => this.articles = res,
err => console.log(err)
)
}
onPress(id) {
console.log(id); <--- THIS PRINTS INDEX NO OF THE CARD
this.router.navigate(['/options',id]);
}
}
And all the options I mentioned above I've kept them in a separate component.
options-panel.component.html
<div style="margin: 0 auto; width:50vw">
<p> {{data }} </p>
<button (click)="back()"> Back</button>
<div style="display:flex; margin-top:1rem;padding:1rem;">
<button style="margin:0.5rem;"> upVote </button>
<button style="margin:0.5rem;"> DownVote </button>
...
</div>
</div>
options-panel.component.ts
import { Component, OnInit } from '#angular/core';
import {ActivatedRoute,Router } from '#angular/router';
#Component({
...
})
export class OptionsPanelComponent implements OnInit {
private data;
constructor(private router: Router,private activatedRoute: ActivatedRoute) { }
ngOnInit() {
this.activatedRoute.paramMap.subscribe(id =>{
this.data = id.get('id');
})
}
back(){
this.router.navigate(['../'])
}
}
And please review my app-routing.module.ts
...
import { ArticlesComponent } from './articles/articles.component';
import { OptionsPanelComponent } from './options-panel/options-panel.component';
const routes: Routes = [
{path: 'articles', component: ArticlesComponent},
{path:'',redirectTo: 'articles', pathMatch:'full'},
{path: 'options/:id', component:OptionsPanelComponent}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
PS: I read documentation and dependancy injection in Angular. I also tried this stackblitz project.
But I'm getting no errors but blank page.
Please tell me what else I should do now.
What you showed in your picture was working, but you are printing just the id. You must get the article with that id from somewhere, like you service.
There is some information missing about your project, but you could make something like this.
Get the id from the route.
Retrieve your article from your service.
Show it on front using async if possible.
options-panel.component.ts
#Component({
...
})
export class OptionsPanelComponent implements OnInit {
private id: number;
private article$: Observable<Article>;
constructor(private router: Router, private activatedRoute: ActivatedRoute, private _articleService: ArticlesService) { }
ngOnInit() {
this.id = Number(this.activateRoute.snapshot.params['id']);
if (this.id) {
this.article$ = this._articleService.getArticle(this.id);
}
}
back(){
this.router.navigate(['../'])
}
}
options-panel.component.html
<div style="margin: 0 auto; width:50vw" *ngIf="article$ | async as article">
<p> {{ article.content }} </p>
<button (click)="back()"> Back</button>
<div style="display:flex; margin-top:1rem;padding:1rem;">
<button style="margin:0.5rem;"> upVote </button>
<button style="margin:0.5rem;"> DownVote </button>
...
</div>
</div>
If you are seeing that Welcome to my app! and logo, it must be on the index.html file.
If I am not wrong then, you want to create a separate link for each read more. In that case, you have to add router-link in your html and you have to update your routes as well. Try this one:
Your html:
<div class="row mt-5">
<div class="col-md-4 mb-3" *ngFor="let article of articles; let i = index;">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title">{{article.title}}</h5>
<a [routerLink]="['/'+article.id]" routerLinkActive="router-link-active" (click)="onPress(i)" class="btn btn-primary">Read More</a>
</div>
<div class="card-footer text-muted">
{{article.date}}
</div>
</div>
</div>
</div>
this html will generate links for each read more. you also have to update your router and have to put your router-outlet in your desired position.
const routes: Routes = [
{
path: 'articles', component: ArticlesComponent,
children: [
{
path: 'id',
component: 'Read more component'
}
]
},
{ path: '', redirectTo: 'articles', pathMatch: 'full' },
{ path: 'options/:id', component: OptionsPanelComponent }
];
My project contains 4 components: Course-List, Course-Detail, Course-Play, Course Quiz, and the scheme is like that:
The data is pass like that: every course have array of segment and every segments have array of questions. All the data I get from a backend server (Ruby on Rails API project) and it pass correctly.
This is the interfaces of each data structure:
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;
}
I'm having a problem with course-quiz component.
This is how the quiz looks:
Problems:
When click submit and next the data's not saving
I want to add option of coloring the correct answer and user answer after press submit
I want that when all questions are answered that new page show instead (not working probably because the first)
This is the project in stackblitz. The project not working because stackblitz isn't working with sass. I'll add relevant code also here:
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>