analog icon indicating copy to clipboard operation
analog copied to clipboard

load function not working

Open jdgamble555 opened this issue 1 year ago • 12 comments

Please provide the environment you discovered this bug in.

I'm following the directions: https://analogjs.org/docs/features/data-fetching/server-side-data-fetching

And I get an undefined error in both dev and prod.

Which area/package is the issue in?

vite-plugin-angular

Description

ERROR TypeError: Cannot read properties of undefined (reading 'loaded')

Please provide the exception or error you saw

main.ts:5 ERROR TypeError: Cannot read properties of undefined (reading 'loaded')
    at HomeComponent_Template (index.page.ts?v=138da47c:38:56)
    at executeTemplate (chunk-C4LID52L.js?v=138da47c:6347:5)
    at refreshView (chunk-C4LID52L.js?v=138da47c:7384:7)
    at detectChangesInView (chunk-C4LID52L.js?v=138da47c:7547:5)
    at detectChangesInViewIfAttached (chunk-C4LID52L.js?v=138da47c:7531:3)
    at detectChangesInComponent (chunk-C4LID52L.js?v=138da47c:7525:3)
    at detectChangesInChildComponents (chunk-C4LID52L.js?v=138da47c:7567:5)
    at refreshView (chunk-C4LID52L.js?v=138da47c:7440:7)
    at detectChangesInView (chunk-C4LID52L.js?v=138da47c:7547:5)
    at detectChangesInViewIfAttached (chunk-C4LID52L.js?v=138da47c:7531:3)

Other information

I want the variable to load on the server, not get hydrated and then updated either way. I would also think the load function would use a resolver?

Here is the affected pages:

https://github.com/jdgamble555/analog-test/tree/master/src/app/pages

Trying to figure out Analog for the first time :)

J

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

  • [ ] Yes
  • [ ] No

jdgamble555 avatar Feb 28 '24 03:02 jdgamble555

Not sure why the bare input isn't working yet, but using the injectLoad + async pipe works fine

https://github.com/brandonroberts/jdgamble555-analog-test/commit/7c8ffcbe778d7f415300ce614a75fe98ba36f347

https://jdgamble555-analog-test.vercel.app/

brandonroberts avatar Feb 28 '24 14:02 brandonroberts

Ok, that fixes it, but the data still jumps:

https://analog-test-mu.vercel.app/

https://github.com/jdgamble555/analog-test/blob/master/src/app/pages/index.page.ts

I'm trying to figure out where the state transfer takes place on this.

J

jdgamble555 avatar Feb 28 '24 18:02 jdgamble555

I see. It happens here https://github.com/analogjs/analog/blob/main/packages/router/src/lib/route-config.ts#L51-L56

HttpClient isn't used during prerendering though, as it's a direct API request.

brandonroberts avatar Feb 28 '24 19:02 brandonroberts

Is there some other way to detect its using edge rendering at build time than the globalThis in the Vercel environment?

brandonroberts avatar Feb 28 '24 19:02 brandonroberts

I think if you visit a non-prerendered page and use SSR it should work correctly. You can also skip prerendering of the / page by using an empty array in the prerender config

analog({
  prerender: {
    routes: []
  }
})

brandonroberts avatar Feb 28 '24 19:02 brandonroberts

https://jdgamble555-analog-test.vercel.app/about

brandonroberts avatar Feb 28 '24 19:02 brandonroberts

Ok got it!

https://analog-test-mu.vercel.app/

So it was prerendering thing. I would think that should be disabled by default as most front pages are going to be dynamic?

J

jdgamble555 avatar Feb 28 '24 20:02 jdgamble555

We discussed this previously and most sites building with Analog would prerender the root URL unless using something like tRPC

https://analogjs.org/docs/features/server/server-side-rendering#prerendering-routes

brandonroberts avatar Feb 28 '24 22:02 brandonroberts

@brandonroberts - I've been thinking more about this. I think the async part of the function should be done before the component even loads. You can do this with APP_INITIALIZER and abstractions.

I updating an old article on preloading data in angular universal to show how this is done now-a-days.

REPO: GitHub data.service.ts: GitHub Demo:: Vercel

Maybe the core code of Analog should work this way. This is how SvelteKit, Nuxt, Next, etc would do it. @brandonroberts what are your thoughts?

J

jdgamble555 avatar Mar 03 '24 01:03 jdgamble555

@brandonroberts - Just wanted to give you an example of the resolver version:

  1. Create a resolver for the path https://github.com/jdgamble555/angular-17-todo/blob/master/src/app/components/about/about.resolver.ts
  2. Add the resolver to the route https://github.com/jdgamble555/angular-17-todo/blob/master/src/app/app.routes.ts
  3. Show the data from the resolver https://github.com/jdgamble555/angular-17-todo/blob/master/src/app/components/about/about.component.ts

The component will not load until the data is resolved. If you use an observable (toSignal is best), and you have data that could change, you could easily convert it for child routes etc. and the data will get updated by the observable.

https://angular-17-todo.vercel.app/


So in Analog's case, you could:

  1. You could import the server route instead of the service, call it with fetch (or http), and it will get updated on each page load.
  2. You could add an option to cache the results or not.
  3. Of course you would also want to use CSRF - Use the Edge Middleware Package probably.
  4. And it will reload the resolver on every load, but cache the server data if you want it to.

All of this under-the-hood.

Some ideas... either way, I firmly believe the resolver is made for this.

J

jdgamble555 avatar Mar 13 '24 02:03 jdgamble555

@jdgamble555 we're missing the CSRF/cache part, but this is also what the injectLoad with .page.ts and .server.ts files do today.

1/2. "load" resolver for the route

  • https://github.com/analogjs/analog/blob/beta/packages/router/src/lib/route-config.ts#L32
  • This resolves the data before the route loads
  • The data is cached through the HttpClient request
  1. Show the data
  • https://github.com/analogjs/analog/blob/beta/packages/router/src/lib/inject-load.ts
  • This can be converted into a signal

You can also define a resolver in the RouteMeta for a page route.

// about.page.ts
export const routeMeta: RouteMeta = {
  resolve: {
    load: LoadResolver
  }
};

@Component({...})
export default class AboutComponent {
  data = toSignal(injectLoad(), { requireSync: true });
}

We could make this work for manually configured routes also. I agree a resolver works for both here though

brandonroberts avatar Mar 13 '24 15:03 brandonroberts

No I figured that's how it works, but you could probably simplify the code with a resolver.

Does your code refetch on the browser after the server loads? I forgot that the resolver is ran on both the server and browser so you would need to transfer the state to avoid refetching. That actually is a cache itself, so I was able to get rid of the random cache service.

https://github.com/jdgamble555/angular-17-todo/blob/master/src/app/components/about/about.resolver.ts


Side Note: It looks like this is how SvelteKit is getting the URL on the server so you could avoid the ENV variable. (I'm not understanding why you have it).

https://github.com/sveltejs/kit/blob/877f4bbd2e111602b222c10621c431b09accf7a7/packages/kit/src/utils/url.js#L144

J

jdgamble555 avatar Mar 13 '24 23:03 jdgamble555

Closing in favor of this:

https://github.com/analogjs/analog/issues/1033

jdgamble555 avatar Apr 13 '24 19:04 jdgamble555