src/app/course-tabs/course-selection/course-selection.component.ts
Provides the user management for the selected courses, including a course search. overlay to add/remove courses.
| selector | app-course-selection |
| styleUrls | course-selection.component.css |
| templateUrl | ./course-selection.component.html |
Properties |
|
Methods |
constructor(katanaService: KatanaService, courseService: CourseService)
|
||||||||||||
|
Constructor
Parameters :
|
| addAll |
addAll()
|
|
Selects all courses found in the search
Returns :
void
|
| closeCourseSelect |
closeCourseSelect()
|
|
Closes the courseSelect page when the user clicks on the margins of the page
Returns :
void
|
| Async getCourses |
getCourses()
|
|
This retrieves all courses that match the given parameters from Canvas and returns them as an array of course objects that have been modified by us. The returned results will populate the 'courseResults' array and will display in the results table in the course-selection html file.
Returns :
any
|
| isAdded | ||||||||
isAdded(course: any)
|
||||||||
|
Checks to see if the selected course from courseResults is already in the courses array in courseService.
Parameters :
Returns :
boolean
Whether the course is already added. |
| removeAll |
removeAll()
|
|
Deselects all selected courses.
Returns :
void
|
| sortBy | ||||||||
sortBy(param: any)
|
||||||||
|
Sorts the courseResults array according to the course attribute defined by 'param'.
Parameters :
Returns :
void
|
| stopEvent | ||||||
stopEvent(event: )
|
||||||
|
Stops the courseSelect page from closing when the user interacts with the page (area that is not in the margins)
Parameters :
Returns :
void
|
| Private blueprint |
blueprint:
|
Type : ElementRef
|
Decorators : ViewChild
|
|
Element reference to the blueprint filter input. |
| courseResults |
courseResults:
|
Type : Course[]
|
Default value : []
|
|
Results of a search. |
| Public courseService |
courseService:
|
Type : CourseService
|
|
Provides information and management for selected courses.
|
| lastSortedBy |
lastSortedBy:
|
Type : string
|
|
Holds what the last search was sorted by. |
| Private searchBy |
searchBy:
|
Type : ElementRef
|
Decorators : ViewChild
|
|
Element reference to the search-by filter input. |
| searching |
searching:
|
Default value : false
|
|
Whether or not a search is currently processing |
| Private searchText |
searchText:
|
Type : ElementRef
|
Decorators : ViewChild
|
|
Element reference to the search text input. |
| Private subAccount |
subAccount:
|
Type : ElementRef
|
Decorators : ViewChild
|
|
Element reference to the sub-account filter input. |
| Private term |
term:
|
Type : ElementRef
|
Decorators : ViewChild
|
|
Element reference to the term filter input. |
import { Component, ViewChild, ElementRef } from '@angular/core';
import { KatanaService } from '../../katana.service';
import { CourseService } from '../../course.service';
import { Course } from '../../interfaces';
/**
* Provides the user management for the selected courses, including a course search.
* overlay to add/remove courses.
*/
@Component({
selector: 'app-course-selection',
templateUrl: './course-selection.component.html',
styleUrls: ['./course-selection.component.css']
})
export class CourseSelectionComponent {
/** Element reference to the sub-account filter input. */
@ViewChild('subAccount') private subAccount: ElementRef;
/** Element reference to the term filter input. */
@ViewChild('term') private term: ElementRef;
/** Element reference to the blueprint filter input. */
@ViewChild('blueprint') private blueprint: ElementRef;
/** Element reference to the search-by filter input. */
@ViewChild('searchBy') private searchBy: ElementRef;
/** Element reference to the search text input. */
@ViewChild('searchText') private searchText: ElementRef;
/** Whether or not a search is currently processing */
searching = false;
/** Holds what the last search was sorted by. */
lastSortedBy: string;
/** Results of a search. */
courseResults: Course[] = [];
/**
* Constructor
* @param {KatanaService} katanaService Provides functionality for making API calls to the Katana server.
* @param {CourseService} courseService Provides information and management for selected courses.
*/
constructor(private katanaService: KatanaService,
public courseService: CourseService) { }
/**
* This retrieves all courses that match the given parameters from Canvas
* and returns them as an array of course objects that have been modified
* by us. The returned results will populate the 'courseResults' array and
* will display in the results table in the course-selection html file.
*/
async getCourses() {
// Canvas makes you have an input of at least three characters to use the search_term in the API
if (this.searchText.nativeElement.value.length > 2) {
// Replace any whitespaces with '%20' for the query parameter and make everything lowercase
const searchPhrase = this.searchText.nativeElement.value.replace(/\s/g, '%20');
// Set the loading circle to display before retrieving the courses
this.searching = true;
// Send the search parameters to the katana service to build the correct URI
this.katanaService.getCourses({
subAccount: this.subAccount.nativeElement.value,
term: this.term.nativeElement.value,
blueprint: this.blueprint.nativeElement.value,
searchPhrase: searchPhrase,
searchBy: this.searchBy.nativeElement.value
})
/* After getting the courses from Canvas, check to make sure what you
got back matches what is currently in the search text input box */
.then((courses: Course[]) => {
if (searchPhrase === this.searchText.nativeElement.value.replace(/\s/g, '%20')) {
this.searching = false;
this.courseResults = courses;
}
})
.catch(console.error);
} else {
/* If the string in the search box is less than three characters
then stop searching and set the returned course results to an empty array
instead of leaving the last results in the table */
this.searching = false;
this.courseResults = [];
}
}
/**
* Sorts the courseResults array according to the course attribute defined by 'param'.
* @param {string} param The object key to be sorted.
* @returns {void}
*/
sortBy(param: any) {
// If they click on the same category more than once, it will reverse the order of the results
if (this.lastSortedBy === param) {
this.courseResults = this.courseResults.reverse();
this.lastSortedBy = param;
return;
}
this.lastSortedBy = param;
// Sort numerically
if (param === 'id' || param === 'account' || param === 'blueprint') {
this.courseResults.sort((a, b) => {
return a[param] - b[param];
});
} else {
// Sort alphabetically
this.courseResults.sort((a, b) => {
const name1 = a[param].toLowerCase();
const name2 = b[param].toLowerCase();
if (name1 < name2) {
return -1;
}
if (name1 > name2) {
return 1;
}
return 0;
});
}
}
/**
* Checks to see if the selected course from courseResults
* is already in the courses array in courseService.
* @param {object} course The course that is being checked.
* @returns {boolean} Whether the course is already added.
*/
isAdded(course: any) {
return this.courseService.courses.find(c => c.id === course.id) !== undefined;
}
/** Deselects all selected courses. */
removeAll() {
this.courseService.courses.forEach(c => this.courseService.removeCourse(c));
}
/** Selects all courses found in the search */
addAll() {
this.courseResults.forEach(c => {
if (!this.isAdded(c)) {
this.courseService.addCourse(c);
}
});
}
/** Closes the courseSelect page when the user clicks on the margins of the page */
closeCourseSelect() {
this.courseService.courseSelectionOpen = false;
}
/** Stops the courseSelect page from closing when the user interacts with the page (area that is not in the margins)
* @param {object} event The JS onClick event
*/
stopEvent(event) {
event.stopPropagation();
}
}
<div class="courseSearch" [class.visible]="courseService.courseSelectionOpen" (click)="closeCourseSelect();">
<div class="courseSearch__courseSelection" (click)="stopEvent($event);">
<h2 class="courseSearch__header">
Course Selection
</h2>
<label>Sub-Account
<select (change)="getCourses()" #subAccount>
<option value=""></option>
<option value="13">Development</option>
<!-- <option value="5">Online</option>
<option value="8">Sandbox</option>
<option value="24">Pathway</option> -->
<option value="41">EnglishConnect</option>
<option value="1" selected>All</option>
</select>
</label>
<label>Term
<select (change)="getCourses()" #term>
<option value=""></option>
<option value="10">Fall 2018</option>
<option value="4">Winter 2018</option>
<option value="9">Spring 2018</option> -->
<option value="5">Master Courses</option>
<option value="1">Default Term</option>
<option value="" selected>All</option>
</select>
</label>
<label>Blueprint
<select (change)="getCourses()" #blueprint>
<option value=""></option>
<option value="true">True</option>
<option value="false">False</option>
<option value="" selected>All</option>
</select>
</label>
<label>Search By
<select (change)="getCourses()" #searchBy>
<option value="teacher">Instructor</option>
<option value="course" selected>Course</option>
</select>
</label>
<label>Search Term</label>
<input type="text" placeholder="Search courses..." (input)="getCourses()" #searchText/>
<div class="flex-container label-row">
<label>Results {{courseResults.length !== 0 ? '(' + courseResults.length + ')': '0'}}</label>
<button class="btn green green-accent-4 waves-effect addAll" (click)="addAll()" [class.disabled]="courseResults.length === 0" >Add All Results</button>
<button *ngIf="courseService.courseSelectionOpen" class="waves-effect red lighten-1 btn removeAll" [class.disabled]="courseService.courses.length == 0"
(click)="removeAll()">Remove All Courses</button>
</div>
<table class="highlight">
<thead>
<tr>
<th>+/-</th>
<!-- <th></th> -->
<th (click)="sortBy($event, 'course_name')">Name</th>
<th (click)="sortBy($event, 'course_code')">Code</th>
<th (click)="sortBy($event, 'instructor')">Instructor</th>
<th (click)="sortBy($event, 'account')">Account</th>
<th (click)="sortBy($event, 'term')">Term</th>
<th (click)="sortBy($event, 'blueprint')">Blueprint</th>
<th (click)="sortBy($event, 'id')">ID</th>
<th>Open in Canvas</th>
</tr>
</thead>
<tbody>
<ng-container *ngIf="!searching">
<tr *ngFor="let course of courseResults" [ngClass]="isAdded(course) ? 'grey lighten-2' : ''" class="table-element-{{course.id}}">
<td (click)="courseService.addCourse(course)">
<i class="material-icons">{{isAdded(course) ? 'remove_circle' : 'add_circle'}}</i>
</td>
<td (click)="courseService.addCourse(course)">{{course.course_name}}</td>
<td (click)="courseService.addCourse(course)">{{course.course_code}}</td>
<td (click)="courseService.addCourse(course)">{{course.instructor}}</td>
<td (click)="courseService.addCourse(course)">{{course.account}}</td>
<td (click)="courseService.addCourse(course)">{{course.term}}</td>
<td (click)="courseService.addCourse(course)">{{course.blueprint}}</td>
<td (click)="courseService.addCourse(course)">{{course.id}}</td>
<td>
<a href="{{course.url}}" target="_blank">
<i class="material-icons">open_in_new</i>
</a>
</td>
</tr>
</ng-container>
<!-- Put the loader in the body if loading -->
<div *ngIf="searching">
<div class="loader">
<div class="preloader-wrapper big active">
<div class="spinner-layer spinner-blue-only">
<div class="circle-clipper left">
<div class="circle"></div>
</div>
<div class="gap-patch">
<div class="circle"></div>
</div>
<div class="circle-clipper right">
<div class="circle"></div>
</div>
</div>
</div>
</div>
<div class="loader">
Searching ...
</div>
</div>
</tbody>
</table>
</div>
</div>