angularangular-httpclientangular-signalsangular19

httpResource and Input-Signal: How does it work? (Angular 19)


I am creating a blog page, that fetches an article. I am using SSR/SSG.

I have problems using input signals and the new httpResource. Neither response1 nor its alternative response2 work.

export class ArticlePageComponent {
  slug = input.required<string>();

  response1 = httpResource<BlogPostWrapper>(
     () => 'https://foo/' + this.slug(),
   ); // NG0950: Input is required but no value is available yet

  response2 = computed(() =>
    httpResource<BlogPostWrapper>(
      () =>
        'https://foo/' + this.slug(),
    ),
  ); // httpResourceRef() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`.
}

Solution

  • Update:

    After setting up the scenario for SSR, I was able to replicate the error.

    The reason for the error is because you are showing the component through routing (E.g: /slug/:id) and we receive the slug as an input.required.

    When we render the component through routing there is no way to set the id field during normal routing configuration.

    To achieve this, we must have withComponentInputBinding so that the input.required can read the value through the routing params. So we can add this to the app.config.ts:

    app.config.ts
    import { provideRouter, withComponentInputBinding } from '@angular/router';
    
    export const appConfig: ApplicationConfig = {
      providers: [
        provideZoneChangeDetection({ eventCoalescing: true }),
        provideClientHydration(withEventReplay()),
        provideHttpClient(),
        provideRouter(routes, withComponentInputBinding()),
      ],
    };
    

    After this, we are guaranteed to access the input.required signal, so this error will go away. We can just use the original method of defining the http resource and it will work fine.

    @Component({ ... })
    export class SlugComponent {
      private injector = inject(Injector);
      slug = input.required<any>({
        alias: 'id',
      });
      response: HttpResourceRef<any> = httpResource<any>(
        () => 'https://jsonplaceholder.typicode.com/todos/' + this.slug(),
        {
          injector: this.injector,
        }
      );
    }
    

    Stackblitz Demo -> cd test -> npm i -> npm run start.


    Previous Answer:

    Define the httpResource on the ngOnInithook.

    You can also try defining this on the constructor where injection context is inherently available (might not work)

    Since in ngOnInit the injection context is not inherently available, we need to provide it explicitly through the second parameter of httpResource HttpResourceOptions.

    NG0950 - Angular Docs

    A required input was accessed but no value was bound.

    This can happen when a required input is accessed too early in your directive or component. This is commonly happening when the input is read as part of class construction.

    Inputs are guaranteed to be available in the ngOnInit lifecycle hook and afterwards.

    export class ArticlePageComponent {
      private injector = inject(Injector);
      slug = input.required<BlogPostWrapper>();
      response!: HttpResourceRef<any>;
      
      ngOnInit() {
        this.response = httpResource<BlogPostWrapper>(
         () => 'https://foo/' + this.slug(), 
         { injector: this.injector }
       ); 
      }