From 818adc0d473b8d0a58f9f18d4b734c7bf955eb10 Mon Sep 17 00:00:00 2001 From: James Fusia Date: Sat, 25 May 2024 21:38:22 -0400 Subject: [PATCH] Moved stuff around, changed the way I'm storing milestones data --- src/app/app.component.html | 11 +- src/app/app.component.scss | 10 + src/app/app.component.ts | 40 ++- src/components/chart/chart-type.ts | 3 +- src/components/chart/chart.component.ts | 19 +- src/components/select/select.component.html | 9 +- src/components/select/select.component.scss | 14 + src/components/select/select.component.ts | 13 +- src/components/table/header.ts | 9 + src/components/table/table.component.html | 12 + src/components/table/table.component.scss | 0 src/components/table/table.component.spec.ts | 23 ++ src/components/table/table.component.ts | 15 ++ src/enums/column.ts | 28 -- src/enums/csv.ts | 30 +++ src/enums/milestone.ts | 35 +++ src/models/key-value.ts | 9 + src/models/milestone.ts | 23 -- src/models/score.ts | 17 -- src/services/data.service.ts | 258 +++++++++++++------ src/services/filters.service.ts | 25 ++ 21 files changed, 437 insertions(+), 166 deletions(-) create mode 100644 src/components/table/header.ts create mode 100644 src/components/table/table.component.html create mode 100644 src/components/table/table.component.scss create mode 100644 src/components/table/table.component.spec.ts create mode 100644 src/components/table/table.component.ts delete mode 100644 src/enums/column.ts create mode 100644 src/enums/csv.ts create mode 100644 src/enums/milestone.ts create mode 100644 src/models/key-value.ts delete mode 100644 src/models/milestone.ts delete mode 100644 src/models/score.ts create mode 100644 src/services/filters.service.ts diff --git a/src/app/app.component.html b/src/app/app.component.html index 6a25003..891cfd7 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1 +1,10 @@ - +
+ + + + + +
+
+ +
diff --git a/src/app/app.component.scss b/src/app/app.component.scss index e69de29..138aee3 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -0,0 +1,10 @@ +:host { + display: flex; + flex-direction: column; +} + +div { + flex: 1; + flex-direction: row; + display: flex; +} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 929ad10..4803758 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,15 +1,49 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { ChartComponent } from '../components/chart/chart.component'; import { SelectComponent } from '../components/select/select.component'; +import { DataService } from '../services/data.service'; +import { TableComponent } from '../components/table/table.component'; +import { Header } from '../components/table/header'; +import { FilterService } from '../services/filters.service'; @Component({ selector: 'app-root', standalone: true, - imports: [RouterOutlet, ChartComponent, SelectComponent], + imports: [RouterOutlet, ChartComponent, SelectComponent, TableComponent], templateUrl: './app.component.html', styleUrl: './app.component.scss', }) -export class AppComponent { +export class AppComponent implements OnInit { title = 'Georgia Milestones'; + + data = inject(DataService); + filter = inject(FilterService); + + header = [new Header({ label: 'School', source: 'school' })]; + rows: Record[] = []; + + ngOnInit(): void { + // this.filter.filters$.subscribe((filters: KeyValue[]) => { + // this.data.Data.pipe(take(1)).subscribe((data: Milestone[]) => { + // const result = data.filter((d: Milestone) => { + // const match = (filters ?? []).findIndex((f: KeyValue) => { + // switch (f.key) { + // case 'year': + // return d.Year.toString() === f.value; + // case 'grade': + // return d.Grade.toString() === f.value; + // case 'county': + // return d.County === f.value; + // case 'school': + // return d.School === f.value; + // } + // return false; + // }); + // return match > -1; + // }); + // this.rows = result.map((r: Milestone) => ({ school: r.School })); + // }); + // }); + } } diff --git a/src/components/chart/chart-type.ts b/src/components/chart/chart-type.ts index 5cea4c4..f9ae496 100644 --- a/src/components/chart/chart-type.ts +++ b/src/components/chart/chart-type.ts @@ -12,6 +12,7 @@ export enum ChartType { export interface IDataset { type?: ChartType; borderColor?: string; + label?: string; data: string[]; backgroundColor?: string | string[]; pointBackgroundColor?: string; @@ -20,6 +21,6 @@ export interface IDataset { export interface IData { labels?: string[]; - labelsSource: string[]; + labelsSource?: string[]; datasets: IDataset[]; } diff --git a/src/components/chart/chart.component.ts b/src/components/chart/chart.component.ts index c8cbb59..819f94b 100644 --- a/src/components/chart/chart.component.ts +++ b/src/components/chart/chart.component.ts @@ -5,7 +5,7 @@ import { OnDestroy, ViewChild, } from '@angular/core'; -import { Chart, ChartOptions } from 'chart.js'; +import { Chart, ChartOptions } from 'chart.js/auto'; import { PluginNodata } from './plugin-nodata'; import { PluginMoreColors } from './plugin-more-colors'; import { ChartType, IData } from './chart-type'; @@ -22,9 +22,20 @@ export class ChartComponent implements AfterViewInit, OnDestroy { private chart: any = undefined; private data: IData = { - labels: [], - labelsSource: [], - datasets: [], + labels: Array.from( + { length: 10 }, + () => `${Math.floor(Math.random() * 100)}` + ), + // labelsSource: [], + datasets: [ + { + label: 'test', + data: Array.from( + { length: 10 }, + () => `${Math.floor(Math.random() * 100)}` + ), + }, + ], }; constructor() {} diff --git a/src/components/select/select.component.html b/src/components/select/select.component.html index 814a111..72e5e0f 100644 --- a/src/components/select/select.component.html +++ b/src/components/select/select.component.html @@ -1,4 +1,7 @@ - - + + + diff --git a/src/components/select/select.component.scss b/src/components/select/select.component.scss index e69de29..820daaa 100644 --- a/src/components/select/select.component.scss +++ b/src/components/select/select.component.scss @@ -0,0 +1,14 @@ +:host { + display: block; + width: 200px; +} + +// label { +// padding-right: 10px; +// float: right; +// } + +select { + width: 200px; + float: right; +} diff --git a/src/components/select/select.component.ts b/src/components/select/select.component.ts index 06059d8..10e1a29 100644 --- a/src/components/select/select.component.ts +++ b/src/components/select/select.component.ts @@ -1,6 +1,8 @@ import { Component, Input, inject } from '@angular/core'; -import { DataService } from '../../services/data.service'; import { CommonModule } from '@angular/common'; +import { Observable } from 'rxjs'; +import { KeyValue } from '../../models/key-value'; +import { FilterService } from '../../services/filters.service'; @Component({ selector: 'fbi-select', @@ -11,7 +13,12 @@ import { CommonModule } from '@angular/common'; }) export class SelectComponent { @Input() label!: string; - @Input() data!: (string | number)[]; + @Input() key!: string; + @Input() data!: Observable; - private dataService = inject(DataService); + private filterService = inject(FilterService); + + onChange(event: any): void { + this.filterService.set(this.key, event?.target?.value); + } } diff --git a/src/components/table/header.ts b/src/components/table/header.ts new file mode 100644 index 0000000..9ba23c2 --- /dev/null +++ b/src/components/table/header.ts @@ -0,0 +1,9 @@ +export class Header { + label: string; + source: string; + + constructor(data: Partial
) { + this.label = data?.label ?? ''; + this.source = data?.source ?? ''; + } +} diff --git a/src/components/table/table.component.html b/src/components/table/table.component.html new file mode 100644 index 0000000..f27f42a --- /dev/null +++ b/src/components/table/table.component.html @@ -0,0 +1,12 @@ + + + + + + + +
+ {{ h.label }} +
+ {{ row[h.source] }} +
diff --git a/src/components/table/table.component.scss b/src/components/table/table.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/components/table/table.component.spec.ts b/src/components/table/table.component.spec.ts new file mode 100644 index 0000000..a2015da --- /dev/null +++ b/src/components/table/table.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TableComponent } from './table.component'; + +describe('TableComponent', () => { + let component: TableComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TableComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(TableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/components/table/table.component.ts b/src/components/table/table.component.ts new file mode 100644 index 0000000..6ce3342 --- /dev/null +++ b/src/components/table/table.component.ts @@ -0,0 +1,15 @@ +import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; +import { Header } from './header'; + +@Component({ + selector: 'fbi-table', + standalone: true, + imports: [CommonModule], + templateUrl: './table.component.html', + styleUrl: './table.component.scss', +}) +export class TableComponent { + @Input() header!: Header[]; + @Input() rows!: Record[]; +} diff --git a/src/enums/column.ts b/src/enums/column.ts deleted file mode 100644 index 3f6bc5e..0000000 --- a/src/enums/column.ts +++ /dev/null @@ -1,28 +0,0 @@ -export enum Column { - County = 2, - School = 3, - ELA_Count = 4, - ELA_Mean = 5, - ELA_0 = 6, - ELA_1 = 7, - ELA_2 = 8, - ELA_3 = 9, - Math_Count = 12, - Math_Mean = 13, - Math_0 = 14, - Math_1 = 15, - Math_2 = 16, - Math_3 = 17, - Sci_Count = 20, - Sci_Mean = 21, - Sci_0 = 22, - Sci_1 = 23, - Sci_2 = 24, - Sci_3 = 25, - Soc_Count = 28, - Soc_Mean = 29, - Soc_0 = 30, - Soc_1 = 31, - Soc_2 = 32, - Soc_3 = 33, -} diff --git a/src/enums/csv.ts b/src/enums/csv.ts new file mode 100644 index 0000000..d32ec35 --- /dev/null +++ b/src/enums/csv.ts @@ -0,0 +1,30 @@ +export enum Csv { + School_Code = 'School Code', + School_Label = 'School Name', + County_Code = 'System Code', + County_Label = 'System Name', + ELA_Count = 'English Language Arts Number Tested', + ELA_Mean = 'English Language Arts Mean Scale Score', + ELA_L0 = 'English Language Arts % Beginning Learner', + ELA_L1 = 'English Language Arts % Developing Learner', + ELA_L2 = 'English Language Arts % Proficient Learner', + ELA_L3 = 'English Language Arts % Distinguished Learner', + Math_Count = 'Mathematics Number Tested', + Math_Mean = 'Mathematics Mean Scale Score', + Math_L0 = 'Mathematics % Beginning Learner', + Math_L1 = 'Mathematics % Developing Learner', + Math_L2 = 'Mathematics % Proficient Learner', + Math_L3 = 'Mathematics % Distinguished Learner', + Science_Count = 'Science Number Tested', + Science_Mean = 'Science Mean Scale Score', + Science_L0 = 'Science % Beginning Learner', + Science_L1 = 'Science % Developing Learner', + Science_L2 = 'Science % Proficient Learner', + Science_L3 = 'Science % Distinguished Learner', + Social_Count = 'Social Studies Number Tested', + Social_Mean = 'Social Studies Mean Scale Score', + Social_L0 = 'Social Studies % Beginning Learner', + Social_L1 = 'Social Studies % Developing Learner', + Social_L2 = 'Social Studies % Proficient Learner', + Social_L3 = 'Social Studies % Distinguished Learner', +} diff --git a/src/enums/milestone.ts b/src/enums/milestone.ts new file mode 100644 index 0000000..ac3d3a6 --- /dev/null +++ b/src/enums/milestone.ts @@ -0,0 +1,35 @@ +export enum Milestone { + Cohort = 'cohort', + Year = 'year', + County = 'mcode', + School = 'lcode', + Grade = 'grade', + ELACount = 'ecount', + ELAMean = 'emean', + ELA0 = 'ela0', + ELA1 = 'ela1', + ELA2 = 'ela2', + ELA3 = 'ela3', + ELARank = 'elarank', + MathCount = 'mcount', + MathMean = 'mmean', + Math0 = 'math0', + Math1 = 'math1', + Math2 = 'math2', + Math3 = 'math3', + MathRank = 'mathrank', + SciCount = 'ccount', + SciMean = 'cmean', + Sci0 = 'sci0', + Sci1 = 'sci1', + Sci2 = 'sci2', + Sci3 = 'sci3', + SciRank = 'scirank', + SocCount = 'ocount', + SocMean = 'omean', + Soc0 = 'soc0', + Soc1 = 'soc1', + Soc2 = 'soc2', + Soc3 = 'soc3', + SocRank = 'socrank', +} diff --git a/src/models/key-value.ts b/src/models/key-value.ts new file mode 100644 index 0000000..7c72855 --- /dev/null +++ b/src/models/key-value.ts @@ -0,0 +1,9 @@ +export class KeyValue { + key: string; + value: string; + + constructor(data: Partial) { + this.key = data?.key ?? ''; + this.value = data?.value ?? ''; + } +} diff --git a/src/models/milestone.ts b/src/models/milestone.ts deleted file mode 100644 index 1e2a483..0000000 --- a/src/models/milestone.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Score } from './score'; - -export class Milestone { - Year: number; - Grade: number; - County: string; - School: string; - ELA: Score; - Math: Score; - Science: Score; - Social: Score; - - constructor(data: Partial) { - this.Year = data?.Year ?? 2000; - this.Grade = data?.Grade ?? 0; - this.County = data?.County ?? ''; - this.School = data?.School ?? ''; - this.ELA = new Score(data?.ELA ?? {}); - this.Math = new Score(data?.Math ?? {}); - this.Science = new Score(data?.Science ?? {}); - this.Social = new Score(data?.Social ?? {}); - } -} diff --git a/src/models/score.ts b/src/models/score.ts deleted file mode 100644 index aefe359..0000000 --- a/src/models/score.ts +++ /dev/null @@ -1,17 +0,0 @@ -export class Score { - Count: number; - Mean: number; - L0: number; - L1: number; - L2: number; - L3: number; - - constructor(data: Partial) { - this.Count = data?.Count ?? 0; - this.Mean = data?.Mean ?? 0; - this.L0 = data?.L0 ?? 0; - this.L1 = data?.L1 ?? 0; - this.L2 = data?.L2 ?? 0; - this.L3 = data?.L3 ?? 0; - } -} diff --git a/src/services/data.service.ts b/src/services/data.service.ts index eab2c75..74309f4 100644 --- a/src/services/data.service.ts +++ b/src/services/data.service.ts @@ -1,118 +1,210 @@ import { Injectable } from '@angular/core'; import Papa, { ParseRemoteConfig, ParseResult } from 'papaparse'; -import { Milestone } from '../models/milestone'; -import { Column } from '../enums/column'; -import { Subject } from 'rxjs'; +import { ReplaySubject } from 'rxjs'; +import { KeyValue } from '../models/key-value'; +import { Csv } from '../enums/csv'; +import { Milestone } from '../enums/milestone'; @Injectable({ providedIn: 'root' }) export class DataService { - private _data: Milestone[] = []; + private _data: Record[] = []; + private data = new ReplaySubject[]>(); + readonly Data = this.data.asObservable(); - private counties = new Subject(); + private _counties = new Map(); + private counties = new ReplaySubject(); readonly Counties = this.counties.asObservable(); - private schools = new Subject(); + private _schools = new Map(); + private schools = new ReplaySubject(); readonly Schools = this.schools.asObservable(); - private years = new Subject(); + private _years = Array(2023 - 2015 + 1) + .fill(0) + .map((_, index) => 2015 + index) + .filter((y) => y !== 2020); + private years = new ReplaySubject(); readonly Years = this.years.asObservable(); - private grades = new Subject(); + private _grades = Array(8 - 3 + 1) + .fill(0) + .map((_, index) => 3 + index); + private grades = new ReplaySubject(); readonly Grades = this.grades.asObservable(); + private _cohorts = new Map(); + private cohorts = new ReplaySubject(); + readonly Cohorts = this.cohorts.asObservable(); + constructor() { this.load(); } private load(): void { - let files: { url: string; year: number; grade: number }[] = []; - for (let year = 2015; year < 2024; ++year) { - if (year !== 2020) { - for (let grade = 3; grade < 9; ++grade) { - files.push({ - url: `assets/data/${year}-${grade}.csv`, - year: year, - grade: grade, - }); - } - } - } + let count = this._years.length * this._grades.length; + this._years.forEach((year: number) => { + this._grades.forEach((grade: number) => { + Papa.parse(`assets/data/${year}-${grade}.csv`, { + download: true, + header: false, + complete: (results: ParseResult>) => { + const headers = this.getHeaders((results?.data ?? []).slice(1, 3)); + const lookup = (x: string, record: Record): any => { + const idx = headers.findIndex((h: string) => x === h); + return (idx > -1 ? record[idx] : '') as any; + }; + const cohort = year + 12 - grade; + this._cohorts.set(`${cohort}`, `Class of ${cohort}`); - let count = files.length; - files.forEach((file: { url: string; year: number; grade: number }) => { - Papa.parse(file.url, { - download: true, - header: false, - complete: (results: ParseResult>) => { - (results?.data ?? []).forEach( - (record: Record, index: number) => { - if (index < 3) return; - this._data.push( - new Milestone({ - Year: file.year, - Grade: file.grade, - County: record[Column.County], - School: record[Column.School], - ELA: { - Count: record[Column.ELA_Count], - Mean: record[Column.ELA_Mean], - L0: record[Column.ELA_0], - L1: record[Column.ELA_1], - L2: record[Column.ELA_2], - L3: record[Column.ELA_3], - }, - Math: { - Count: record[Column.Math_Count], - Mean: record[Column.Math_Mean], - L0: record[Column.Math_0], - L1: record[Column.Math_1], - L2: record[Column.Math_2], - L3: record[Column.Math_3], - }, - Science: { - Count: record[Column.Sci_Count], - Mean: record[Column.Sci_Mean], - L0: record[Column.Sci_0], - L1: record[Column.Sci_1], - L2: record[Column.Sci_2], - L3: record[Column.Sci_3], - }, - Social: { - Count: record[Column.Soc_Count], - Mean: record[Column.Soc_Mean], - L0: record[Column.Soc_0], - L1: record[Column.Soc_1], - L2: record[Column.Soc_2], - L3: record[Column.Soc_3], - }, - }) + const data: Record[] = []; + + (results?.data ?? []).forEach((record: Record) => { + let code = parseInt(lookup(Csv.County_Code, record)); + // skip non-data rows on the csv + if (isNaN(code)) return; + + const ms = {} as Record; + // const ms = new Milestone({}); + ms[Milestone.Year] = year; + ms[Milestone.Grade] = grade; + ms[Milestone.Cohort] = cohort; + ms[Milestone.County] = lookup(Csv.County_Code, record); + ms[Milestone.School] = lookup(Csv.School_Code, record); + ms[Milestone.ELACount] = lookup(Csv.ELA_Count, record); + ms[Milestone.ELAMean] = lookup(Csv.ELA_Mean, record); + ms[Milestone.ELA0] = lookup(Csv.ELA_L0, record); + ms[Milestone.ELA1] = lookup(Csv.ELA_L1, record); + ms[Milestone.ELA2] = lookup(Csv.ELA_L2, record); + ms[Milestone.ELA3] = lookup(Csv.ELA_L3, record); + ms[Milestone.MathCount] = lookup(Csv.Math_Count, record); + ms[Milestone.MathMean] = lookup(Csv.Math_Mean, record); + ms[Milestone.Math0] = lookup(Csv.Math_L0, record); + ms[Milestone.Math1] = lookup(Csv.Math_L1, record); + ms[Milestone.Math2] = lookup(Csv.Math_L2, record); + ms[Milestone.Math3] = lookup(Csv.Math_L3, record); + ms[Milestone.SciCount] = lookup(Csv.Science_Count, record); + ms[Milestone.SciMean] = lookup(Csv.Science_Mean, record); + ms[Milestone.Sci0] = lookup(Csv.Science_L0, record); + ms[Milestone.Sci1] = lookup(Csv.Science_L1, record); + ms[Milestone.Sci2] = lookup(Csv.Science_L2, record); + ms[Milestone.Sci3] = lookup(Csv.Science_L3, record); + ms[Milestone.SocCount] = lookup(Csv.Social_Count, record); + ms[Milestone.SocMean] = lookup(Csv.Social_Mean, record); + ms[Milestone.Soc0] = lookup(Csv.Social_L0, record); + ms[Milestone.Soc1] = lookup(Csv.Social_L1, record); + ms[Milestone.Soc2] = lookup(Csv.Social_L2, record); + ms[Milestone.Soc3] = lookup(Csv.Social_L3, record); + data.push(ms); + + this._counties.set( + lookup(Csv.County_Code, record), + lookup(Csv.County_Label, record) ); - } - ); - --count; - if (count === 0) this.loadComplete(); - }, - error: (error: any) => { - console.error(error); - --count; - if (count === 0) this.loadComplete(); - }, - } as ParseRemoteConfig); + this._schools.set( + `${lookup(Csv.County_Code, record)}-${lookup( + Csv.School_Code, + record + )}`, + lookup(Csv.School_Label, record) + ); + }); + + [ + { sort: Milestone.ELAMean, rank: Milestone.ELARank }, + { sort: Milestone.MathMean, rank: Milestone.MathRank }, + { sort: Milestone.SciMean, rank: Milestone.SciRank }, + { sort: Milestone.SocMean, rank: Milestone.SocRank }, + ].forEach((m) => { + data + .sort( + (a: Record, b: Record) => + +a[m.sort] - b[m.sort] + ) + .forEach( + (a: Record, index: number) => (a[m.rank] = index) + ); + }); + + this._data.push(...data); + + --count; + if (count === 0) this.loadComplete(); + }, + error: (error: any) => { + console.error(error); + --count; + if (count === 0) this.loadComplete(); + }, + } as ParseRemoteConfig); + }); }); } + private getHeaders(data: Record[]): string[] { + const headers: string[] = []; + data.forEach((record: Record) => { + let prv = ''; + Object.keys(record).forEach((key: string, index: number) => { + let value = (record[key] ?? '') + .replace(/\n/g, '') + .replace(/- EOG/g, ''); + if (value.length === 0) value = prv; + + headers[index] = `${headers[index] ?? ''} ${value}`.trim(); + prv = value; + }); + }); + return headers; + } + private loadComplete(): void { + this.cohorts.next( + [...this._cohorts] + .map(([key, value]) => new KeyValue({ key: key, value: value })) + .sort((a: KeyValue, b: KeyValue) => a.value.localeCompare(b.value)) + ); this.counties.next( - [...new Set(this._data.map((data: Milestone) => data.County))].sort() + [...this._counties] + .map( + ([code, label]) => + new KeyValue({ + key: code, + value: label + .toLowerCase() + .replace(/\b[a-z]/g, (c) => c.toUpperCase()), + }) + ) + .sort((a: KeyValue, b: KeyValue) => a.value.localeCompare(b.value)) ); this.schools.next( - [...new Set(this._data.map((data: Milestone) => data.School))].sort() + [...this._schools] + .map( + ([code, label]) => + new KeyValue({ + key: code, + value: label + .toLowerCase() + .replace(/\b[a-z]/g, (c) => c.toUpperCase()), + }) + ) + .sort((a: KeyValue, b: KeyValue) => a.value.localeCompare(b.value)) ); this.years.next( - [...new Set(this._data.map((data: Milestone) => data.Year))].sort() + this._years + .map( + (year: number) => + new KeyValue({ key: year.toString(), value: year.toString() }) + ) + .sort((a: KeyValue, b: KeyValue) => a.value.localeCompare(b.value)) ); this.grades.next( - [...new Set(this._data.map((data: Milestone) => data.Grade))].sort() + this._grades + .map( + (grade: number) => + new KeyValue({ key: grade.toString(), value: grade.toString() }) + ) + .sort((a: KeyValue, b: KeyValue) => a.value.localeCompare(b.value)) ); + this.data.next(this._data); } } diff --git a/src/services/filters.service.ts b/src/services/filters.service.ts new file mode 100644 index 0000000..c6b55d3 --- /dev/null +++ b/src/services/filters.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; +import { KeyValue } from '../models/key-value'; + +@Injectable({ providedIn: 'root' }) +export class FilterService { + private _values: KeyValue[] = []; + private set filters(value: KeyValue[]) { + this._values = value; + this.filterSubject.next(this._values); + } + private get filters(): KeyValue[] { + return this._values; + } + private filterSubject = new Subject(); + readonly filters$ = this.filterSubject.asObservable(); + + constructor() {} + + set(key: string, value: any): void { + const update = this.filters.filter((f: KeyValue) => f.key !== key); + if (!!value) update.push(new KeyValue({ key: key, value: value })); + this.filters = update; + } +} -- 2.49.1