最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Angular signals react to local state changes, but not to changes from input signal - Stack Overflow

programmeradmin4浏览0评论

I have this scenario, where the parent component will fetch user details and show then on the UI, code below:

@Component({
  selector: 'app-root',
  imports: [Child, FormsModule],
  template: `
    <div> User Details</div>
    @if(resource.value(); as user) {
      <div> Title: {{user.title}}</div>
      <app-like-button [likes]="user.id"/>
    }
    <button (click)="refresh()">Reload User Data</button>
  `,
})
export class App {
  http = inject(HttpClient);
  id = signal(1);
  resource: ResourceRef<UserDetails> = rxResource({
    request: () => this.id(),
    loader: ({ request: id }: any) =>
      this.http.get<UserDetails>(
        `/${id}`
      ),
  });

  refresh() {
    this.resource.reload();
  }
}

Similarly I have a likes component which is responsible for showing the count and the like/dislike button.

@Component({
  selector: 'app-like-button',
  imports: [FormsModule],
  template: `
  <div> Child </div>
    <div>likes: {{likes()}}</div>
    <button (click)="like()">Like</button>
    <button (click)="unLike()">Dislike</button>
  `,
})
export class Child {

  // local state for this component
  likes: ModelSignal<number> = model.required<number>();

  constructor() {
    effect(() => {
      const likesCount = this.likes();
      // trigger an API call to save the like button press:
      console.log(likesCount);
    });
  }

  like() {
    this.likes.update((likesPrev: number) => likesPrev + 1);
  }

  unLike() {
    this.likes.update((likesPrev: number) => likesPrev - 1);
  }
}

In the below code, I have an effect which reacts to changes of the likes signal, by react I mean the changes are saved to the database using a put API call.

The problem is that, I am using an effect which reacts to likes signal changes. So during initialization the PUT API call is called, which is not necessary.

I would like the API to react to only local state updates of likes signal.

Stackblitz Demo

Expected result, is that API call ( represented by console.log) should not fire on initial load.

I have this scenario, where the parent component will fetch user details and show then on the UI, code below:

@Component({
  selector: 'app-root',
  imports: [Child, FormsModule],
  template: `
    <div> User Details</div>
    @if(resource.value(); as user) {
      <div> Title: {{user.title}}</div>
      <app-like-button [likes]="user.id"/>
    }
    <button (click)="refresh()">Reload User Data</button>
  `,
})
export class App {
  http = inject(HttpClient);
  id = signal(1);
  resource: ResourceRef<UserDetails> = rxResource({
    request: () => this.id(),
    loader: ({ request: id }: any) =>
      this.http.get<UserDetails>(
        `https://jsonplaceholder.typicode/todos/${id}`
      ),
  });

  refresh() {
    this.resource.reload();
  }
}

Similarly I have a likes component which is responsible for showing the count and the like/dislike button.

@Component({
  selector: 'app-like-button',
  imports: [FormsModule],
  template: `
  <div> Child </div>
    <div>likes: {{likes()}}</div>
    <button (click)="like()">Like</button>
    <button (click)="unLike()">Dislike</button>
  `,
})
export class Child {

  // local state for this component
  likes: ModelSignal<number> = model.required<number>();

  constructor() {
    effect(() => {
      const likesCount = this.likes();
      // trigger an API call to save the like button press:
      console.log(likesCount);
    });
  }

  like() {
    this.likes.update((likesPrev: number) => likesPrev + 1);
  }

  unLike() {
    this.likes.update((likesPrev: number) => likesPrev - 1);
  }
}

In the below code, I have an effect which reacts to changes of the likes signal, by react I mean the changes are saved to the database using a put API call.

The problem is that, I am using an effect which reacts to likes signal changes. So during initialization the PUT API call is called, which is not necessary.

I would like the API to react to only local state updates of likes signal.

Stackblitz Demo

Expected result, is that API call ( represented by console.log) should not fire on initial load.

Share Improve this question asked 11 hours ago Naren MuraliNaren Murali 57.3k5 gold badges41 silver badges71 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

You can use a model to accept the likes from the parent component. Why I am doing this is because the model has a subscribe property (No need to unsubscribe automatically unsubscribed), which fires only when the local state is updated through set or update models.

@Component({
  selector: 'app-like-button',
  imports: [FormsModule],
  template: `
  <div> Child </div>
    <div>likes: {{likes()}}</div>
    <button (click)="like()">Like</button>
    <button (click)="unLike()">Dislike</button>
  `,
})
export class Child {
  // local state for this component
  likes: ModelSignal<number> = model.required<number>();

  constructor() {
    // subscription is auto unsubscribed on component destroy:
    this.likes.subscribe((value: number) => {
      // trigger an API call to save the like button press:
      console.log(value);
    });
  }

Full Code:

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

export interface UserDetails {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

@Component({
  selector: 'app-like-button',
  imports: [FormsModule],
  template: `
  <div> Child </div>
    <div>likes: {{likes()}}</div>
    <button (click)="like()">Like</button>
    <button (click)="unLike()">Dislike</button>
  `,
})
export class Child {
  // local state for this component
  likes: ModelSignal<number> = model.required<number>();

  constructor() {
    // subscription is auto unsubscribed on component destroy:
    this.likes.subscribe((value: number) => {
      // trigger an API call to save the like button press:
      console.log(value);
    });
  }

  like() {
    this.likes.update((likesPrev: number) => likesPrev + 1);
  }

  unLike() {
    this.likes.update((likesPrev: number) => likesPrev - 1);
  }
}

@Component({
  selector: 'app-root',
  imports: [Child, FormsModule],
  template: `
    <div> User Details</div>
    @if(resource.value(); as user) {
      <div> Title: {{user.title}}</div>
      <app-like-button [likes]="user.id"/>
    }
    <button (click)="refresh()">Reload User Data</button>
  `,
})
export class App {
  http = inject(HttpClient);
  id = signal(1);
  resource: ResourceRef<UserDetails> = rxResource({
    request: () => this.id(),
    loader: ({ request: id }: any) =>
      this.http.get<UserDetails>(
        `https://jsonplaceholder.typicode/todos/${id}`
      ),
  });

  refresh() {
    this.resource.reload();
  }
}

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

Stackblitz Demo

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论