admin管理员组

文章数量:1277900

My base requirement is to start and stop the Resource API at will. By start/stop I mean to start/stop listening for signal changes (signals provided within the request callback).

By start I mean, the resource API is supposed to act normally, eagerly load the request based on the input signal and react to signal changes.

By stop I mean, do not have any value and remain idle, even when there are new signal changes from the request signals.

Below is my minimal reproducible code along with a working stackblitz for debugging:

HTML:

<div>
  <div>
    Resource Request triggers:
  </div>
  <div>
    <input [(ngModel)]="id" type="number"/>
  </div>
</div>
<div>
  @if(![rs.Loading, rs.Reloading].includes(rxResource.status())) {
    {{rxResource.value() | json}}
  } @else{
    Loading...
  }
</div>
<hr/>
<div>
  @if(![rs.Loading, rs.Reloading].includes(resource.status())) {
    {{resource.value() | json}}
  } @else{
    Loading...
  }
</div>
<hr/>
<div>
<button (click)="start()">Start Listening</button>
<button (click)="stop()">Stop Listening</button>
</div>

TS:

  export class App {
    id = signal(1);
    rs = ResourceStatus;
    http = inject(HttpClient);
    resourceRequest = (): ResourceRequest => {
      return {
        id: this.id(),
      };
    };
    resource: ResourceRef<any> = resource<ResourceRequest, any>({
      request: this.resourceRequest,
      loader: ({ request: { id }, abortSignal }) => {
        return fetch(`/${id}`, {
          signal: abortSignal,
        }).then((res: any) => res.json());
      },
    });
    rxResource: ResourceRef<any> = rxResource<ResourceRequest, any>({
      request: this.resourceRequest,
      loader: ({ request: { id } }) => {
        return this.http.get<any>(
          `/${id}`
        );
      },
    });

    start() {
      // how to start the resource API programmatically.
    }

    stop() {
      // how to stop the resource API programmatically.
    }

Stackblitz Demo

My base requirement is to start and stop the Resource API at will. By start/stop I mean to start/stop listening for signal changes (signals provided within the request callback).

By start I mean, the resource API is supposed to act normally, eagerly load the request based on the input signal and react to signal changes.

By stop I mean, do not have any value and remain idle, even when there are new signal changes from the request signals.

Below is my minimal reproducible code along with a working stackblitz for debugging:

HTML:

<div>
  <div>
    Resource Request triggers:
  </div>
  <div>
    <input [(ngModel)]="id" type="number"/>
  </div>
</div>
<div>
  @if(![rs.Loading, rs.Reloading].includes(rxResource.status())) {
    {{rxResource.value() | json}}
  } @else{
    Loading...
  }
</div>
<hr/>
<div>
  @if(![rs.Loading, rs.Reloading].includes(resource.status())) {
    {{resource.value() | json}}
  } @else{
    Loading...
  }
</div>
<hr/>
<div>
<button (click)="start()">Start Listening</button>
<button (click)="stop()">Stop Listening</button>
</div>

TS:

  export class App {
    id = signal(1);
    rs = ResourceStatus;
    http = inject(HttpClient);
    resourceRequest = (): ResourceRequest => {
      return {
        id: this.id(),
      };
    };
    resource: ResourceRef<any> = resource<ResourceRequest, any>({
      request: this.resourceRequest,
      loader: ({ request: { id }, abortSignal }) => {
        return fetch(`https://jsonplaceholder.typicode/todos/${id}`, {
          signal: abortSignal,
        }).then((res: any) => res.json());
      },
    });
    rxResource: ResourceRef<any> = rxResource<ResourceRequest, any>({
      request: this.resourceRequest,
      loader: ({ request: { id } }) => {
        return this.http.get<any>(
          `https://jsonplaceholder.typicode/todos/${id}`
        );
      },
    });

    start() {
      // how to start the resource API programmatically.
    }

    stop() {
      // how to stop the resource API programmatically.
    }

Stackblitz Demo

Share Improve this question asked Feb 23 at 20:52 Naren MuraliNaren Murali 58.2k5 gold badges44 silver badges76 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1

It's not about stop/starting a resource, it's about having a having a state that represents the parameters of a loader that should pull the async data.

If resource were meant to be enabled/disabled they would have dedicated methods for that.

Either you have all your request parameters and you can return them.

request: () => {
  if(myState().param1 && myState().param2) {
     return { param1: myState().param1, param2: myState.param2 }
  }
  return undefined // I don't have all my params 
}

or you don't and you return undefined, telling the resource that you don't have the necessary data to perform the async request.

The Solution:

When the request callback returns undefined, the resource API goes into Idle state, discarding any values already present (if any) and remain empty.

So we can use this principle to create a start/stop logic, which is controlled by a control signal resourceControl:

resourceControl = signal(true);

We use this resourceControl to return either undefined (do not listen) or { id: this.id(), } (If you want to listen to this.id()):

resourceRequest = (): ResourceRequest | undefined => {
  if (this.resourceControl()) {
    return {
      id: this.id(),
    };
  } else {
    return undefined;
  }
};

The start/stop functions will simply toggle this control signal resourceControl:

start() {
  this.resourceControl.set(true);
}

stop() {
  this.resourceControl.set(false);
}

Now we use this callback and set it to the request property for both resource and rxResource.

resource: ResourceRef<any> = resource<ResourceRequest | undefined, any>({
  request: this.resourceRequest,
  loader: ({ request: { id }, abortSignal }) => {
    return fetch(`https://jsonplaceholder.typicode/todos/${id}`, {
      signal: abortSignal,
    }).then((res: any) => res.json());
  },
});
rxResource: ResourceRef<any> = rxResource<ResourceRequest | undefined, any>({
  request: this.resourceRequest,
  loader: ({ request: { id } }) => {
    return this.http.get<any>(
      `https://jsonplaceholder.typicode/todos/${id}`
    );
  },
});

Full Code:

import {
  Component,
  inject,
  ResourceRef,
  ResourceStatus,
  signal,
  resource,
} from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { rxResource } from '@angular/core/rxjs-interop';
import { HttpClient, provideHttpClient } from '@angular/common/http';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

export interface ResourceRequest {
  id: number;
}

@Component({
  selector: 'app-root',
  imports: [CommonModule, FormsModule],
  template: `
  <div>
    <div>
      Resource Request triggers:
    </div>
    <div>
      <input [(ngModel)]="id" type="number"/>
    </div>
  </div>
    <div>
      @if(![rs.Loading, rs.Reloading].includes(rxResource.status())) {
        {{rxResource.value() | json}}
      } @else{
        Loading...
      }
    </div>
    <hr/>
    <div>
      @if(![rs.Loading, rs.Reloading].includes(resource.status())) {
        {{resource.value() | json}}
      } @else{
        Loading...
      }
    </div>
    <hr/>
    <div>
    <div>Current Status: {{rs[resource.status()]}}</div>
    <button (click)="start()">Start Listening</button>
    <button (click)="stop()">Stop Listening</button>
    </div>
  `,
})
export class App {
  id = signal(1);
  rs = ResourceStatus;
  http = inject(HttpClient);
  resourceControl = signal(true);
  resourceRequest = (): ResourceRequest | undefined => {
    if (this.resourceControl()) {
      return {
        id: this.id(),
      };
    } else {
      return undefined;
    }
  };
  resource: ResourceRef<any> = resource<ResourceRequest | undefined, any>({
    request: this.resourceRequest,
    loader: ({ request: { id }, abortSignal }) => {
      return fetch(`https://jsonplaceholder.typicode/todos/${id}`, {
        signal: abortSignal,
      }).then((res: any) => res.json());
    },
  });
  rxResource: ResourceRef<any> = rxResource<ResourceRequest | undefined, any>({
    request: this.resourceRequest,
    loader: ({ request: { id } }) => {
      return this.http.get<any>(
        `https://jsonplaceholder.typicode/todos/${id}`
      );
    },
  });

  start() {
    this.resourceControl.set(true);
  }

  stop() {
    this.resourceControl.set(false);
  }
}

bootstrapApplication(App, {
  providers: [provideHttpClient()],
});

Stackblitz Demo

本文标签: