diff --git a/apps/docs/docs/template/virtual-view-directive.mdx b/apps/docs/docs/template/virtual-view-directive.mdx index 5543eeb29b..b224532f45 100644 --- a/apps/docs/docs/template/virtual-view-directive.mdx +++ b/apps/docs/docs/template/virtual-view-directive.mdx @@ -46,6 +46,22 @@ RxVirtualView is designed to work in combination with related directives: ### Show a widget when it's visible, otherwise show a placeholder +```typescript +import { RxVirtualView, RxVirtualViewContent, RxVirtualViewObserver, RxVirtualViewPlaceholder } from '@rx-angular/template/virtual-view'; +// Other imports... + +@Component({ + selector: 'my-list', + imports: [RxVirtualView, RxVirtualViewContent, RxVirtualViewObserver, RxVirtualViewPlaceholder], + templateUrl: './my-list.component.html', + styleUrls: ['./my-list.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyListComponent { + // Component code +} +``` + ```html
@@ -82,6 +98,22 @@ This will make sure you don't run into stuttery scrolling behavior and layout sh This example demonstrates how to use RxVirtualView to optimize lists by only rendering the visible list items. We are only rendering the `item` component when it's visible to the user. Otherwise, it gets replaced by an empty div. +```typescript +import { RxVirtualView, RxVirtualViewContent, RxVirtualViewObserver, RxVirtualViewPlaceholder } from '@rx-angular/template/virtual-view'; +// Other imports... + +@Component({ + selector: 'my-list', + imports: [RxVirtualView, RxVirtualViewContent, RxVirtualViewObserver, RxVirtualViewPlaceholder], + templateUrl: './my-list.component.html', + styleUrls: ['./my-list.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MyListComponent { + // Component code +} +``` + ```html
@for (item of items; track item.id) { diff --git a/libs/eslint-plugin/package.json b/libs/eslint-plugin/package.json index d5ee8ca9c5..574aba84b9 100644 --- a/libs/eslint-plugin/package.json +++ b/libs/eslint-plugin/package.json @@ -1,6 +1,15 @@ { "name": "@rx-angular/eslint-plugin", "version": "2.1.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://rx-angular.io/", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/rx-angular/rx-angular.git" + }, "peerDependencies": { "@typescript-eslint/parser": "^6.13.2 || ^7.0.0", "eslint": ">=8.0.0", diff --git a/libs/state/CHANGELOG.md b/libs/state/CHANGELOG.md index 7c8a214af7..7834bedf7f 100644 --- a/libs/state/CHANGELOG.md +++ b/libs/state/CHANGELOG.md @@ -2,6 +2,15 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +## [19.0.3](https://github.com/rx-angular/rx-angular/compare/state@19.0.2...state@19.0.3) (2025-01-28) + + +### Bug Fixes + +* properly include files in tsconfig ([7d26e82](https://github.com/rx-angular/rx-angular/commit/7d26e8200b0e11449e2f1273893c2644eee506da)) + + + ## [19.0.2](https://github.com/rx-angular/rx-angular/compare/state@19.0.1...state@19.0.2) (2024-12-28) diff --git a/libs/state/effects/src/lib/effects.service.ts b/libs/state/effects/src/lib/effects.service.ts index daf002bc43..04cc14d8c6 100644 --- a/libs/state/effects/src/lib/effects.service.ts +++ b/libs/state/effects/src/lib/effects.service.ts @@ -1,10 +1,5 @@ -import { - DestroyRef, - ErrorHandler, - inject, - Injectable, - Optional, -} from '@angular/core'; +import { DestroyRef, ErrorHandler, inject, Injectable } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { EMPTY, from, @@ -18,14 +13,14 @@ import { import { catchError, filter, - mapTo, + map, mergeAll, share, takeUntil, tap, } from 'rxjs/operators'; import { DestroyProp, OnDestroy$ } from './model'; -import { toHook, untilDestroyed } from './utils'; +import { toHook } from './utils'; /** * @deprecated - use rxEffects instead @@ -72,17 +67,9 @@ import { toHook, untilDestroyed } from './utils'; */ @Injectable() export class RxEffects implements OnDestroy$ { - constructor( - @Optional() - private readonly errorHandler: ErrorHandler | null, - ) { - inject(DestroyRef).onDestroy(() => { - this._hooks$.next({ destroy: true }); - this.subscription.unsubscribe(); - }); - } - private static nextId = 0; + private readonly destroyRef = inject(DestroyRef); + private readonly errorHandler = inject(ErrorHandler, { optional: true }); readonly _hooks$ = new Subject(); private readonly observables$ = new Subject>(); // we have to use publish here to make it hot (composition happens without subscriber) @@ -91,6 +78,13 @@ export class RxEffects implements OnDestroy$ { onDestroy$: Observable = this._hooks$.pipe(toHook('destroy')); private readonly destroyers: Record> = {}; + constructor() { + this.destroyRef.onDestroy(() => { + this._hooks$.next({ destroy: true }); + this.subscription.unsubscribe(); + }); + } + /** * Performs a side-effect whenever a source observable emits, and handles its subscription. * @@ -171,7 +165,10 @@ export class RxEffects implements OnDestroy$ { } const effectId = RxEffects.nextId++; const destroy$ = (this.destroyers[effectId] = new Subject()); - const applyBehavior = pipe(mapTo(effectId), takeUntil(destroy$)); + const applyBehavior = pipe( + map(() => effectId), + takeUntil(destroy$), + ); if (fnOrObj != null) { this.observables$.next( from(obsOrSub).pipe( @@ -233,7 +230,7 @@ export class RxEffects implements OnDestroy$ { untilEffect(effectId: number) { return (source: Observable) => source.pipe( - untilDestroyed(this), + takeUntilDestroyed(this.destroyRef), takeUntil(this.effects$.pipe(filter((eId) => eId === effectId))), ); } diff --git a/libs/state/effects/src/lib/utils.ts b/libs/state/effects/src/lib/utils.ts index 7735017ba8..70e87eef7a 100644 --- a/libs/state/effects/src/lib/utils.ts +++ b/libs/state/effects/src/lib/utils.ts @@ -1,9 +1,9 @@ -import { MonoTypeOperatorFunction, Observable } from 'rxjs'; -import { filter, map, shareReplay, take, takeUntil } from 'rxjs/operators'; -import { HookProps, OnDestroy$, SingleShotProps } from './model'; +import { Observable } from 'rxjs'; +import { filter, map, shareReplay, take } from 'rxjs/operators'; +import { HookProps, SingleShotProps } from './model'; export function isSingleShotHookNameGuard( - name: unknown + name: unknown, ): name is keyof SingleShotProps { return !!name && typeof name === 'string' && name !== ''; } @@ -16,7 +16,7 @@ const singleShotOperators = (o$: Observable): Observable => o$.pipe( filter((v) => v === true), take(1), - shareReplay() + shareReplay(), ); /** @@ -32,19 +32,6 @@ export function toHook(name: H) { return (o$: Observable): Observable => o$.pipe( map((p) => p[name]), - operators + operators, ); } - -/** - * This operator can be used to take instances that implements `OnDestroy$` and unsubscribes from the given Observable when the instances - * `onDestroy$` Observable emits. - * - * @param instanceWithLifecycle - */ -export function untilDestroyed( - instanceWithLifecycle: OnDestroy$ -): MonoTypeOperatorFunction { - return (source) => - source.pipe(takeUntil(instanceWithLifecycle.onDestroy$)); -} diff --git a/libs/state/package.json b/libs/state/package.json index 1d9a305703..61d8c013ae 100644 --- a/libs/state/package.json +++ b/libs/state/package.json @@ -1,6 +1,6 @@ { "name": "@rx-angular/state", - "version": "19.0.2", + "version": "19.0.3", "description": "@rx-angular/state is a light-weight, flexible, strongly typed and tested tool dedicated to reduce the complexity of managing component state and side effects in angular", "publishConfig": { "access": "public" @@ -43,7 +43,7 @@ }, "peerDependencies": { "@angular/core": "^19.0.0", - "@rx-angular/cdk": "^19.0.1", + "@rx-angular/cdk": "^19.1.0", "rxjs": "^6.5.3 || ^7.4.0" }, "dependencies": { diff --git a/libs/state/spec/rx-state.spec.ts b/libs/state/spec/rx-state.spec.ts index 98f9fbbf19..a4b27e4975 100644 --- a/libs/state/spec/rx-state.spec.ts +++ b/libs/state/spec/rx-state.spec.ts @@ -181,8 +181,6 @@ describe(rxState, () => { ); const state = component.state; - fixture.detectChanges(); - // TODO @edbzn: Remove detecting changes twice when we have a better solution fixture.detectChanges(); expect(state.get('count')).toBe(1337); @@ -208,8 +206,6 @@ describe(rxState, () => { ); const state = component.state; - fixture.detectChanges(); - // TODO @edbzn: Remove detecting changes twice when we have a better solution fixture.detectChanges(); expect(state.get('count')).toBe(4); @@ -233,8 +229,6 @@ describe(rxState, () => { ); const state = component.state; - fixture.detectChanges(); - // TODO @edbzn: Remove detecting changes twice when we have a better solution fixture.detectChanges(); expect(state.get('count')).toBe(1337); diff --git a/libs/template/CHANGELOG.md b/libs/template/CHANGELOG.md index c5aa765a1f..4b8ce91584 100644 --- a/libs/template/CHANGELOG.md +++ b/libs/template/CHANGELOG.md @@ -2,6 +2,15 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +## [19.2.1](https://github.com/rx-angular/rx-angular/compare/template@19.2.0...template@19.2.1) (2025-01-28) + + +### Bug Fixes + +* **template:** move import in virtual scrolling to rxjs/operators ([9d61704](https://github.com/rx-angular/rx-angular/commit/9d61704d95ee83304c31c7b0f34488c6aa479dc0)) + + + # [19.2.0](https://github.com/rx-angular/rx-angular/compare/template@19.1.2...template@19.2.0) (2025-01-09) diff --git a/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/autosize-virtual-scroll-strategy.ts b/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/autosize-virtual-scroll-strategy.ts index a405d0d67b..1a47e64d79 100644 --- a/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/autosize-virtual-scroll-strategy.ts +++ b/libs/template/experimental/virtual-scrolling/src/lib/scroll-strategies/autosize-virtual-scroll-strategy.ts @@ -15,7 +15,6 @@ import { MonoTypeOperatorFunction, Observable, of, - pairwise, ReplaySubject, Subject, } from 'rxjs'; @@ -27,6 +26,7 @@ import { groupBy, map, mergeMap, + pairwise, startWith, switchMap, take, diff --git a/libs/template/package.json b/libs/template/package.json index 692b657e09..11c7a693bc 100644 --- a/libs/template/package.json +++ b/libs/template/package.json @@ -1,6 +1,6 @@ { "name": "@rx-angular/template", - "version": "19.2.0", + "version": "19.2.1", "description": "**Fully** Reactive Component Template Rendering in Angular. @rx-angular/template aims to be a reflection of Angular's built in renderings just reactive.", "publishConfig": { "access": "public" @@ -44,7 +44,7 @@ }, "peerDependencies": { "@angular/core": "^19.0.0", - "@rx-angular/cdk": "^19.0.1", + "@rx-angular/cdk": "^19.1.0", "rxjs": "^6.5.3 || ^7.4.0" }, "dependencies": { diff --git a/package.json b/package.json index 6c9105536c..8b3ec41c98 100644 --- a/package.json +++ b/package.json @@ -68,16 +68,16 @@ "@angular-devkit/build-angular": "19.0.0", "@angular-devkit/core": "19.0.0", "@angular-devkit/schematics": "19.0.0", - "@angular-eslint/eslint-plugin": "18.4.1", - "@angular-eslint/eslint-plugin-template": "18.4.1", - "@angular-eslint/template-parser": "18.4.1", + "@angular-eslint/eslint-plugin": "19.0.2", + "@angular-eslint/eslint-plugin-template": "19.0.2", + "@angular-eslint/template-parser": "19.0.2", "@angular/build": "19.0.0", "@angular/cli": "~19.0.0", "@angular/compiler-cli": "19.0.0", "@angular/language-service": "19.0.0", "@commitlint/cli": "^19.2.1", "@commitlint/config-angular": "^19.1.0", - "@jscutlery/semver": "^4.1.0", + "@jscutlery/semver": "^5.5.1", "@nx-plus/docusaurus": "patch:@nx-plus/docusaurus@npm%3A14.1.0#~/.yarn/patches/@nx-plus-docusaurus-npm-14.1.0-b526e34c01.patch", "@nx/angular": "20.1.0", "@nx/cypress": "20.1.0", diff --git a/yarn.lock b/yarn.lock index f3d82cbf0d..d10ffc52fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -391,19 +391,19 @@ __metadata: languageName: node linkType: hard -"@angular-eslint/bundled-angular-compiler@npm:18.4.1": - version: 18.4.1 - resolution: "@angular-eslint/bundled-angular-compiler@npm:18.4.1" - checksum: 10c0/2dee97efb8e0c5c57e5bdad9438b6bec03cb660bbe08a745adbcffe7d08807db2cf0f95aed8f445b6922f8b50ef7b4b1326d81258d13f0481b12be7752de88a8 +"@angular-eslint/bundled-angular-compiler@npm:19.0.2": + version: 19.0.2 + resolution: "@angular-eslint/bundled-angular-compiler@npm:19.0.2" + checksum: 10c0/e42bbc4acd14884d6b530fe6b62be4909cd84035c3b955061cc228a67b7d4edb3380bbd4572ceda7612c4f40f6eebbbc6e76e88725f40027278afc5e2ca1165d languageName: node linkType: hard -"@angular-eslint/eslint-plugin-template@npm:18.4.1": - version: 18.4.1 - resolution: "@angular-eslint/eslint-plugin-template@npm:18.4.1" +"@angular-eslint/eslint-plugin-template@npm:19.0.2": + version: 19.0.2 + resolution: "@angular-eslint/eslint-plugin-template@npm:19.0.2" dependencies: - "@angular-eslint/bundled-angular-compiler": "npm:18.4.1" - "@angular-eslint/utils": "npm:18.4.1" + "@angular-eslint/bundled-angular-compiler": "npm:19.0.2" + "@angular-eslint/utils": "npm:19.0.2" aria-query: "npm:5.3.2" axobject-query: "npm:4.1.0" peerDependencies: @@ -411,47 +411,47 @@ __metadata: "@typescript-eslint/utils": ^7.11.0 || ^8.0.0 eslint: ^8.57.0 || ^9.0.0 typescript: "*" - checksum: 10c0/f34e2a4922bca70972a44a02387ab2abea2a5f48d7b9c0bfdd3e85662f59d6f12dbda903799cf0c4629db2117170fc64ff28433bf0be1e447058f7680246bec3 + checksum: 10c0/54d35c7f83db6ed40c9f51111dda0238ac3aed0c30d4de4f882f0499a16251565005bf15e365bf098fe8c61301dbcca13f3e8d51fbfbbf57d0eaa46b960a80b2 languageName: node linkType: hard -"@angular-eslint/eslint-plugin@npm:18.4.1": - version: 18.4.1 - resolution: "@angular-eslint/eslint-plugin@npm:18.4.1" +"@angular-eslint/eslint-plugin@npm:19.0.2": + version: 19.0.2 + resolution: "@angular-eslint/eslint-plugin@npm:19.0.2" dependencies: - "@angular-eslint/bundled-angular-compiler": "npm:18.4.1" - "@angular-eslint/utils": "npm:18.4.1" + "@angular-eslint/bundled-angular-compiler": "npm:19.0.2" + "@angular-eslint/utils": "npm:19.0.2" peerDependencies: "@typescript-eslint/utils": ^7.11.0 || ^8.0.0 eslint: ^8.57.0 || ^9.0.0 typescript: "*" - checksum: 10c0/c319ce97f90ef41f55c23460a853f44db389a7bf475a206f7f739549523ea08a6522f28d6cfde9fd5df1e4fa64aac4a19dde9533134352e840e7938b02d02bd6 + checksum: 10c0/77ad1662ad020a772faed7518b786c7dc020b812ffb8b13dc18fcf7ff018b7c4716de574cdf1ed7dc1df01bb343be7a85772273b6df03ccc92519b90ab2bde0b languageName: node linkType: hard -"@angular-eslint/template-parser@npm:18.4.1": - version: 18.4.1 - resolution: "@angular-eslint/template-parser@npm:18.4.1" +"@angular-eslint/template-parser@npm:19.0.2": + version: 19.0.2 + resolution: "@angular-eslint/template-parser@npm:19.0.2" dependencies: - "@angular-eslint/bundled-angular-compiler": "npm:18.4.1" + "@angular-eslint/bundled-angular-compiler": "npm:19.0.2" eslint-scope: "npm:^8.0.2" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: "*" - checksum: 10c0/a7ae4bc5b1bcfd2467a70578e3948471e8e8a09afe1ebf6c3b1dede5dd33d9edb57c81d00e40c01724ab7aba41d78338b86ab5db48dc812cbc9f7a2d9496782d + checksum: 10c0/96d5b786af03f729910571f4917dcb74d352ff225bf5ec13113834f29198904f7f89d2e49cc337c1753fe0b1575c5936c3c0e541cf09d18d0ed4eb15e7db8b01 languageName: node linkType: hard -"@angular-eslint/utils@npm:18.4.1": - version: 18.4.1 - resolution: "@angular-eslint/utils@npm:18.4.1" +"@angular-eslint/utils@npm:19.0.2": + version: 19.0.2 + resolution: "@angular-eslint/utils@npm:19.0.2" dependencies: - "@angular-eslint/bundled-angular-compiler": "npm:18.4.1" + "@angular-eslint/bundled-angular-compiler": "npm:19.0.2" peerDependencies: "@typescript-eslint/utils": ^7.11.0 || ^8.0.0 eslint: ^8.57.0 || ^9.0.0 typescript: "*" - checksum: 10c0/47cc7b7554764673179cee73fd2c01576ed9cf328a2fa0c8e29669b55b91ea73ce789b72472c628b05bb9ae6e6d53cd083ae8997e6aa495705713ab34f14df8e + checksum: 10c0/e1ad0104259bdead95a9923aee831aa2b0d66d8cb4924fb5f8c19f9660ffa79241d93f6bcc4ab75b39682a7895fcd46f172d751230503f0887ea0071f62967fa languageName: node linkType: hard @@ -7378,9 +7378,9 @@ __metadata: languageName: node linkType: hard -"@jscutlery/semver@npm:^4.1.0": - version: 4.1.0 - resolution: "@jscutlery/semver@npm:4.1.0" +"@jscutlery/semver@npm:^5.5.1": + version: 5.5.1 + resolution: "@jscutlery/semver@npm:5.5.1" dependencies: chalk: "npm:4.1.2" conventional-changelog: "npm:^5.1.0" @@ -7400,8 +7400,8 @@ __metadata: inquirer: "npm:8.2.6" rxjs: "npm:7.8.1" peerDependencies: - "@nx/devkit": ^17.0.0 - checksum: 10c0/7e3ce1e307c6f68ed93b2feaa45fcb9ff812f3316cbb19e450f7cbc9d5468e1f71d3f12d75ef42c03b05b72a87194a260248938955f1eae5be58cbd7f4f12190 + "@nx/devkit": ^18.0.0 || ^19.0.0 || ^20.0.0 + checksum: 10c0/a515d11f713471c805b730cfcd0b39739ab51cc5b26dce6538667b56e65dfdf9d47dfcf0b987c4f3eb7c18ba7b71e200df9bcdef48dfb8fc6cf3c772ef840f1c languageName: node linkType: hard @@ -9455,7 +9455,7 @@ __metadata: tslib: "npm:^2.4.1" peerDependencies: "@angular/core": ^19.0.0 - "@rx-angular/cdk": ^19.0.1 + "@rx-angular/cdk": ^19.1.0 rxjs: ^6.5.3 || ^7.4.0 languageName: unknown linkType: soft @@ -9468,7 +9468,7 @@ __metadata: tslib: "npm:^2.4.1" peerDependencies: "@angular/core": ^19.0.0 - "@rx-angular/cdk": ^19.0.1 + "@rx-angular/cdk": ^19.1.0 rxjs: ^6.5.3 || ^7.4.0 languageName: unknown linkType: soft @@ -27440,9 +27440,9 @@ __metadata: "@angular-devkit/build-angular": "npm:19.0.0" "@angular-devkit/core": "npm:19.0.0" "@angular-devkit/schematics": "npm:19.0.0" - "@angular-eslint/eslint-plugin": "npm:18.4.1" - "@angular-eslint/eslint-plugin-template": "npm:18.4.1" - "@angular-eslint/template-parser": "npm:18.4.1" + "@angular-eslint/eslint-plugin": "npm:19.0.2" + "@angular-eslint/eslint-plugin-template": "npm:19.0.2" + "@angular-eslint/template-parser": "npm:19.0.2" "@angular/animations": "npm:19.0.0" "@angular/build": "npm:19.0.0" "@angular/cdk": "npm:19.0.0" @@ -27462,7 +27462,7 @@ __metadata: "@angular/ssr": "npm:19.0.0" "@commitlint/cli": "npm:^19.2.1" "@commitlint/config-angular": "npm:^19.1.0" - "@jscutlery/semver": "npm:^4.1.0" + "@jscutlery/semver": "npm:^5.5.1" "@nx-plus/docusaurus": "patch:@nx-plus/docusaurus@npm%3A14.1.0#~/.yarn/patches/@nx-plus-docusaurus-npm-14.1.0-b526e34c01.patch" "@nx/angular": "npm:20.1.0" "@nx/cypress": "npm:20.1.0"