analog icon indicating copy to clipboard operation
analog copied to clipboard

Use Resolvers Instead of Async Imports

Open jdgamble555 opened this issue 10 months ago • 2 comments

Which scope/s are relevant/related to the feature request?

create-analog

Information

Now that we know Zoneless is coming sooner than later as experimental, I think Analog needs to prepare for it. I actually wrote the waitFor function that was later implemented in Analog trying to avoid resolvers, when I didn't really understand that we definitely need to use them instead. Get rid of it. Get rid of all importing directly into the component, as this is not how Angular works, or should work.

It will break Analog when we go Zoneless!

I think this is the problem I keep going back to with Analog from different directions. The markdown components have a bug keeping SSR working correctly. Importing routes doesn't work as expected. We need transfer state for resolvers (provideClientHydration) so that we don't double fetch. This all seems to be problems related to not using resolvers. I have a few solutions.

import { TransferState, inject, makeStateKey } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { map } from "rxjs";

// put in resolver for async operations
export const useAsyncTransferState = async <T>(
    name: string,
    fn: () => T
) => {
    const state = inject(TransferState);
    const key = makeStateKey<T>(name);
    const cache = state.get(key, null);
    if (cache) {
        return cache;
    }
    const data = await fn() as T;
    state.set(key, data);
    return data;
};

// put in resolver for non-async operations
export const useTransferState = <T>(
    name: string,
    fn: () => T
) => {
    const state = inject(TransferState);
    const key = makeStateKey<T>(name);
    const cache = state.get(key, null);
    if (cache) {
        return cache;
    }
    const data = fn() as T;
    state.set(key, data);
    return data;
};

// idea accidently came from Brandon
export const injectResolver = <T>(name: string) =>
    inject(ActivatedRoute).data.pipe<T>(map(r => r[name]));

export const injectSnapResolver = <T>(name: string) =>
    inject(ActivatedRoute).snapshot.data[name] as T;

You can see an example of useAsyncTransferState() in my Analog Firebase Repo.

We can use resolvers easier thanks to Brandon's accidental idea trying to understand my though process. I use them, they work great!

Problems to Solve

  • The .md files should be loaded in the resolver on the server and transferred to the client. The JS bundle for importing them should not even be on the client.
  • The server-only routes should be automatically injected into the browser with TransferState inside the resolver.
  • After the regular gets fixed, there should be dependency tracking so that it only gets reran when necessary similar to SvelteKit.

I think my functions above could be added to the framework as utility functions anyone could use.

This has been the only thing I have seen that holds back Analog.

Thanks,

J

Describe any alternatives/workarounds you're currently using

Obviously if Angular adds a way to resolve things in the component with Zoneless, that way would be an option too.

I would be willing to submit a PR to fix this issue

  • [ ] Yes
  • [ ] No

jdgamble555 avatar Apr 13 '24 19:04 jdgamble555