File

src/app/course-tabs/course-selection/course-selection.component.ts

Description

Provides the user management for the selected courses, including a course search. overlay to add/remove courses.

Metadata

selector app-course-selection
styleUrls course-selection.component.css
templateUrl ./course-selection.component.html

Index

Properties
Methods

Constructor

constructor(katanaService: KatanaService, courseService: CourseService)

Constructor

Parameters :
Name Type Optional Description
katanaService KatanaService no

Provides functionality for making API calls to the Katana server.

courseService CourseService no

Provides information and management for selected courses.

Methods

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 :
Name Type Optional Description
course any no

The course that is being checked.

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 :
Name Type Optional Description
param any no

The object key to be sorted.

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 :
Name Optional Description
event no

The JS onClick event

Returns : void

Properties

Private blueprint
blueprint: ElementRef
Type : ElementRef
Decorators : ViewChild

Element reference to the blueprint filter input.

courseResults
courseResults: Course[]
Type : Course[]
Default value : []

Results of a search.

Public courseService
courseService: CourseService
Type : CourseService
Provides information and management for selected courses.
lastSortedBy
lastSortedBy: string
Type : string

Holds what the last search was sorted by.

Private searchBy
searchBy: ElementRef
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: ElementRef
Type : ElementRef
Decorators : ViewChild

Element reference to the search text input.

Private subAccount
subAccount: ElementRef
Type : ElementRef
Decorators : ViewChild

Element reference to the sub-account filter input.

Private term
term: ElementRef
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>
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""