Skip to content

feat(resource): make defaultValue accept a callback function instead of a static value #63241

@eneajaho

Description

@eneajaho

Which @angular/* package(s) are relevant/related to the feature request?

core

Description

If we want to use cached data on resources, we have to use startWith in our observables, but that's not great, because we loose the functionality of isLoading, as that will be immediately true.

Currently defaultValue would be a great place for that, but defaultValue doesn't accept dynamic values (callback fn that can return a different value everytime it's called), and this makes it painful to have resources that use cache data.

class UsersListStore {
	private readonly currentCachedItems = computed(() => {
		return this.usersGlobalStore.getEntitiesByFilters(this.currentFiltersPayload());
	});

	// WITHOUT REACTIVE DEFAULT VALUE, needs rxjs operators
	readonly usersRes = rxResource({
		params: () => this.currentFiltersPayload(),
		stream: ({ params: payload }) => {
			return this.usersApiService.getUsers(payload).pipe(
				tap(result => {
					this.usersGlobalStore.storeResultsAndFilters(result.items, payload);
				}),
				startWith({ items: this.currentCachedItems(), total: this.currentCachedItems().length }),
				filter(x => x.total > 0)
			);
		},
	});
}

Proposed solution

class UsersListStore {
	private readonly currentCachedItems = computed(() => {
		return this.usersGlobalStore.getEntitiesByFilters(this.currentFiltersPayload());
	});

	// WITH REACTIVE DEFAULT VALUE, no need for rxjs operators
	readonly usersRes = rxResource({
		defaultValue: () => ({
			items: this.currentCachedItems(),
			total: this.currentCachedItems().length,
		}),
		params: () => this.currentFiltersPayload(),
		stream: ({ params: payload }) => {
			return this.usersApiService.getUsers(payload).pipe(
				tap(result => {
					this.usersGlobalStore.storeResultsAndFilters(result.items, payload);
				})
			);
		},
	});
}

Alternatives considered

We can use a getter but that's not great 👀

class UsersListStore {
	private readonly currentCachedItems = computed(() => {
		return this.usersGlobalStore.getEntitiesByFilters(this.currentFiltersPayload());
	});

        get resourceDefaultValue() {
           return {
			items: this.currentCachedItems(),
			total: this.currentCachedItems().length,
		};
        }

	// WITH REACTIVE DEFAULT VALUE, no need for rxjs operators
	readonly usersRes = rxResource({
		defaultValue: this.resourceDefaultValue,
		params: () => this.currentFiltersPayload(),
		stream: ({ params: payload }) => {
			return this.usersApiService.getUsers(payload).pipe(
				tap(result => {
					this.usersGlobalStore.storeResultsAndFilters(result.items, payload);
				})
			);
		},
	});
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: coreIssues related to the framework runtimecross-cutting: resourceIssues related to the newly introduced resource / httpResource

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions