import { Component } from '@angular/core';
@Component({
selector: 'main-app',
templateUrl: './app/app.template.html'
})
export class MainComponent {
counter: number = 0;
onClick() {
this.counter++;
}
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { MainComponent } from './app.component';
import { ChartComponent } from './chart.component';
import { TreeTableComponent } from './treetable.component';
// Import PrimeNG modules
import {AccordionModule} from 'primeng/primeng';
import {AutoCompleteModule} from 'primeng/primeng';
import {BreadcrumbModule} from 'primeng/primeng';
import {ButtonModule} from 'primeng/primeng';
import {CalendarModule} from 'primeng/primeng';
import {CarouselModule} from 'primeng/primeng';
import {ChartModule} from 'primeng/primeng';
import {CheckboxModule} from 'primeng/primeng';
import {ChipsModule} from 'primeng/primeng';
import {CodeHighlighterModule} from 'primeng/primeng';
import {ConfirmDialogModule} from 'primeng/primeng';
import {SharedModule} from 'primeng/primeng';
import {ContextMenuModule} from 'primeng/primeng';
import {DataGridModule} from 'primeng/primeng';
import {DataListModule} from 'primeng/primeng';
import {DataScrollerModule} from 'primeng/primeng';
import {DataTableModule} from 'primeng/primeng';
import {DialogModule} from 'primeng/primeng';
import {DragDropModule} from 'primeng/primeng';
import {DropdownModule} from 'primeng/primeng';
import {EditorModule} from 'primeng/primeng';
import {FieldsetModule} from 'primeng/primeng';
import {FileUploadModule} from 'primeng/primeng';
import {GalleriaModule} from 'primeng/primeng';
import {GMapModule} from 'primeng/primeng';
import {GrowlModule} from 'primeng/primeng';
import {InputMaskModule} from 'primeng/primeng';
import {InputSwitchModule} from 'primeng/primeng';
import {InputTextModule} from 'primeng/primeng';
import {InputTextareaModule} from 'primeng/primeng';
import {LightboxModule} from 'primeng/primeng';
import {ListboxModule} from 'primeng/primeng';
import {MegaMenuModule} from 'primeng/primeng';
import {MenuModule} from 'primeng/primeng';
import {MenubarModule} from 'primeng/primeng';
import {MessagesModule} from 'primeng/primeng';
import {MultiSelectModule} from 'primeng/primeng';
import {OrderListModule} from 'primeng/primeng';
import {OverlayPanelModule} from 'primeng/primeng';
import {PaginatorModule} from 'primeng/primeng';
import {PanelModule} from 'primeng/primeng';
import {PanelMenuModule} from 'primeng/primeng';
import {PasswordModule} from 'primeng/primeng';
import {PickListModule} from 'primeng/primeng';
import {ProgressBarModule} from 'primeng/primeng';
import {RadioButtonModule} from 'primeng/primeng';
import {RatingModule} from 'primeng/primeng';
import {ScheduleModule} from 'primeng/primeng';
import {SelectButtonModule} from 'primeng/primeng';
import {SlideMenuModule} from 'primeng/primeng';
import {SliderModule} from 'primeng/primeng';
import {SpinnerModule} from 'primeng/primeng';
import {SplitButtonModule} from 'primeng/primeng';
import {StepsModule} from 'primeng/primeng';
import {TabMenuModule} from 'primeng/primeng';
import {TabViewModule} from 'primeng/primeng';
import {TerminalModule} from 'primeng/primeng';
import {TieredMenuModule} from 'primeng/primeng';
import {ToggleButtonModule} from 'primeng/primeng';
import {ToolbarModule} from 'primeng/primeng';
import {TooltipModule} from 'primeng/primeng';
import {TreeModule} from 'primeng/primeng';
import {TreeTableModule} from 'primeng/primeng';
import {TriStateCheckboxModule} from 'primeng/primeng';
@NgModule({
imports: [
BrowserModule,
FormsModule,
// AccordionModule,
AutoCompleteModule,
// BreadcrumbModule,
ButtonModule,
// CalendarModule,
// CarouselModule,
ChartModule,
CheckboxModule,
// ChipsModule,
// CodeHighlighterModule,
ConfirmDialogModule,
SharedModule,
ContextMenuModule,
DataGridModule,
DataListModule,
DataScrollerModule,
DataTableModule,
DialogModule,
DragDropModule,
DropdownModule,
// EditorModule,
// FieldsetModule,
// FileUploadModule,
// GalleriaModule,
// GMapModule,
GrowlModule,
InputMaskModule,
InputSwitchModule,
InputTextModule,
InputTextareaModule,
// LightboxModule,
// ListboxModule,
// MegaMenuModule,
MenuModule,
MenubarModule,
MessagesModule,
MultiSelectModule,
// OrderListModule,
OverlayPanelModule,
PaginatorModule,
PanelModule,
PanelMenuModule,
PasswordModule,
// PickListModule,
ProgressBarModule,
RadioButtonModule,
RatingModule,
// ScheduleModule,
SelectButtonModule,
SlideMenuModule,
SliderModule,
SpinnerModule,
SplitButtonModule,
StepsModule,
TabMenuModule,
TabViewModule,
// TerminalModule,
// TieredMenuModule,
ToggleButtonModule,
ToolbarModule,
TooltipModule,
TreeModule,
TreeTableModule,
TriStateCheckboxModule
],
declarations: [ MainComponent, ChartComponent, TreeTableComponent ],
bootstrap: [ MainComponent, ChartComponent, TreeTableComponent ]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);
<!DOCTYPE html>
<html>
<head>
<title>Angular QuickStart</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--<link rel="stylesheet" href="styles.css">-->
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
<!-- Bootstrap dependencies -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
<!-- PrimeNG style dependencies -->
<link rel="stylesheet" href="https://unpkg.com/primeng@4.0.1/resources/themes/omega/theme.css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
<link rel="stylesheet" href="https://unpkg.com/primeng@4.0.1/resources/primeng.min.css" />
<!-- Polyfill for older browsers -->
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.6/Chart.bundle.js"></script>
<script src="https://unpkg.com/zone.js@0.8.4?main=browser"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.8"></script>
<script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>
<!-- 2. Configure SystemJS -->
<script src="systemjs.config.js"></script>
<style>
body {
padding: 1em;
font-family: Arial, Helvetica, sans-serif;
}
.vote-treetable td,
.ui-treetable tbody .ui-treetable-row td {
position: relative;
}
.ui-widget-header .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-state-highlight
{
background: #117ec315;
}
</style>
</head>
<body>
<main-app>Loading my-app...</main-app>
<br/>
</body>
</html>
/**
* WEB ANGULAR VERSION
* (based on systemjs.config.js in angular.io)
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
// DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
transpiler: 'ts',
typescriptOptions: {
// Copy of compiler options in standard tsconfig.json
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
},
meta: {
'typescript': {
"exports": "ts"
}
},
paths: {
// paths serve as alias
'npm:': 'https://unpkg.com/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'app',
// angular bundles
'@angular/animations': 'npm:@angular/animations@4.0.0/bundles/animations.umd.js',
'@angular/core': 'npm:@angular/core@4.0.0/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common@4.0.0/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler@4.0.0/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser@4.0.0/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic@4.0.0/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http@4.0.0/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router@4.0.0/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms@4.0.0/bundles/forms.umd.js',
'@angular/upgrade': 'npm:@angular/upgrade@4.0.0/bundles/upgrade.umd.js',
'@angular/upgrade/static': 'npm:@angular/upgrade@4.0.0/bundles/upgrade-static.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js',
'ts': 'npm:plugin-typescript@4.0.10/lib/plugin.js',
'typescript': 'npm:typescript@2.0.3/lib/typescript.js',
'primeng': 'npm:primeng@4.0.1'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.ts',
defaultExtension: 'ts'
},
rxjs: {
defaultExtension: 'js'
},
primeng: {
defaultExtension: 'js'
}
}
});
if (!global.noBootstrap) { bootstrap(); }
// Bootstrap the `AppModule`(skip the `app/main.ts` that normally does this)
function bootstrap() {
console.log('Auto-bootstrapping');
// Stub out `app/main.ts` so System.import('app') doesn't fail if called in the index.html
System.set(System.normalizeSync('app/main.ts'), System.newModule({ }));
// bootstrap and launch the app (equivalent to standard main.ts)
Promise.all([
System.import('@angular/platform-browser-dynamic'),
getAppModule()
])
.then(function (imports) {
var platform = imports[0];
var app = imports[1];
platform.platformBrowserDynamic().bootstrapModule(app.AppModule);
})
.catch(function(err){ console.error(err); });
}
// Import AppModule or make the default AppModule if there isn't one
// returns a promise for the AppModule
function getAppModule() {
if (global.noAppModule) {
return makeAppModule();
}
return System.import('app/app.module').catch(makeAppModule)
}
function makeAppModule() {
console.log('No AppModule; making a bare-bones, default AppModule');
return Promise.all([
System.import('@angular/core'),
System.import('@angular/platform-browser'),
System.import('app/app.component')
])
.then(function (imports) {
var core = imports[0];
var browser = imports[1];
var appComp = imports[2].AppComponent;
var AppModule = function() {}
AppModule.annotations = [
new core.NgModule({
imports: [ browser.BrowserModule ],
declarations: [ appComp ],
bootstrap: [ appComp ]
})
]
return {AppModule: AppModule};
})
}
})(this);
<button pButton label="Increment" (click)="onClick($event)"></button>
{{counter}}
<p-tabView>
<p-tabPanel header="Votes">
<treetable-app>Loading votes...</treetable-app>
</p-tabPanel>
<p-tabPanel header="Line Chart">
<chart-app type="line">Loading Line Chart...</chart-app>
</p-tabPanel>
<p-tabPanel header="Bar Chart">
<chart-app type="bar">Loading Bar Chart...</chart-app>
</p-tabPanel>
<p-tabPanel header="Doughnut Chart">
<chart-app type="doughnut">Loading Doughnut Chart...</chart-app>
</p-tabPanel>
</p-tabView>
import { Component, OnInit, ViewChild, Input } from '@angular/core';
@Component({
selector: 'chart-app',
templateUrl: './app/chart.template.html'
})
export class ChartComponent implements OnInit {
@Input()
type: string = 'line';
@ViewChild('monthlyChart')
monthlyChart;
@ViewChild('weeklyChart')
weeklyChart;
public msgs: Message[];
constructor() {
this.weeklyDataOptions = {
title: {
display: true,
text: 'Realtime Data',
fontSize: 16
},
legend: {
display: false,
position: 'right'
}
}
this.bgColors = {
backgroundColor: [
"#FF6384",
"#36A2EB",
"#FFCE56",
"#63FF84",
"#A236EB",
"#CEFF56",
"#EBA236"
],
hoverBackgroundColor: [
"#FF83A4",
"#56C2FB",
"#FFEE76",
"#83FFA4",
"#C256FB",
"#EEFF76",
"#FBC256"
]
};
}
ngOnInit() {
let options = this.type === 'line'
? {}
: this.bgColors;
this.monthlyData = {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [
{
label: 'First Dataset',
borderColor: '#4bc0c0',
},
{
label: 'Second Dataset',
borderColor: '#565656',
}
]
}
this.weeklyData = {
labels: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
datasets: [{ label: 'Weekly' }],
};
this.UpdateChart(this.monthlyChart, this.monthlyData.datasets, Object.assign(options, { fill: false }));
this.UpdateChart(this.weeklyChart, this.weeklyData.datasets, Object.assign(options, { fill: true }));
setInterval(() =>
this.UpdateChart(this.weeklyChart, this.weeklyData.datasets)
, 5000);
}
public UpdateChart(chart: ChartComponent, datasets: Array<Object>, options: Object) {
//console.log('updating');
datasets.map(ds => Object.assign(ds, { data: this.getRandomValues(7, 10) }, options));
chart.refresh();
}
selectData(event) {
let msg = this.monthlyData.datasets[event.element._datasetIndex].data[event.element._index];
this.msgs = [];
this.msgs.push({
severity: 'info',
summary: 'Data Selected',
detail: msg
});
}
public getRandomValues(rowCount: number, maxValue: number): Array<number> {
return new Array(rowCount).fill(0).map(() => Math.round(Math.random() * maxValue));
}
}
{{monthlyData.labels[0]}}
<p-growl [(value)]="msgs"></p-growl>
<div style="max-width: 400px">
<p-chart #monthlyChart [type]="type" [data]="monthlyData" (onDataSelect)="selectData($event)"></p-chart>
<p-chart #weeklyChart [type]="type" [data]="weeklyData" [options]="weeklyDataOptions"></p-chart>
</div>
<p-growl [value]="msgs"></p-growl>
<p-treeTable [value]="data" selectionMode="multiple" [(selection)]="selectedReasons" (onNodeSelect)="reasonSelect($event)" (onNodeUnselect)="reasonUnselect($event)" (onNodeExpand)="reasonExpand($event)" [contextMenu]="cm" reorderableColumns="true" class="vote-treetable">
<p-header>v0.1a</p-header>
<!--<p-column *ngFor="let col of cols" [field]="col.field" [header]="col.header"></p-column>-->
<p-column field="Title" header="Title">
<ng-template let-node="rowData" pTemplate="body">
<input [(ngModel)]="node.data.Title" style="background: transparent; border: none;">
</ng-template>
</p-column>
<!--
<p-column field="Priority" header="Priority">
<ng-template let-node="rowData" pTemplate="body">
<p-rating [(ngModel)]="node.data.Priority" [cancel]="false"
(onRate)="handleRate($event)" (onCancel)="handleCancel($event)"></p-rating>
<span *ngIf="msg" style="margin-left:10px">{{msg}}</span>
</ng-template>
</p-column>-->
<p-column field="Weight" header="Weight">
<ng-template let-node="rowData" pTemplate="body">
<!--<p-triStateCheckbox [(ngModel)]="node.data.Weight"></p-triStateCheckbox>-->
<p-slider [(ngModel)]="node.data.Weight" min="-3" max="3" (onChange)="voteChange($event)" animate="true"></p-slider>
</ng-template>
</p-column>
<p-column *ngFor="let c of choices;let j = index;" [field]="node.data.VoteGroups[j].ReasonId" [header]="c.Title">
<ng-template let-node="rowData" pTemplate="body">
<!--<p-slider [(ngModel)]="node.data.Weight" min="-3" max="3" (onChange)="voteChange($event)" animate="true"></p-slider>-->
<p-spinner [(ngModel)]="node.data.VoteGroups[j].ReasonId" class="vote-spinner" min="-3" max="3" size="2">
</p-spinner>
<!--<input type="text" pInputText [(ngModel)]="node.data.Weight"/> (onChange)="voteChange($event)"-->
</ng-template>
</p-column>
</p-treeTable>
<style>
.vote-treetable td,
.ui-treetable tbody .ui-treetable-row td {
position: relative !important;
}
.vote-spinner {
position: absolute;
right: 2px;
top: 50%;
transform: translateY(-50%);
opacity: 0.9;
}
.vote-spinner > input {
width: 100%;
}
</style>
<p>Selected Nodes: <span *ngFor="let r of selectedReasons">{{r.data.Title}}<br/></span></p>
<p-contextMenu #cm [model]="menuItems"></p-contextMenu>
import { Component, OnInit, ViewChild, Input } from '@angular/core';
import {Header} from 'primeng/primeng';
import {Footer} from 'primeng/primeng';
import {TriStateCheckboxModule} from 'primeng/primeng';
import {RatingModule} from 'primeng/primeng';
import {Reason} from '../reason.ts';
import {Choice} from '../choice.ts';
import {Vote} from '../vote.ts';
import {VoteGroup} from '../votegroup.ts';
import {Data} from '../data.ts';
import Utils from '../utils.ts';
@Component({
selector: 'treetable-app',
templateUrl: './app/treetable.template.html',
//styleUrls: ['./app/treetable.component.css']
})
export class TreeTableComponent implements OnInit {
menuItems: MenuItem[] = [];
choices: Choice[] = [];
reasons: TreeNode[] = [];
votes: Vote[] = [];
selectedReason: TreeNode;
selectedReasons: TreeNode[] = [];
constructor() {}
ngOnInit() {
this.choices = <Choice[]> [
new Choice(1, 1, 'C1', 0),
new Choice(2, 1, 'C2', 1),
new Choice(3, 1, 'C3', 2)];
this.reasons = <Reason[]> [ // id, topic, parent, title, position, weight, choiceCount
new Reason(1, 1, null, 'R1', 0, 1),
new Reason(2, 1, 1, 'R2', 1, undefined),
new Reason(3, 1, null, 'R3', 2, 1)];
this.votes = <Vote[]> [ // id, reason, choice, value, user
new Vote(1, 1, 1, 2),
new Vote(2, 2, 1, -1),
new Vote(3, 3, 2, -2)];
this.data = <TreeNode[]>[];
for(let reason of this.reasons) {
let rVotes = this.votes.filter(v => v.ReasonId == reason.Id) || [];
for(let choice in this.choices) {
let rcVotes = rVotes.filter(v => v.ChoiceId == choice.Id) || [];
reason.VoteGroups.push(new VoteGroup(reason.Id, choice.Id, rcVotes))
}
this.data.push(this.getObject(reason));
}
console.log(JSON.stringify(this.data));
this.data = Utils.arrayToTree(this.reasons);
//alert(JSON.stringify(this.data));
this.menuItems = [
{label: 'View', icon: 'fa-search', command: (event) => this.viewReason(this.selectedReasons)},
{label: 'Delete', icon: 'fa-close', command: (event) => this.deleteReason(this.selectedReasons)}
];
}
getObject(data: any) {
return { data: data };
}
reasonSelect(event) {
//this.msgs = [];
//this.msgs.push({severity: 'info', summary: 'Reason Selected', detail: event.node.data.Title});
}
reasonUnselect(event) {
//this.msgs = [];
//this.msgs.push({severity: 'info', summary: 'Reason Unselected', detail: event.node.data.Title});
}
reasonExpand(event) {
//if(event.node)
//this.nodeService.getLazyFilesystem().then(nodes => event.node.children = nodes);
}
viewReason(nodes: TreeNode[]) {
for(let node of nodes) {
this.msgs = [];
this.msgs.push({severity: 'info', summary: 'View Reason', detail: node.data.Title});
}
}
deleteReason(nodes: TreeNode[]) {
for(let node of nodes) {
node.parent.children = node.parent.children.filter(n => n.data !== node.data);
this.msgs = [];
this.msgs.push({severity: 'info', summary: 'Delete Reason', detail: node.data.Title});
}
}
handleRate(event) {
//alert("You have rated " + event.value);
}
handleCancel(event) {
alert("Rating Cancelled");
}
voteChange(event) {
if(event) {
this.msgs = [];
this.msgs.push({severity: 'info', summary: 'Vote', detail: event.value});
}
}
}
import {Choice} from './choice.ts';
import {Vote} from './vote.ts';
export class Reason {
Id: number;
ParentId: number|null;
TopicId: number;
Title: string;
Position: number;
Priority: number;
Weight: number = 0.00;
UserId: number;
Choices: Choice[] = [];
Votes: Vote[] = [];
VoteGroups: VoteGroup[] = [];
ChildReasons: Reason[] = [];
constructor(id: number, topicId: number, parentId: number|null, title: string, position: number, weight: number, description: string) {
this.Id = id;
this.topicId = topicId;
this.ParentId = parentId;
this.Title = title;
this.Position = position;
this.Weight = weight;
this.Description = description;
this.Priority = 0;
}
setChildReasons(reasons: Reason[]) {
}
}
import {Reason} from './reason.ts';
import {Vote} from './vote.ts';
export class Choice {
Id: number;
TopicId: number;
Title: string;
Position: number;
UserId: number;
Votes: Vote[] = [];
Reasons: Reason[] = [];
constructor(id: number, topicId: number, title: string, position: number) {
this.Id = id;
this.TopicId = topicId;
this.Title = title;
this.Position = position;
}
}
import {Choice} from './choice.ts';
import {Reason} from './reason.ts';
export class Vote {
Choice: Choice;
Reason: Reason;
Id: number;
//TopicId: number;
ReasonId: number;
ChoiceId: number;
UserId: number;
Value: number;
Min: number = -3;
Max: number = 3;
constructor(id: number, reasonId: number, choiceId: number, value: number, userId:number) {
this.Id = id;
//this.TopicId = topicId;
this.ReasonId = reasonId;
this.ChoiceId = choiceId;
this.Value = value;
this.UserId = userId;
}
public getUsername(): string {
return 'SomeName' + this.Id;
}
public voteUp(): void {
this.Value += 1;
}
public voteDown(): void {
this.Value += -1;
}
}
import {Choice} from './choice.ts';
import {Reason} from './reason.ts';
import {Vote} from './vote.ts';
export class Data {
constructor() { }
}
export interface Item {
Id: number;
ParentId: number | null;
[key: string]: any;
}
export interface TreeItem {
data: Item | null;
children: TreeItem[];
}
export interface ArrayToTreeConfig {
id: string;
parentId: string;
}
export default class Utils {
/* Unflattens an array to a tree with runtime O(n) */
static arrayToTree (items: Item[], config: ArrayToTreeConfig = { id: 'Id', parentId: 'ParentId' }): TreeItem[] {
// the resulting unflattened tree
const rootItems: TreeItem[] = []
// stores all already processed items with ther ids as key so we can easily look them up
const lookup: { [id: string]: TreeItem } = {}
// idea of this loop:
// whenever an item has a parent, but the parent is not yet in the lookup object, we store a preliminary parent
// in the lookup object and fill it with the data of the parent later
// if an item has no parentId, add it as a root element to rootItems
for (const item of items) {
const itemId = item[config.id]
const parentId = item[config.parentId]
// look whether item already exists in the lookup table
if (!Object.prototype.hasOwnProperty.call(lookup, itemId)) {
// item is not yet there, so add a preliminary item (its data will be added later)
lookup[itemId] = { data: null, children: [] }
}
// add the current item's data to the item in the lookup table
lookup[itemId].data = item
const TreeItem = lookup[itemId]
if (parentId === null) {
// is a root item
rootItems.push(TreeItem)
} else {
// has a parent
// look whether the parent already exists in the lookup table
if (!Object.prototype.hasOwnProperty.call(lookup, parentId)) {
// parent is not yet there, so add a preliminary parent (its data will be added later)
lookup[parentId] = { data: null, children: [] }
}
// add the current item to the parent
lookup[parentId].children.push(TreeItem)
}
}
return rootItems
}
}
//import {Choice} from './choice.ts';
//import {Reason} from './reason.ts';
import {Vote} from './vote.ts';
export class VoteGroup {
ReasonId: number;
ChoiceId: number;
Votes: Vote[] = [];
AverageValue: number;
UserValue: number;
PctValue: number;
Usernames: string;
constructor(reasonId: number, choiceId: number, votes: Vote[]) {
this.ReasonId = reasonId;
this.ChoiceId = choiceId;
this.Votes = votes || [];
this.AverageValue = this.getAverageValue();
this.PctValue = this.getPercentage();
}
public getPercentage(): number {
return 100 * this.getTotalValue();// / (this.Max - this.Min)
}
public getTotalValue(): number {
return this.Votes.reduce(function(sum: number, vote: Vote) {
return sum + vote.Value;
}, 0);
}
public getVoteCount(): number {
return this.Votes.length;
}
public getAverageValue(): number {
return this.Votes.length == 0
? 0
: this.getTotalValue() / this.Votes.length;
}
public getUsernames(users: any): string {
let userIds = this.Votes.map(function(vote: Vote) {
return vote.getUsername();
});
return userIds.join(', ');
}
public addVote(c: Vote): void {
this.Votes.push(c);
}
public removeVote(i: number): void {
if (this.Votes.length <= i)
throw new Error("index out of bound!");
this.Votes.splice(i, 1);
}
public VoteUp(): void {
this.Value += 1;
}
public VoteDown(): void {
this.Value += -1;
}
}