From fa8f1d51e882b66c9befb215d040e79478e13114 Mon Sep 17 00:00:00 2001 From: tomaz Date: Sat, 21 Jun 2025 12:56:23 +0200 Subject: [PATCH 01/11] ng generate @angular/core:standalone --- .../ng-datatable/src/lib/DataTableModule.ts | 24 ------------------- projects/ng-datatable/src/public-api.ts | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 projects/ng-datatable/src/lib/DataTableModule.ts diff --git a/projects/ng-datatable/src/lib/DataTableModule.ts b/projects/ng-datatable/src/lib/DataTableModule.ts deleted file mode 100644 index d0eaf15..0000000 --- a/projects/ng-datatable/src/lib/DataTableModule.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {NgModule} from "@angular/core"; - -import {DataTable} from "./DataTable"; -import {DefaultSorter} from "./DefaultSorter"; -import {Paginator} from "./Paginator"; -import {BootstrapPaginator} from "./BootstrapPaginator"; - -@NgModule({ - imports: [ - DataTable, - DefaultSorter, - Paginator, - BootstrapPaginator - ], - exports: [ - DataTable, - DefaultSorter, - Paginator, - BootstrapPaginator - ] -}) -export class DataTableModule { - -} diff --git a/projects/ng-datatable/src/public-api.ts b/projects/ng-datatable/src/public-api.ts index 849ca95..16ab967 100644 --- a/projects/ng-datatable/src/public-api.ts +++ b/projects/ng-datatable/src/public-api.ts @@ -6,4 +6,4 @@ export * from "./lib/BootstrapPaginator"; export * from "./lib/Paginator"; export * from "./lib/DefaultSorter"; export * from "./lib/DataTable"; -export * from "./lib/DataTableModule"; + From b358818734190618791cac5f076d3fa2aeb3c75a Mon Sep 17 00:00:00 2001 From: tomaz Date: Sat, 21 Jun 2025 13:02:32 +0200 Subject: [PATCH 02/11] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20demo=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/demo/src/app.component.ts | 89 ++++++++++++++++-------------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/projects/demo/src/app.component.ts b/projects/demo/src/app.component.ts index b58beb1..e54887b 100644 --- a/projects/demo/src/app.component.ts +++ b/projects/demo/src/app.component.ts @@ -1,52 +1,57 @@ import { Component, OnInit, inject } from "@angular/core"; -import {HttpClient} from "@angular/common/http"; -import {DataTableModule, SortBy, SortOrder} from "ng-datatable"; +import { HttpClient } from "@angular/common/http"; +import { + BootstrapPaginator, + DataTable, + DefaultSorter, + SortBy, + SortOrder, +} from "ng-datatable"; import { FormsModule } from "@angular/forms"; import { DataFilterPipe } from "./data-filter.pipe"; import { UpperCasePipe } from "@angular/common"; @Component({ - selector: "app-root", - templateUrl: "./app.component.html", - imports: [ - DataTableModule, - FormsModule, - DataFilterPipe, - UpperCasePipe - ] + selector: "app-root", + templateUrl: "./app.component.html", + imports: [ + FormsModule, + DataFilterPipe, + UpperCasePipe, + DataTable, + DefaultSorter, + BootstrapPaginator, + ], }) export class AppComponent implements OnInit { - private http = inject(HttpClient); - - - public data: any[]; - public filterQuery = ""; - public rowsOnPage = 10; - public sortBy: SortBy = "email"; - public sortOrder: SortOrder = "asc"; - - ngOnInit(): void { - this.http.get("/data.json") - .subscribe((data) => { - setTimeout(() => { - this.data = data; - }, 2000); - }); - } - - public toInt(num: string) { - return +num; + private http = inject(HttpClient); + + public data: any[]; + public filterQuery = ""; + public rowsOnPage = 10; + public sortBy: SortBy = "email"; + public sortOrder: SortOrder = "asc"; + + ngOnInit(): void { + this.http.get("/data.json").subscribe((data) => { + setTimeout(() => { + this.data = data; + }, 2000); + }); + } + + public toInt(num: string) { + return +num; + } + + public sortByWordLength = (a: any) => { + return a.city.length; + }; + + public remove(item: any) { + const index = this.data.indexOf(item); + if (index > -1) { + this.data.splice(index, 1); } - - public sortByWordLength = (a: any) => { - return a.city.length; - } - - public remove(item: any) { - const index = this.data.indexOf(item); - if (index > -1) { - this.data.splice(index, 1); - } - } - + } } From d4fef81cdf6cc74a9e68e93fbb361a2c07ce6b81 Mon Sep 17 00:00:00 2001 From: tomaz Date: Sat, 21 Jun 2025 13:05:01 +0200 Subject: [PATCH 03/11] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20change=20log=20+=20?= =?UTF-8?q?=E2=AC=86=EF=B8=8F=20bump=20minor=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 57 +++++++++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58b1125..c486f6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,26 @@ # Changelog +## 20.1.0 (2025-06-21) + +Changes + +- Use Angular 20 standalone + +```typescript +imports: [ + DataTable, + DefaultSorter, + BootstrapPaginator, + ], +``` + ## 20.0.0 (2025-06-20) Changes - Use Angular 20 (closes #20) -*Note: This library does not work with zoneless at the moment, contributions are welcome!* +_Note: This library does not work with zoneless at the moment, contributions are welcome!_ ## 19.0.0 (2025-01-28) @@ -44,11 +58,13 @@ Changes - Use Angular 16 ## 15.1.2 (2023-04-24) + Changes - Update dependencies to latest, fixes security alert ## 15.1.1 (2023-02-18) + Changes - Update dependencies to latest (closes #13) @@ -109,31 +125,31 @@ Changes: ## 11.1.0 (2020-11-23) Changes: - + - Remove dependency to lodash ## 11.0.1 (2020-11-23) Changes: - + - Specify correct peer dependency version ## 11.0.0 (2020-11-15) Changes: - + - Use Angular 11 ## 10.0.0 (2020-06-27) Changes: - + - Use Angular 10 ## 9.0.1 (2019-11-06) Changes: - + - Use Angular 9 - Use Angular CLI for build pipeline - Fix some linting issues @@ -141,42 +157,42 @@ Changes: ## 8.0.0 (2019-11-06) Changes: - + - Use Angular 8 ## 2.0.0 (2018-07-10) Changes: - + - Published as package @pascalhonegger/ng-datatable - Use Angular 7 ## 0.7.3 (2018-10-04) Changes: - + - Security updates in dependencies. ## 0.7.2 (2018-10-03) Changes: - + - Updated examples dependencies. (Thanks to @PascalHonegger) ## 0.7.1 (2018-10-03) Changes: - + - Angular and RxJS get updated to version 6 (see package.json). The only code changes include changes to systemjs.config and two import statements. (Pull Request #3. Thanks to @PascalHonegger) ## 0.7.0 (2018-03-15) Changes: - + - First commit of forked project (@cmglez10/ng-datatable) - Updated to Angular 5 - + ## 0.6.0 (2017-03-27) Fixes: @@ -186,9 +202,9 @@ Fixes: ## 0.5.2 (2016-11-13) Changes: - + - added inputs/outputs for sorting (#14) - + Bugfixes: - detect changes in inputData array (#10) @@ -198,10 +214,10 @@ Bugfixes: ## 0.5.1 (2016-10-25) Changes: - + - changed the old "typings" system to the new "@types" system - added support for AOT compilation - + Bugfixes: - sorting by child properties (#41) @@ -211,12 +227,12 @@ Bugfixes: Breaking changes: - update angular library to 2.0.0 - + Bugfixes: - sort case insensitive - fixed pagination, fix #29, #33 - + #Changelog ## 0.4.2 (2016-05-11) @@ -254,10 +270,9 @@ Bugfixes: - remove `href` attribute from DefaultSorter - add style `cursor: pointer` to links in DefaultSorter and BootstrapPaginator - + ## 0.2.2 (2016-03-21) Bugfixes: - remove `href` attribute from BootstrapPaginator template - diff --git a/package.json b/package.json index 44a9adc..8b3eee9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ng-datatable", - "version": "20.0.0", + "version": "20.1.0", "packageManager": "pnpm@10.12.1+sha512.f0dda8580f0ee9481c5c79a1d927b9164f2c478e90992ad268bbb2465a736984391d6333d2c327913578b2804af33474ca554ba29c04a8b13060a717675ae3ac", "scripts": { "ng": "ng", From f01100a42be5cea473fa057d51ad297e87eb6551 Mon Sep 17 00:00:00 2001 From: tomaz Date: Sat, 21 Jun 2025 14:55:46 +0200 Subject: [PATCH 04/11] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20signals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- .../ng-datatable/src/lib/DataTable.spec.ts | 79 ++-- projects/ng-datatable/src/lib/DataTable.ts | 376 ++++++++++-------- .../ng-datatable/src/lib/DefaultSorter.ts | 71 ++-- projects/ng-datatable/src/lib/Paginator.ts | 54 +-- 5 files changed, 319 insertions(+), 263 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c486f6f..4a689b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changes -- Use Angular 20 standalone +- Breaking using Angular 20 standalone ```typescript imports: [ diff --git a/projects/ng-datatable/src/lib/DataTable.spec.ts b/projects/ng-datatable/src/lib/DataTable.spec.ts index 71d3e11..2ec06a5 100644 --- a/projects/ng-datatable/src/lib/DataTable.spec.ts +++ b/projects/ng-datatable/src/lib/DataTable.spec.ts @@ -25,7 +25,7 @@ describe("DataTable directive tests", () => { {id: 5, name: "Ðrone"}, {id: 4, name: "Ananas"} ]; - datatable.ngOnChanges({inputData: new SimpleChange(null, datatable.inputData, false)}); + datatable.ngOnChanges({inputData: new SimpleChange(null, datatable.inputData(), false)}); }); describe("initializing", () => { @@ -38,7 +38,7 @@ describe("DataTable directive tests", () => { it("data should be equal to inputData", () => { datatable.ngDoCheck(); - expect(datatable.data).toEqual(datatable.inputData); + expect(datatable.data).toEqual(datatable.inputData()); }); it("data should be 2 first items", () => { @@ -131,8 +131,8 @@ describe("DataTable directive tests", () => { datatable.sortBy = "id"; datatable.sortOrder = "asc"; datatable.ngOnChanges({ - sortBy: new SimpleChange(null, datatable.sortBy, true), - sortOrder: new SimpleChange(null, datatable.sortOrder, true) + sortBy: new SimpleChange(null, datatable.sortBy(), true), + sortOrder: new SimpleChange(null, datatable.sortOrder(), true) }); datatable.ngDoCheck(); expect(datatable.data).toEqual([ @@ -154,8 +154,8 @@ describe("DataTable directive tests", () => { datatable.sortBy = "id"; datatable.sortOrder = "desc"; datatable.ngOnChanges({ - sortBy: new SimpleChange(null, datatable.sortBy, false), - sortOrder: new SimpleChange(null, datatable.sortOrder, false) + sortBy: new SimpleChange(null, datatable.sortBy(), false), + sortOrder: new SimpleChange(null, datatable.sortOrder(), false) }); datatable.ngDoCheck(); @@ -170,10 +170,10 @@ describe("DataTable directive tests", () => { datatable.ngDoCheck(); datatable.sortBy = "id"; datatable.ngOnChanges({ - sortBy: new SimpleChange(null, datatable.sortBy, false) + sortBy: new SimpleChange(null, datatable.sortBy(), false) }); datatable.ngDoCheck(); - expect(datatable.sortOrder).toEqual("asc"); + expect(datatable.sortOrder()).toEqual("asc"); }); it("should set sortOrder to 'asc' if provided something else than 'asc' or 'desc'", (done) => { @@ -185,12 +185,13 @@ describe("DataTable directive tests", () => { datatable.ngDoCheck(); datatable.sortBy = "id"; datatable.sortOrder = "bulb" as "asc"; + const sortOrder = datatable.sortOrder(); datatable.ngOnChanges({ - sortBy: new SimpleChange(null, datatable.sortBy, false), - sortOrder: new SimpleChange(null, datatable.sortOrder, false) + sortBy: new SimpleChange(null, datatable.sortBy(), false), + sortOrder: new SimpleChange(null, sortOrder, false) }); datatable.ngDoCheck(); - expect(datatable.sortOrder).toEqual("asc"); + expect(sortOrder).toEqual("asc"); expect(datatable.data).toEqual([ {id: 1, name: "Duck"}, {id: 2, name: "ącki"}, @@ -204,7 +205,7 @@ describe("DataTable directive tests", () => { datatable.setSort("id", "bulb" as "desc"); expect(datatable.getSort()).toEqual({sortBy: "id", sortOrder: "asc"}); datatable.ngDoCheck(); - expect(datatable.sortOrder).toEqual("asc"); + expect(datatable.sortOrder()).toEqual("asc"); expect(datatable.data).toEqual([ {id: 1, name: "Duck"}, {id: 2, name: "ącki"}, @@ -221,9 +222,9 @@ describe("DataTable directive tests", () => { }); datatable.ngDoCheck(); datatable.sortOrder = "desc"; - datatable.ngOnChanges({sortOrder: new SimpleChange(null, datatable.sortOrder, false)}); + datatable.ngOnChanges({sortOrder: new SimpleChange(null, datatable.sortOrder(), false)}); datatable.ngDoCheck(); - expect(datatable.data).toEqual(datatable.inputData); + expect(datatable.data).toEqual(datatable.inputData()); }); it("should call output event when sorting changed", (done) => { @@ -246,7 +247,7 @@ describe("DataTable directive tests", () => { }); done(); datatable.sortOrder = "bulb" as "desc"; - datatable.ngOnChanges({sortOrder: new SimpleChange(null, datatable.sortOrder, false)}); + datatable.ngOnChanges({sortOrder: new SimpleChange(null, datatable.sortOrder(), false)}); datatable.ngDoCheck(); }); // Wywołanie outputa gdy zmiana z innej strony @@ -293,7 +294,7 @@ describe("DataTable directive tests", () => { {name: "Claire", age: 7}, {name: "Anna", age: 12} ]; - datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData, newData, false)}); + datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData(), newData, false)}); datatable.setSort(["name", "age"], "asc"); datatable.ngDoCheck(); @@ -316,7 +317,7 @@ describe("DataTable directive tests", () => { {name: "Claire", city: {zip: "11111"}}, {name: "Anna", city: {zip: "21111"}} ]; - datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData, newData, false)}); + datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData(), newData, false)}); datatable.setSort("city.zip", "asc"); datatable.ngDoCheck(); @@ -334,25 +335,25 @@ describe("DataTable directive tests", () => { describe("data change", () => { it("should refresh data when inputData change", () => { const newData = [{id: 5, name: "Ðrone"}, {id: 4, name: "Ananas"}]; - datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData, newData, false)}); + datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData(), newData, false)}); datatable.ngDoCheck(); expect(datatable.data).toEqual([{id: 5, name: "Ðrone"}, {id: 4, name: "Ananas"}]); }); it("should refresh data when rows removed from inputData", () => { datatable.ngDoCheck(); - expect(datatable.data).toEqual(datatable.inputData); - datatable.inputData.pop(); + expect(datatable.data).toEqual(datatable.inputData()); + datatable.inputData().pop(); datatable.ngDoCheck(); - expect(datatable.data).toEqual(datatable.inputData); + expect(datatable.data).toEqual(datatable.inputData()); }); it("should refresh data when rows added to inputData", () => { datatable.ngDoCheck(); - expect(datatable.data).toEqual(datatable.inputData); - datatable.inputData.push({id: 6, name: "Furby"}); + expect(datatable.data).toEqual(datatable.inputData()); + datatable.inputData().push({id: 6, name: "Furby"}); datatable.ngDoCheck(); - expect(datatable.data).toEqual(datatable.inputData); + expect(datatable.data).toEqual(datatable.inputData()); }); it("should fire onPageChange event after inputData change", (done) => { @@ -366,7 +367,7 @@ describe("DataTable directive tests", () => { done(); }); const newData = [{id: 5, name: "Ðrone"}, {id: 4, name: "Ananas"}]; - datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData, newData, false)}); + datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData(), newData, false)}); datatable.ngDoCheck(); }); @@ -380,7 +381,7 @@ describe("DataTable directive tests", () => { expect(opt.rowsOnPage).toEqual(2); done(); }); - datatable.inputData.push({id: 6, name: "Furby"}); + datatable.inputData().push({id: 6, name: "Furby"}); datatable.ngDoCheck(); }); @@ -394,7 +395,7 @@ describe("DataTable directive tests", () => { expect(opt.rowsOnPage).toEqual(2); done(); }); - range(0, 3).forEach(() => datatable.inputData.pop()); + range(0, 3).forEach(() => datatable.inputData().pop()); datatable.ngDoCheck(); }); @@ -403,7 +404,7 @@ describe("DataTable directive tests", () => { datatable.ngDoCheck(); const newData = [{id: 5, name: "Ðrone"}, {id: 4, name: "Ananas"}]; - datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData, newData, false)}); + datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData(), newData, false)}); datatable.ngDoCheck(); expect(datatable.data).toEqual(newData); }); @@ -413,9 +414,9 @@ describe("DataTable directive tests", () => { datatable.ngDoCheck(); expect(datatable.data).toEqual([{id: 2, name: "ącki"}, {id: 5, name: "Ðrone"}]); - datatable.inputData.pop(); - datatable.inputData.pop(); - datatable.inputData.pop(); + datatable.inputData().pop(); + datatable.inputData().pop(); + datatable.inputData().pop(); datatable.ngDoCheck(); expect(datatable.data).toEqual([{id: 3, name: "banana"}, {id: 1, name: "Duck"}]); }); @@ -425,7 +426,7 @@ describe("DataTable directive tests", () => { datatable.ngDoCheck(); const newData = [{id: 5, name: "Ðrone"}, {id: 1, name: "Duck"}, {id: 4, name: "Ananas"}]; - datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData, newData, false)}); + datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData(), newData, false)}); datatable.ngDoCheck(); expect(datatable.data).toEqual([{id: 1, name: "Duck"}]); }); @@ -435,7 +436,7 @@ describe("DataTable directive tests", () => { datatable.ngDoCheck(); expect(datatable.data).toEqual([{id: 1, name: "Duck"}]); - datatable.inputData.pop(); + datatable.inputData().pop(); datatable.ngDoCheck(); expect(datatable.data).toEqual([{id: 1, name: "Duck"}]); }); @@ -445,7 +446,7 @@ describe("DataTable directive tests", () => { datatable.ngDoCheck(); expect(datatable.data).toEqual([{id: 1, name: "Duck"}]); - datatable.inputData.push({id: 6, name: "Furby"}); + datatable.inputData().push({id: 6, name: "Furby"}); datatable.ngDoCheck(); expect(datatable.data).toEqual([{id: 1, name: "Duck"}]); }); @@ -454,19 +455,19 @@ describe("DataTable directive tests", () => { datatable.setPage(2, 1); datatable.ngDoCheck(); - datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData, [], false)}); + datatable.ngOnChanges({inputData: new SimpleChange(datatable.inputData(), [], false)}); datatable.ngDoCheck(); - expect(datatable.activePage).toEqual(1); + expect(datatable.activePage()).toEqual(1); }); it("shouldn't change page to 0 when data is empty after removed rows", () => { datatable.setPage(2, 1); datatable.ngDoCheck(); - range(0, 5).forEach(() => datatable.inputData.pop()); + range(0, 5).forEach(() => datatable.inputData().pop()); datatable.ngDoCheck(); - expect(datatable.inputData.length).toEqual(0); - expect(datatable.activePage).toEqual(1); + expect(datatable.inputData().length).toEqual(0); + expect(datatable.activePage()).toEqual(1); }); }); }); diff --git a/projects/ng-datatable/src/lib/DataTable.ts b/projects/ng-datatable/src/lib/DataTable.ts index cae45a0..4a220b9 100644 --- a/projects/ng-datatable/src/lib/DataTable.ts +++ b/projects/ng-datatable/src/lib/DataTable.ts @@ -1,211 +1,259 @@ -import { Directive, Input, EventEmitter, OnChanges, DoCheck, IterableDiffers, IterableDiffer, Output, inject, SimpleChanges } from "@angular/core"; +import { + Directive, + EventEmitter, + OnChanges, + DoCheck, + IterableDiffers, + IterableDiffer, + Output, + inject, + SimpleChanges, + model, +} from "@angular/core"; import { ReplaySubject } from "rxjs"; - export type SortOrder = "asc" | "desc"; export type SortByFunction = (data: T) => any; -export type SortBy = string | SortByFunction | (string | SortByFunction)[]; +export type SortBy = + | string + | SortByFunction + | (string | SortByFunction)[]; export interface SortEvent { - sortBy: SortBy; - sortOrder: string; + sortBy: SortBy; + sortOrder: string; } export interface PageEvent { - activePage: number; - rowsOnPage: number; - dataLength: number; + activePage: number; + rowsOnPage: number; + dataLength: number; } export interface DataEvent { - length: number; + length: number; } @Directive({ - selector: "table[mfData]", - exportAs: "mfDataTable" + selector: "table[mfData]", + exportAs: "mfDataTable", }) export class DataTable implements OnChanges, DoCheck { + #diff: IterableDiffer; + readonly inputData = model([], { alias: "mfData" }); - private diff: IterableDiffer; - @Input("mfData") public inputData: T[] = []; + readonly sortBy = model>("", { alias: "mfSortBy" }); + readonly sortOrder = model("asc", { alias: "mfSortOrder" }); + @Output("mfSortByChange") sortByChange = new EventEmitter>(); + @Output("mfSortOrderChange") sortOrderChange = new EventEmitter(); - @Input("mfSortBy") public sortBy: SortBy = ""; - @Input("mfSortOrder") public sortOrder: SortOrder = "asc"; - @Output("mfSortByChange") public sortByChange = new EventEmitter>(); - @Output("mfSortOrderChange") public sortOrderChange = new EventEmitter(); + readonly rowsOnPage = model(1000, { alias: "mfRowsOnPage" }); + readonly activePage = model(1, { alias: "mfActivePage" }); - @Input("mfRowsOnPage") public rowsOnPage = 1000; - @Input("mfActivePage") public activePage = 1; + #mustRecalculateData = false; - private mustRecalculateData = false; + data: T[]; - public data: T[]; + onSortChange = new ReplaySubject(1); + onPageChange = new EventEmitter(); - public onSortChange = new ReplaySubject(1); - public onPageChange = new EventEmitter(); + constructor() { + const differs = inject(IterableDiffers); - public constructor() { - const differs = inject(IterableDiffers); + this.#diff = differs.find([]).create(); + } - this.diff = differs.find([]).create(); - } + getSort(): SortEvent { + return { sortBy: this.sortBy(), sortOrder: this.sortOrder() }; + } - public getSort(): SortEvent { - return { sortBy: this.sortBy, sortOrder: this.sortOrder }; + setSort(sortBy: SortBy, sortOrder: SortOrder): void { + const sortByValue = this.sortBy(); + const sortOrderValue = this.sortOrder(); + if (sortByValue !== sortBy || sortOrderValue !== sortOrder) { + this.sortBy.set(sortBy); + this.sortOrder.set( + ["asc", "desc"].indexOf(sortOrder) >= 0 ? sortOrder : "asc" + ); + this.#mustRecalculateData = true; + this.onSortChange.next({ + sortBy: sortByValue, + sortOrder: sortOrderValue, + }); + this.sortByChange.emit(sortByValue); + this.sortOrderChange.emit(sortOrderValue); } + } - public setSort(sortBy: SortBy, sortOrder: SortOrder): void { - if (this.sortBy !== sortBy || this.sortOrder !== sortOrder) { - this.sortBy = sortBy; - this.sortOrder = ["asc", "desc"].indexOf(sortOrder) >= 0 ? sortOrder : "asc"; - this.mustRecalculateData = true; - this.onSortChange.next({ sortBy: this.sortBy, sortOrder: this.sortOrder }); - this.sortByChange.emit(this.sortBy); - this.sortOrderChange.emit(this.sortOrder); - } - } + getPage(): PageEvent { + return { + activePage: this.activePage(), + rowsOnPage: this.rowsOnPage(), + dataLength: this.inputData().length, + }; + } - public getPage(): PageEvent { - return { activePage: this.activePage, rowsOnPage: this.rowsOnPage, dataLength: this.inputData.length }; + setPage(activePageIn: number, rowsOnPageIn: number): void { + if ( + this.rowsOnPage() !== rowsOnPageIn || + this.activePage() !== activePageIn + ) { + this.activePage.set( + this.activePage() !== activePageIn + ? activePageIn + : this.#calculateNewActivePage(this.rowsOnPage(), rowsOnPageIn) + ); + this.rowsOnPage.set(rowsOnPageIn); + this.#mustRecalculateData = true; + const inputData = this.inputData(); + this.onPageChange.emit({ + activePage: this.activePage(), + rowsOnPage: this.rowsOnPage(), + dataLength: inputData ? inputData.length : 0, + }); } + } - public setPage(activePage: number, rowsOnPage: number): void { - if (this.rowsOnPage !== rowsOnPage || this.activePage !== activePage) { - this.activePage = this.activePage !== activePage ? activePage : this.calculateNewActivePage(this.rowsOnPage, rowsOnPage); - this.rowsOnPage = rowsOnPage; - this.mustRecalculateData = true; - this.onPageChange.emit({ - activePage: this.activePage, - rowsOnPage: this.rowsOnPage, - dataLength: this.inputData ? this.inputData.length : 0 - }); - } - } + #calculateNewActivePage( + previousRowsOnPage: number, + currentRowsOnPage: number + ): number { + const firstRowOnPage = (this.activePage() - 1) * previousRowsOnPage + 1; + const newActivePage = Math.ceil(firstRowOnPage / currentRowsOnPage); + return newActivePage; + } - private calculateNewActivePage(previousRowsOnPage: number, currentRowsOnPage: number): number { - const firstRowOnPage = (this.activePage - 1) * previousRowsOnPage + 1; - const newActivePage = Math.ceil(firstRowOnPage / currentRowsOnPage); - return newActivePage; - } + #recalculatePage() { + const lastPage = Math.ceil(this.inputData().length / this.rowsOnPage()); + this.activePage.set( + lastPage < this.activePage() ? lastPage : this.activePage() + ); + this.activePage.set(this.activePage() || 1); - private recalculatePage() { - const lastPage = Math.ceil(this.inputData.length / this.rowsOnPage); - this.activePage = lastPage < this.activePage ? lastPage : this.activePage; - this.activePage = this.activePage || 1; + this.onPageChange.emit({ + activePage: this.activePage(), + rowsOnPage: this.rowsOnPage(), + dataLength: this.inputData().length, + }); + } - this.onPageChange.emit({ - activePage: this.activePage, - rowsOnPage: this.rowsOnPage, - dataLength: this.inputData.length - }); + ngOnChanges(changes: SimpleChanges): any { + if (changes["rowsOnPage"]) { + this.rowsOnPage.set(changes["rowsOnPage"].previousValue); + this.setPage(this.activePage(), changes["rowsOnPage"].currentValue); + this.#mustRecalculateData = true; } - - public ngOnChanges(changes: SimpleChanges): any { - if (changes["rowsOnPage"]) { - this.rowsOnPage = changes["rowsOnPage"].previousValue; - this.setPage(this.activePage, changes["rowsOnPage"].currentValue); - this.mustRecalculateData = true; - } - if (changes["sortBy"] || changes["sortOrder"]) { - if (["asc", "desc"].indexOf(this.sortOrder) < 0) { - console.warn("ng-datatable: value for input mfSortOrder must be one of ['asc', 'desc'], but is:", this.sortOrder); - this.sortOrder = "asc"; - } - if (this.sortBy) { - this.onSortChange.next({ sortBy: this.sortBy, sortOrder: this.sortOrder }); - } - this.mustRecalculateData = true; - } - if (changes["inputData"]) { - this.inputData = changes["inputData"].currentValue || []; - this.diff.diff(this.inputData); // Update diff to prevent duplicate update in ngDoCheck - this.recalculatePage(); - this.mustRecalculateData = true; - } + if (changes["sortBy"] || changes["sortOrder"]) { + const sortOrder = this.sortOrder(); + if (["asc", "desc"].indexOf(this.sortOrder()) < 0) { + console.warn( + "ng-datatable: value for input mfSortOrder must be one of ['asc', 'desc'], but is:", + sortOrder + ); + this.sortOrder.set("asc"); + } + const sortBy = this.sortBy(); + if (sortBy) { + this.onSortChange.next({ sortBy: sortBy, sortOrder: sortOrder }); + } + this.#mustRecalculateData = true; } - - public ngDoCheck(): any { - const changes = this.diff.diff(this.inputData); - if (changes) { - this.recalculatePage(); - this.mustRecalculateData = true; - } - if (this.mustRecalculateData) { - this.fillData(); - this.mustRecalculateData = false; - } + if (changes["inputData"]) { + this.inputData.set(changes["inputData"].currentValue || []); + this.#diff.diff(this.inputData()); // Update diff to prevent duplicate update in ngDoCheck + this.#recalculatePage(); + this.#mustRecalculateData = true; } + } - private fillData(): void { - // this.activePage = this.activePage; - // this.rowsOnPage = this.rowsOnPage; - - const offset = (this.activePage - 1) * this.rowsOnPage; - // let data = this.inputData; - // const sortBy = this.sortBy; - // if (typeof sortBy === "string" || sortBy instanceof String) { - // data = orderBy(data, this.caseInsensitiveIteratee(sortBy as string), [this.sortOrder]); - // } else { - // data = orderBy(data, sortBy, [this.sortOrder]); - // } - // data = slice(data, offset, offset + this.rowsOnPage); - - this.data = [...this.inputData] - .sort(this.sorter(this.sortBy, this.sortOrder)) - .slice(offset, offset + this.rowsOnPage); + ngDoCheck(): any { + const changes = this.#diff.diff(this.inputData()); + if (changes) { + this.#recalculatePage(); + this.#mustRecalculateData = true; } - - private caseInsensitiveIteratee(sortBy: string | SortByFunction) { - return (row: any): any => { - let value = row; - if (typeof sortBy === "string" || sortBy instanceof String) { - for (const sortByProperty of sortBy.split(".")) { - if (value) { - value = value[sortByProperty]; - } - } - } else if (typeof sortBy === "function") { - value = sortBy(value); - } - - if (value && typeof value === "string" || value instanceof String) { - return value.toLowerCase(); - } - - return value; - }; + if (this.#mustRecalculateData) { + this.#fillData(); + this.#mustRecalculateData = false; } + } - private compare(left: any, right: any): number { - if (left === right) { - return 0; - } - if (left == null && right != null) { - return -1; - } - if (right == null) { - return 1; + #fillData(): void { + // this.activePage = this.activePage; + // this.rowsOnPage = this.rowsOnPage; + + const offset = (this.activePage() - 1) * this.rowsOnPage(); + // let data = this.inputData; + // const sortBy = this.sortBy; + // if (typeof sortBy === "string" || sortBy instanceof String) { + // data = orderBy(data, this.caseInsensitiveIteratee(sortBy as string), [this.sortOrder]); + // } else { + // data = orderBy(data, sortBy, [this.sortOrder]); + // } + // data = slice(data, offset, offset + this.rowsOnPage); + + this.data = [...this.inputData()] + .sort(this.sorter(this.sortBy(), this.sortOrder())) + .slice(offset, offset + this.rowsOnPage()); + } + + #caseInsensitiveIteratee(sortBy: string | SortByFunction) { + return (row: any): any => { + let value = row; + if (typeof sortBy === "string" || sortBy instanceof String) { + for (const sortByProperty of sortBy.split(".")) { + if (value) { + value = value[sortByProperty]; + } } - return left > right ? 1 : -1; + } else if (typeof sortBy === "function") { + value = sortBy(value); + } + + if ((value && typeof value === "string") || value instanceof String) { + return value.toLowerCase(); + } + + return value; + }; + } + + #compare(left: any, right: any): number { + if (left === right) { + return 0; + } + if (left == null && right != null) { + return -1; + } + if (right == null) { + return 1; } + return left > right ? 1 : -1; + } - private sorter(sortBy: SortBy, sortOrder: SortOrder): (left: T, right: T) => number { - const order = sortOrder === "desc" ? -1 : 1; - if (Array.isArray(sortBy)) { - const iteratees = sortBy.map((entry) => this.caseInsensitiveIteratee(entry)); - return (left, right) => { - for (const iteratee of iteratees) { - const comparison = this.compare(iteratee(left), iteratee(right)) * order; - if (comparison !== 0) { - return comparison; - } - } - return 0; - }; - } else { - const iteratee = this.caseInsensitiveIteratee(sortBy); - return (left, right) => this.compare(iteratee(left), iteratee(right)) * order; + sorter( + sortBy: SortBy, + sortOrder: SortOrder + ): (left: T, right: T) => number { + const order = sortOrder === "desc" ? -1 : 1; + if (Array.isArray(sortBy)) { + const iteratees = sortBy.map((entry) => + this.#caseInsensitiveIteratee(entry) + ); + return (left, right) => { + for (const iteratee of iteratees) { + const comparison = + this.#compare(iteratee(left), iteratee(right)) * order; + if (comparison !== 0) { + return comparison; + } } + return 0; + }; + } else { + const iteratee = this.#caseInsensitiveIteratee(sortBy); + return (left, right) => + this.#compare(iteratee(left), iteratee(right)) * order; } + } } diff --git a/projects/ng-datatable/src/lib/DefaultSorter.ts b/projects/ng-datatable/src/lib/DefaultSorter.ts index 0b04b15..71dafb8 100644 --- a/projects/ng-datatable/src/lib/DefaultSorter.ts +++ b/projects/ng-datatable/src/lib/DefaultSorter.ts @@ -1,42 +1,49 @@ -import { Component, OnInit, inject, input } from "@angular/core"; -import {DataTable, SortBy, SortEvent} from "./DataTable"; +import { Component, OnInit, inject, input, signal } from "@angular/core"; +import { DataTable, SortBy, SortEvent } from "./DataTable"; @Component({ - selector: "mfDefaultSorter", - template: ` - - - @if (isSortedByMeAsc) { - - } @else if (isSortedByMeDesc) { - - } - `, - styles: [ - "a { cursor: pointer; }" - ] + selector: "mfDefaultSorter", + template: ` + + @if (isSortedByMeAsc()) { + + } @else if (isSortedByMeDesc()) { + + } + `, + styles: ["a { cursor: pointer; }"], }) export class DefaultSorter implements OnInit { - private mfTable = inject(DataTable); + #mfTable = inject(DataTable); - readonly sortBy = input.required>({ alias: "by" }); + readonly sortBy = input.required>({ alias: "by" }); - isSortedByMeAsc = false; - isSortedByMeDesc = false; + isSortedByMeAsc = signal(false); + isSortedByMeDesc = signal(false); - public ngOnInit(): void { - this.mfTable.onSortChange.subscribe((event: SortEvent) => { - this.isSortedByMeAsc = (event.sortBy == this.sortBy() && event.sortOrder === "asc"); - this.isSortedByMeDesc = (event.sortBy == this.sortBy() && event.sortOrder === "desc"); - }); - } + ngOnInit(): void { + this.#mfTable.onSortChange.subscribe((event: SortEvent) => { + this.isSortedByMeAsc.set( + event.sortBy == this.sortBy() && event.sortOrder === "asc" + ); + this.isSortedByMeDesc.set( + event.sortBy == this.sortBy() && event.sortOrder === "desc" + ); + }); + } - sort() { - if (this.isSortedByMeAsc) { - this.mfTable.setSort(this.sortBy(), "desc"); - } else { - this.mfTable.setSort(this.sortBy(), "asc"); - } - return false; + sort() { + if (this.isSortedByMeAsc()) { + this.#mfTable.setSort(this.sortBy(), "desc"); + } else { + this.#mfTable.setSort(this.sortBy(), "asc"); } + return false; + } } diff --git a/projects/ng-datatable/src/lib/Paginator.ts b/projects/ng-datatable/src/lib/Paginator.ts index 3467d74..ad60f21 100644 --- a/projects/ng-datatable/src/lib/Paginator.ts +++ b/projects/ng-datatable/src/lib/Paginator.ts @@ -1,40 +1,40 @@ import { Component, OnChanges, inject, input } from "@angular/core"; -import {DataTable, PageEvent} from "./DataTable"; +import { DataTable, PageEvent } from "./DataTable"; @Component({ - selector: "mfPaginator", - template: `` + selector: "mfPaginator", + template: ``, }) export class Paginator implements OnChanges { - private injectMfTable = inject(DataTable, { optional: true })!; + #injectMfTable = inject(DataTable, { optional: true })!; - readonly inputMfTable = input(undefined, { alias: "mfTable" }); + readonly inputMfTable = input(undefined, { alias: "mfTable" }); - private mfTable: DataTable; + #mfTable: DataTable; - public activePage: number; - public rowsOnPage: number; - public dataLength = 0; - public lastPage: number; + activePage: number; + rowsOnPage: number; + dataLength = 0; + lastPage: number; - public ngOnChanges(): any { - this.mfTable = this.inputMfTable() ?? this.injectMfTable; - this.onPageChangeSubscriber(this.mfTable.getPage()); - this.mfTable.onPageChange.subscribe(this.onPageChangeSubscriber); - } + ngOnChanges(): any { + this.#mfTable = this.inputMfTable() ?? this.#injectMfTable; + this.#onPageChangeSubscriber(this.#mfTable.getPage()); + this.#mfTable.onPageChange.subscribe(this.#onPageChangeSubscriber); + } - public setPage(pageNumber: number): void { - this.mfTable.setPage(pageNumber, this.rowsOnPage); - } + setPage(pageNumber: number): void { + this.#mfTable.setPage(pageNumber, this.rowsOnPage); + } - public setRowsOnPage(rowsOnPage: number): void { - this.mfTable.setPage(this.activePage, rowsOnPage); - } + setRowsOnPage(rowsOnPage: number): void { + this.#mfTable.setPage(this.activePage, rowsOnPage); + } - private onPageChangeSubscriber = (event: PageEvent) => { - this.activePage = event.activePage; - this.rowsOnPage = event.rowsOnPage; - this.dataLength = event.dataLength; - this.lastPage = Math.ceil(this.dataLength / this.rowsOnPage); - } + #onPageChangeSubscriber = (event: PageEvent) => { + this.activePage = event.activePage; + this.rowsOnPage = event.rowsOnPage; + this.dataLength = event.dataLength; + this.lastPage = Math.ceil(this.dataLength / this.rowsOnPage); + }; } From a6d2d5a37336d43c3386e45aadf06730a1fc5f2e Mon Sep 17 00:00:00 2001 From: tomaz Date: Sat, 21 Jun 2025 14:57:54 +0200 Subject: [PATCH 05/11] ng generate @angular/core:output-migration --- projects/ng-datatable/src/lib/DataTable.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/ng-datatable/src/lib/DataTable.ts b/projects/ng-datatable/src/lib/DataTable.ts index 4a220b9..32e3df2 100644 --- a/projects/ng-datatable/src/lib/DataTable.ts +++ b/projects/ng-datatable/src/lib/DataTable.ts @@ -9,6 +9,7 @@ import { inject, SimpleChanges, model, + output } from "@angular/core"; import { ReplaySubject } from "rxjs"; @@ -45,7 +46,7 @@ export class DataTable implements OnChanges, DoCheck { readonly sortBy = model>("", { alias: "mfSortBy" }); readonly sortOrder = model("asc", { alias: "mfSortOrder" }); @Output("mfSortByChange") sortByChange = new EventEmitter>(); - @Output("mfSortOrderChange") sortOrderChange = new EventEmitter(); + readonly sortOrderChange = output({ alias: 'mfSortOrderChange' }); readonly rowsOnPage = model(1000, { alias: "mfRowsOnPage" }); readonly activePage = model(1, { alias: "mfActivePage" }); From 97f20e93133b82facab20ef342c2d6a14b3338e4 Mon Sep 17 00:00:00 2001 From: tomaz Date: Sat, 21 Jun 2025 15:13:13 +0200 Subject: [PATCH 06/11] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20signals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/demo/src/app.component.html | 199 +++++++++--------- .../src/lib/BootstrapPaginator.ts | 158 +++++++------- projects/ng-datatable/src/lib/Paginator.ts | 32 ++- 3 files changed, 208 insertions(+), 181 deletions(-) diff --git a/projects/demo/src/app.component.html b/projects/demo/src/app.component.html index f44dbb2..b513869 100644 --- a/projects/demo/src/app.component.html +++ b/projects/demo/src/app.component.html @@ -3,106 +3,111 @@

Angular DataTable componentAngular DataTable component
- by - PascalHonegger -

- - -
-
- - -
-
- - -
-
- - -
-
-
-
User information
+ >
+ by + PascalHonegger + +
-
-
- -
- -
-
-
-
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
User information
- - - - - - - - - - - - @for (item of mf.data; track item) { - - - - - - - - } - - - - - - -
- Name - - Email - - Age - - City -
- - {{ item.name }}{{ item.email }}{{ item.age }}{{ item.city | uppercase }}
- -
+
+
+ +
+
+
+ + + + + + + + + + + + + @for (item of mf.data; track item) { + + + + + + + + } + + + + + + +
+ Name + + Email + + Age + + City +
+ + {{ item.name }}{{ item.email }}{{ item.age }}{{ item.city | uppercase }}
+ +
+
+
diff --git a/projects/ng-datatable/src/lib/BootstrapPaginator.ts b/projects/ng-datatable/src/lib/BootstrapPaginator.ts index 6fc82d0..a50ead0 100644 --- a/projects/ng-datatable/src/lib/BootstrapPaginator.ts +++ b/projects/ng-datatable/src/lib/BootstrapPaginator.ts @@ -1,83 +1,95 @@ -import {Component, computed, input} from "@angular/core"; -import {DataTable} from "./DataTable"; +import { Component, computed, input } from "@angular/core"; +import { DataTable } from "./DataTable"; import { Paginator } from "./Paginator"; @Component({ - selector: "mfBootstrapPaginator", - template: ` + selector: "mfBootstrapPaginator", + template: ` - @if (p.dataLength > p.rowsOnPage) { - - } - @if (p.dataLength > minRowsOnPage()) { -
    - @for (rows of rowsOnPageSet(); track rows) { -
  • - {{rows}} -
  • - } -
+ @if (p.dataLength() > p.rowsOnPage()) { + + } @if (p.dataLength() > minRowsOnPage()) { +
    + @for (rows of rowsOnPageSet(); track rows) { +
  • + {{ rows }} +
  • + } +
}
- `, - styles: [ - ".page-link { cursor: pointer; }" - ], - imports: [Paginator] + `, + styles: [".page-link { cursor: pointer; }"], + imports: [Paginator], }) export class BootstrapPaginator { - rowsOnPageSet = input([]); - mfTable = input(); + rowsOnPageSet = input([]); + mfTable = input(); - minRowsOnPage = computed(() => this.rowsOnPageSet().reduce((previous, current) => current < previous ? current : previous, 0)); + minRowsOnPage = computed(() => + this.rowsOnPageSet().reduce( + (previous, current) => (current < previous ? current : previous), + 0 + ) + ); } diff --git a/projects/ng-datatable/src/lib/Paginator.ts b/projects/ng-datatable/src/lib/Paginator.ts index ad60f21..95bb8f5 100644 --- a/projects/ng-datatable/src/lib/Paginator.ts +++ b/projects/ng-datatable/src/lib/Paginator.ts @@ -1,4 +1,11 @@ -import { Component, OnChanges, inject, input } from "@angular/core"; +import { + Component, + OnChanges, + computed, + inject, + input, + signal, +} from "@angular/core"; import { DataTable, PageEvent } from "./DataTable"; @Component({ @@ -12,10 +19,14 @@ export class Paginator implements OnChanges { #mfTable: DataTable; - activePage: number; - rowsOnPage: number; - dataLength = 0; - lastPage: number; + activePage = signal(0); + rowsOnPage = signal(0); + dataLength = signal(0); + lastPage = computed(() => + this.rowsOnPage() === 0 + ? 0 + : Math.ceil(this.dataLength() / this.rowsOnPage()) + ); ngOnChanges(): any { this.#mfTable = this.inputMfTable() ?? this.#injectMfTable; @@ -24,17 +35,16 @@ export class Paginator implements OnChanges { } setPage(pageNumber: number): void { - this.#mfTable.setPage(pageNumber, this.rowsOnPage); + this.#mfTable.setPage(pageNumber, this.rowsOnPage()); } setRowsOnPage(rowsOnPage: number): void { - this.#mfTable.setPage(this.activePage, rowsOnPage); + this.#mfTable.setPage(this.activePage(), rowsOnPage); } #onPageChangeSubscriber = (event: PageEvent) => { - this.activePage = event.activePage; - this.rowsOnPage = event.rowsOnPage; - this.dataLength = event.dataLength; - this.lastPage = Math.ceil(this.dataLength / this.rowsOnPage); + this.activePage.set(event.activePage); + this.rowsOnPage.set(event.rowsOnPage); + this.dataLength.set(event.dataLength); }; } From b2d8cb5f3ab57bb68ae7da434c2007fd8c4faa92 Mon Sep 17 00:00:00 2001 From: tomaz Date: Sat, 21 Jun 2025 15:39:16 +0200 Subject: [PATCH 07/11] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20zoneless?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- angular.json | 16 +++--------- projects/demo/src/app.component.html | 7 +++-- projects/demo/src/app.component.ts | 39 ++++++++++++---------------- projects/demo/src/main.ts | 19 ++++++++++---- 4 files changed, 38 insertions(+), 43 deletions(-) diff --git a/angular.json b/angular.json index ae5f1dc..675ec85 100644 --- a/angular.json +++ b/angular.json @@ -30,10 +30,7 @@ "test": { "builder": "@angular/build:karma", "options": { - "polyfills": [ - "zone.js", - "zone.js/testing" - ], + "polyfills": ["zone.js", "zone.js/testing"], "tsConfig": "projects/ng-datatable/tsconfig.spec.json" } }, @@ -85,17 +82,10 @@ "outputPath": "dist/demo", "index": "projects/demo/src/index.html", "browser": "projects/demo/src/main.ts", - "polyfills": [ - "zone.js" - ], "tsConfig": "projects/demo/tsconfig.app.json", - "assets": [ - "projects/demo/src/data.json" - ], + "assets": ["projects/demo/src/data.json"], "scripts": [], - "styles": [ - "node_modules/bootstrap/dist/css/bootstrap.min.css" - ] + "styles": ["node_modules/bootstrap/dist/css/bootstrap.min.css"] }, "configurations": { "production": { diff --git a/projects/demo/src/app.component.html b/projects/demo/src/app.component.html index b513869..4508f25 100644 --- a/projects/demo/src/app.component.html +++ b/projects/demo/src/app.component.html @@ -60,9 +60,11 @@

+ @if(data.value()) { + - @for (item of mf.data; track item) { + @for (item of mf.data; track $index) {
@@ -108,6 +110,7 @@

+ } diff --git a/projects/demo/src/app.component.ts b/projects/demo/src/app.component.ts index e54887b..b4a5b9d 100644 --- a/projects/demo/src/app.component.ts +++ b/projects/demo/src/app.component.ts @@ -1,5 +1,5 @@ -import { Component, OnInit, inject } from "@angular/core"; -import { HttpClient } from "@angular/common/http"; +import { Component } from "@angular/core"; +import { httpResource } from "@angular/common/http"; import { BootstrapPaginator, DataTable, @@ -23,35 +23,28 @@ import { UpperCasePipe } from "@angular/common"; BootstrapPaginator, ], }) -export class AppComponent implements OnInit { - private http = inject(HttpClient); +export class AppComponent { + filterQuery = ""; + rowsOnPage = 10; + sortBy: SortBy = "email"; + sortOrder: SortOrder = "asc"; - public data: any[]; - public filterQuery = ""; - public rowsOnPage = 10; - public sortBy: SortBy = "email"; - public sortOrder: SortOrder = "asc"; + data = httpResource(() => "/data.json", { + defaultValue: [], + }); - ngOnInit(): void { - this.http.get("/data.json").subscribe((data) => { - setTimeout(() => { - this.data = data; - }, 2000); - }); - } - - public toInt(num: string) { + toInt(num: string) { return +num; } - public sortByWordLength = (a: any) => { + sortByWordLength = (a: any) => { return a.city.length; }; - public remove(item: any) { - const index = this.data.indexOf(item); - if (index > -1) { - this.data.splice(index, 1); + remove(item: any) { + const index = this.data.value()?.indexOf(item); + if (index && index > -1) { + this.data.value()?.splice(index, 1); } } } diff --git a/projects/demo/src/main.ts b/projects/demo/src/main.ts index 567630e..9ebcf00 100644 --- a/projects/demo/src/main.ts +++ b/projects/demo/src/main.ts @@ -1,6 +1,15 @@ -import { bootstrapApplication } from '@angular/platform-browser'; -import { AppComponent } from './app.component'; -import { provideHttpClient } from '@angular/common/http'; +import { bootstrapApplication } from "@angular/platform-browser"; +import { AppComponent } from "./app.component"; +import { provideHttpClient } from "@angular/common/http"; +import { + provideBrowserGlobalErrorListeners, + provideZonelessChangeDetection, +} from "@angular/core"; -bootstrapApplication(AppComponent, { providers: [provideHttpClient()] }) - .catch(err => console.error(err)); +bootstrapApplication(AppComponent, { + providers: [ + provideHttpClient(), + provideZonelessChangeDetection(), + provideBrowserGlobalErrorListeners(), + ], +}).catch((err) => console.error(err)); From 584a946b36c78f794e9138da427987e93f3318e7 Mon Sep 17 00:00:00 2001 From: tomaz Date: Sat, 21 Jun 2025 15:44:38 +0200 Subject: [PATCH 08/11] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20tsconf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/ng-datatable/tsconfig.lib.json | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/projects/ng-datatable/tsconfig.lib.json b/projects/ng-datatable/tsconfig.lib.json index 60ee0a4..212d0f5 100644 --- a/projects/ng-datatable/tsconfig.lib.json +++ b/projects/ng-datatable/tsconfig.lib.json @@ -1,20 +1,29 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/lib", - "declarationMap": true, "declaration": true, + "declarationMap": true, "inlineSources": true, "types": [] }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, - "skipTemplateCodegen": true, - "strictMetadataEmit": true, - "enableResourceInlining": true + "strictStandalone": true, + "strictDomEventTypes": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "typeCheckHostBindings": true, + "strictTemplates": true, + "extendedDiagnostics": { + "checks": { + "invalidBananaInBox": "error", + "nullishCoalescingNotNullable": "error" + } + } }, - "exclude": [ - "src/test.ts", - "**/*.spec.ts" - ] + "include": ["src/**/*.ts"], + "exclude": ["**/*.spec.ts"] } From 378c587d4cc3e844a2343d97944dc144e330481a Mon Sep 17 00:00:00 2001 From: tomaz Date: Sat, 21 Jun 2025 15:48:47 +0200 Subject: [PATCH 09/11] models --- projects/demo/src/app.component.html | 4 ++-- projects/demo/src/app.component.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/projects/demo/src/app.component.html b/projects/demo/src/app.component.html index 4508f25..53b91e0 100644 --- a/projects/demo/src/app.component.html +++ b/projects/demo/src/app.component.html @@ -64,9 +64,9 @@

diff --git a/projects/demo/src/app.component.ts b/projects/demo/src/app.component.ts index b4a5b9d..f7e4131 100644 --- a/projects/demo/src/app.component.ts +++ b/projects/demo/src/app.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { Component, model } from "@angular/core"; import { httpResource } from "@angular/common/http"; import { BootstrapPaginator, @@ -24,10 +24,10 @@ import { UpperCasePipe } from "@angular/common"; ], }) export class AppComponent { - filterQuery = ""; - rowsOnPage = 10; - sortBy: SortBy = "email"; - sortOrder: SortOrder = "asc"; + filterQuery = model(""); + rowsOnPage = model(10); + sortBy = model("email"); + sortOrder = model("asc"); data = httpResource(() => "/data.json", { defaultValue: [], From dc1e62c9312255fb1ff2bc878ae6a846a32f3bb0 Mon Sep 17 00:00:00 2001 From: tomaz Date: Sat, 21 Jun 2025 15:49:26 +0200 Subject: [PATCH 10/11] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20change=20log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a689b6..53c602b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changes -- Breaking using Angular 20 standalone +- Breaking using Angular 20 standalone, signals, zoneless ```typescript imports: [ From ab9f26c161c4099021654b1468a70148ad4b381b Mon Sep 17 00:00:00 2001 From: tomaz Date: Sun, 22 Jun 2025 10:34:25 +0200 Subject: [PATCH 11/11] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20using=20out=20sig?= =?UTF-8?q?nal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/ng-datatable/src/lib/DataTable.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/projects/ng-datatable/src/lib/DataTable.ts b/projects/ng-datatable/src/lib/DataTable.ts index 32e3df2..48a896f 100644 --- a/projects/ng-datatable/src/lib/DataTable.ts +++ b/projects/ng-datatable/src/lib/DataTable.ts @@ -5,11 +5,10 @@ import { DoCheck, IterableDiffers, IterableDiffer, - Output, inject, SimpleChanges, model, - output + output, } from "@angular/core"; import { ReplaySubject } from "rxjs"; @@ -45,8 +44,8 @@ export class DataTable implements OnChanges, DoCheck { readonly sortBy = model>("", { alias: "mfSortBy" }); readonly sortOrder = model("asc", { alias: "mfSortOrder" }); - @Output("mfSortByChange") sortByChange = new EventEmitter>(); - readonly sortOrderChange = output({ alias: 'mfSortOrderChange' }); + readonly sortByChange = output>({ alias: "mfSortByChange" }); + readonly sortOrderChange = output({ alias: "mfSortOrderChange" }); readonly rowsOnPage = model(1000, { alias: "mfRowsOnPage" }); readonly activePage = model(1, { alias: "mfActivePage" });