-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(computed): add computed/extendedComputed with access to previous…
… computed value
- Loading branch information
Showing
7 changed files
with
160 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# ngxtension/computed | ||
|
||
Secondary entry point of `ngxtension`. It can be used by importing from `ngxtension/computed`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"lib": { | ||
"entryFile": "src/index.ts" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "ngxtension/computed", | ||
"$schema": "../../../node_modules/nx/schemas/project-schema.json", | ||
"projectType": "library", | ||
"sourceRoot": "libs/ngxtension/computed/src", | ||
"targets": { | ||
"test": { | ||
"executor": "@nx/jest:jest", | ||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"], | ||
"options": { | ||
"jestConfig": "libs/ngxtension/jest.config.ts", | ||
"testPathPattern": ["computed"], | ||
"passWithNoTests": true | ||
}, | ||
"configurations": { | ||
"ci": { | ||
"ci": true, | ||
"codeCoverage": true | ||
} | ||
} | ||
}, | ||
"lint": { | ||
"executor": "@nx/eslint:lint", | ||
"outputs": ["{options.outputFile}"], | ||
"options": { | ||
"lintFilePatterns": [ | ||
"libs/ngxtension/computed/**/*.ts", | ||
"libs/ngxtension/computed/**/*.html" | ||
] | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { Component, signal } from '@angular/core'; | ||
import { TestBed } from '@angular/core/testing'; | ||
import { computed } from './computed'; | ||
|
||
describe(computed.name, () => { | ||
@Component({ | ||
standalone: true, | ||
template: '', | ||
}) | ||
class Test { | ||
count = signal(0); | ||
multiplier = signal(1); | ||
|
||
result = computed<number>((currentValue) => { | ||
if (this.multiplier() % 2 === 0) { | ||
return this.count() * this.multiplier(); | ||
} | ||
return currentValue; | ||
}); | ||
|
||
track = 0; | ||
trackedResult = computed<number>((currentValue) => { | ||
this.track += 1; | ||
if (this.multiplier() % 2 === 0) { | ||
return this.count() * this.multiplier(); | ||
} | ||
return currentValue; | ||
}); | ||
} | ||
|
||
function setup() { | ||
const fixture = TestBed.createComponent(Test); | ||
fixture.detectChanges(); | ||
return fixture.componentInstance; | ||
} | ||
|
||
it('should work properly', () => { | ||
const component = setup(); | ||
|
||
expect(component.result()).toEqual(undefined); | ||
|
||
component.count.set(1); | ||
expect(component.result()).toEqual(undefined); | ||
|
||
component.multiplier.set(2); | ||
expect(component.result()).toEqual(2); | ||
|
||
component.multiplier.set(3); | ||
expect(component.result()).toEqual(2); | ||
}); | ||
|
||
it('should track dep properly', () => { | ||
const component = setup(); | ||
|
||
// kick off computed | ||
expect(component.trackedResult()).toEqual(undefined); | ||
// track increments | ||
expect(component.track).toEqual(1); | ||
|
||
// set count only while multipler is still odd | ||
component.count.set(1); | ||
// result is still undefined | ||
expect(component.trackedResult()).toEqual(undefined); | ||
// computed is not reinvoked | ||
expect(component.track).toEqual(1); | ||
|
||
// set count only again | ||
component.count.set(2); | ||
// result is still undefined | ||
expect(component.trackedResult()).toEqual(undefined); | ||
// computed is not reinvoked | ||
expect(component.track).toEqual(1); | ||
|
||
// set multiplier to an even number | ||
component.multiplier.set(2); | ||
// result is now 4 (multipler * count) | ||
expect(component.trackedResult()).toEqual(4); | ||
// computed is invoked because multiplier changes | ||
expect(component.track).toEqual(2); | ||
|
||
// set multiplier back to an odd number | ||
component.multiplier.set(3); | ||
// result is 4 because it returns the currentValue | ||
expect(component.trackedResult()).toEqual(4); | ||
// computed is reinvoked because multiplier changes | ||
expect(component.track).toEqual(3); | ||
|
||
// set count while multiplier is an odd number | ||
component.count.set(3); | ||
// result is still 4 because it returns the currentValue | ||
expect(component.trackedResult()).toEqual(4); | ||
// computed is not reinvoked | ||
expect(component.track).toEqual(3); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import type { CreateComputedOptions } from '@angular/core'; | ||
import { computed as ngComputed, signal, untracked } from '@angular/core'; | ||
|
||
export function computed<TValue>( | ||
computedCallback: (currentValue: TValue) => TValue, | ||
options?: CreateComputedOptions<TValue> | ||
) { | ||
if (!options) { | ||
options = { equal: Object.is }; | ||
} | ||
|
||
const currentValue = signal<TValue>(undefined!, { equal: Object.is }); | ||
return ngComputed(() => { | ||
const computedValue = computedCallback(untracked(currentValue)); | ||
untracked(() => { | ||
currentValue.set(computedValue); | ||
}); | ||
return computedValue; | ||
}, options); | ||
} | ||
|
||
export const extendedComputed = computed; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './computed'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters