src/app/task-web/task-view/task-view.component.ts
selector | app-task-view |
styleUrls | ./task-view.component.css |
templateUrl | ./task-view.component.html |
Properties |
Methods |
constructor(router: Router, route: ActivatedRoute, taskViewService: TaskViewService)
|
||||||||||||
Parameters :
|
getTask |
getTask()
|
Get task data from task-view service.
Returns :
void
|
getTaskStatus |
getTaskStatus()
|
Returns :
string
|
getTechName |
getTechName()
|
Returns the tech name for the task's site's tech id.
Returns :
string
One of: M-AT, FS, $T, etc. |
haltTaskTimer |
haltTaskTimer()
|
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Stop subscribing to router events.
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
onTaskSelected | ||||||
onTaskSelected(taskid: number)
|
||||||
Called by related tasks component to switch task viewed.
Parameters :
Returns :
void
|
refreshTask |
refreshTask()
|
Returns :
void
|
setTaskStatus | ||||||
setTaskStatus(val: number)
|
||||||
Parameters :
Returns :
void
|
startTaskTimer |
startTaskTimer()
|
Timer for determining when the task status badge needs to change color to indicate task "staleness." This function makes use of these RxJS features:
Returns :
void
|
taskStatusSubscr |
taskStatusSubscr()
|
This function sets up a subscription for task status changes. This code was adapted from:
Angular: Refetch data on same URL navigation.
According to the article and my anecdotal experience, by default, the router doesn’t emit for events where we are trying to navigate to the same URL that is already activated. Both of these solutions depend on:
The solution watches for
NavigationEnd events
then calls It stops watching when
takeUntil
is provided an observable emit from
Returns :
void
|
customerTextActive |
Default value : true
|
destroyed |
Default value : new Subject<any>()
|
Subject is a special type of observable. It's being used to detect when this, component is destroyed/exited to ensure that the Router events subscription is canceled when at that time. |
dict |
Type : any
|
inhouseTextActive |
Default value : false
|
keywordsActive |
Default value : false
|
relatedTasksActive |
Default value : false
|
task |
Type : any
|
taskID |
Type : any
|
taskStatus |
Type : string
|
taskTimerSubscription |
Type : any
|
import { Component, OnInit, OnDestroy } from '@angular/core';
import {
Router,
ActivatedRoute,
NavigationEnd,
RouterEvent,
} from '@angular/router';
import { TaskViewService } from './task-view.service';
import { interval, Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-task-view',
templateUrl: './task-view.component.html',
styleUrls: ['./task-view.component.css'],
})
export class TaskViewComponent implements OnInit, OnDestroy {
task: any;
dict: any;
taskID: any;
customerTextActive = true;
inhouseTextActive = false;
keywordsActive = false;
relatedTasksActive = false;
taskStatus: string;
taskTimerSubscription: any;
/**
* [Subject]{@link https://rxjs-dev.firebaseapp.com/guide/subject} is a
* special type of observable. It's being used to detect when this, component
* is destroyed/exited to ensure that the Router events subscription is
* canceled when at that time.
*/
destroyed = new Subject<any>();
/**
*/
constructor(
private router: Router,
private route: ActivatedRoute,
private taskViewService: TaskViewService
) { }
ngOnInit(): void {
// Subscribe to task refresh events.
this.taskStatusSubscr();
// Get parameters from task url.
this.route.queryParams.subscribe((params) => (this.taskID = params.taskid));
// Get task and dictionary data from task service.
this.getTask();
this.taskViewService.getDict().subscribe((dict) => (this.dict = dict));
}
/**
* This function sets up a subscription for task status changes.
*
* This code was adapted from:
* [Angular: Refetch data on same URL navigation]{@link https://medium.com/angular-in-depth/refresh-current-route-in-angular-512a19d58f6e}.
* <br>
* Note there's a slightly simpler solution here:
* [Reloading current route in Angular 5 / Angular 6 / Angular 7]{@link https://medium.com/engineering-on-the-incline/reloading-current-route-on-click-angular-5-1a1bfc740ab2}.
* <br>
* It would be interesting to know if this simpler solution is equivalent to
* the first solution.
*
* According to the article and my anecdotal experience, by default,
* the router doesn’t emit for events where we are trying to navigate to
* the same URL that is already activated.
*
* Both of these solutions depend on:
* <ol>
* <li>The Router Module's [onSameUrlNavigation]{@link https://angular.io/api/router/ExtraOptions#onSameUrlNavigation}
* parameter being set to 'reload'
* </li>
* <li>Your route's [runGuardsAndResolvers]{@link https://angular.io/api/router/Route#runGuardsAndResolvers}
* property being set to 'always'
* </li>
* </ol>
*
* The solution watches for
* [NavigationEnd]{@link https://angular.io/api/router/NavigationEnd} events
* then calls <code>this.refreshTask()</code> when one is found.
*
* It stops watching when
* [takeUntil]{@link https://www.learnrxjs.io/learn-rxjs/operators/filtering/takeuntil}
* is provided an observable emit from <code>this.destroyed</code>.
*/
taskStatusSubscr(): void {
this.router.events
.pipe(
// filter out all router events except for
filter((event: RouterEvent) => event instanceof NavigationEnd),
takeUntil(this.destroyed)
)
// fetch task data when a NavigationEnd event occurs
.subscribe(() => {
this.refreshTask();
});
}
setTaskStatus(val: number) {
if (val === 0) {
this.taskStatus = 'pending';
} else {
this.taskStatus = 'expired';
}
}
/**
* Timer for determining when the task status badge needs to change color to
* indicate task "staleness."
*
* This function makes use of these RxJS features:
* <ul>
* <li>[interval]{@link https://rxjs-dev.firebaseapp.com/api/index/function/interval} function</li>
* <li>[Subscription]{@link https://rxjs-dev.firebaseapp.com/api/index/class/Subscription} class</li>
* </ul>
*/
startTaskTimer(): void {
const source = interval(60000);
// when the time subscription emits (a number), the task status is updated
this.taskTimerSubscription = source.subscribe((val: number) =>
this.setTaskStatus(val)
);
}
/**
* Get task data from task-view service.
*/
getTask(): void {
this.taskStatus = 'active';
this.taskViewService.getTask(this.taskID).subscribe((task) => {
this.task = task;
// timer to indicate whether or not the task should be refreshed
this.startTaskTimer();
});
}
/**
* Returns the tech name for the task's site's tech id.
* @returns One of: M-AT, FS, $T, etc.
*/
getTechName(): string {
return this.dict.DAmsTechnologies.filter(
(tech: any) => tech.ID === this.task.AmsTaskMain.TechnologyID
)[0].Name;
}
/**
* Called by related tasks component to switch task viewed.
*/
onTaskSelected(taskid: number): void {
this.taskID = taskid;
this.haltTaskTimer();
this.getTask();
}
getTaskStatus(): string {
return this.taskStatus;
}
haltTaskTimer(): void {
this.taskTimerSubscription.next();
this.taskTimerSubscription.complete();
}
refreshTask(): void {
this.haltTaskTimer();
this.getTask();
}
/**
* Stop subscribing to router events.
*
* <code>this.destroyed</code> emits to <code>takeUntil</code> thus ending the
* subscription to route events
*/
ngOnDestroy(): void {
this.destroyed.next();
this.destroyed.complete();
}
}
<div id="overlay-task-num">
<span id="task-label"> Task # </span>
<span *ngIf="task" id="task-num">{{ task.AmsTaskMain[0].TaskID }}</span>
<a *ngIf="task" [routerLink]="['/task-view']" [queryParams]="{ taskid: task.AmsTaskMain[0].TaskID }" role="tooltip"
aria-haspopup="true">
<span title="Click to refresh task view" class="badge" [ngClass]="{
'badge-success': taskStatus === 'active',
'badge-warning': taskStatus === 'pending',
'badge-danger': taskStatus === 'expired'
}" id="task-badge"></span>
</a>
<span id="task-spinner" *ngIf="!task" class="spinner spinner-sm"> Loading... </span>
</div>
<div class="content-area" style="height: 800px; overflow: hidden">
<clr-tabs *ngIf="task">
<div *ngIf="task.AmsTaskText && task.AmsTaskText.length > 0">
<clr-tab *ngIf="task.AmsTaskText[0].CustomerText">
<button clrTabLink>Customer Text</button>
<ng-template [(clrIfActive)]="customerTextActive">
<clr-tab-content>
<div class="content-container">
<div class="content-area" style="height:800px;overflow-y:scroll;">
Customer Text
</div>
</div>
</clr-tab-content>
</ng-template>
</clr-tab>
<clr-tab *ngIf="task.AmsTaskText[0].InhouseText">
<button clrTabLink>In-house Text</button>
<ng-template [(clrIfActive)]="inhouseTextActive">
<clr-tab-content>
<div class="content-container">
<div class="content-area" style="height:800px;overflow-y:scroll;">
In-house Text
</div>
</div>
</clr-tab-content>
</ng-template>
</clr-tab>
<clr-tab *ngIf="task.AmsTaskText[0].KeywordText">
<button clrTabLink>Keywords</button>
<ng-template [(clrIfActive)]="keywordsActive">
<clr-tab-content>
<div class="content-container">
<div class="content-area" style="height:800px;overflow-y:scroll;">
Keywords
</div>
</div>
</clr-tab-content>
</ng-template>
</clr-tab>
</div>
<clr-tab *ngIf="task.AmsTaskRelatedTasks && task.AmsTaskRelatedTasks.length > 0">
<button clrTabLink>Related Tasks</button>
<ng-template [(clrIfActive)]="relatedTasksActive">
<clr-tab-content>
<div class="content-container">
<div class="content-area" style="height:800px;overflow-y:scroll;">
Related Tasks
</div>
</div>
</clr-tab-content>
</ng-template>
</clr-tab>
</clr-tabs>
<span *ngIf="!task" class="spinner spinner-sm">
Loading...
</span>
</div>
./task-view.component.css
#overlay-task-num {
background-color: #304250;
color: #fafafa;
position: absolute;
top: 54px;
right: 150px;
padding-left: 10px;
min-width: 11em;
width: 11em;
z-index: 100;
}
#task-badge {
position: relative;
top: -2px;
left: 8px;
text-align: right;
}
#task-num {
opacity: 0.65;
text-align: left;
width: 5em;
min-width: 5em;
font-family: Arial, monospace;
}
#task-label {
color: white;
opacity: 1;
text-align: right;
}
#task-spinner {
position: relative;
top: 2px;
left: 8px;
}