Angular 6: 当我的服务中的值发生变化时,在我的组件中更新该值

8
我的目标原则上很简单,但我无法实现它:我希望当一个服务的变量被更改时,我的某个组件能够更新。 为了更好地解释我的问题,这里是一个例子: 在这里,我有一个服务,它会增加或减少一定数量的点数。当它接收到其中一个函数的调用时,会减少或增加这些点数。它还会说明这个变量是否是偶数或奇数。
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class TestService {
  Number: number = 0;
  IsOdd: boolean = false;

  constructor() {}

  IncreaseNumber() {
    this.Number = this.Number + 1;
    this.IsOdd = !this.IsOdd;
  }

  DecreaseNumber() {
    this.Number = this.Number - 1;
    this.IsOdd = !this.IsOdd;
  }
}

在这里,我有一个组件,需要知道我的数字是偶数还是奇数。
在初始化时,没有问题!它知道了!
但是,每次我的服务(test.service.ts)中的数字发生更改时,我该如何确保值对/导入在我的组件(test.component.ts)中也随之变化?
import { Component, OnInit } from '@angular/core';
import { TestService } from '../test.service'

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.scss']
})

export class TestComponent implements OnInit {

  IsOdd: boolean = false;

  constructor(MyService: TestService) {}

  ngOnInit() {
    this.IsOdd = MyService.IsOdd;
  }

}

我该怎么做?

我的组件是否需要以某种方式subscribe到我的服务? 或者我是否需要像使用ngOnInit这样的一个函数来进行更新操作?

提前致谢。


组件需要知道什么样的目的?您需要被通知执行一些额外的处理,还是只需要更新视图? - ConnorsFan
你是如何知道它不起作用的?这意味着你实施了什么来测试你的服务方法? - SeleM
1
你需要在你的服务中使用rxjs中的Subject,并将其作为Observable暴露在你的服务中。然后在你的组件中,你需要订阅你的服务的Observable。接着,在你的服务方法IncreaseNumber和其他方法中,你需要通过Subject发出一个新值,例如subject.next({val: this.Number, IsOdd : this.IsOdd })。当你的服务发生任何变化时,你的组件订阅将会得到更新。 - Niladri
首先,感谢您提供的主题,我会仔细查看。他需要进行额外的处理。再次说明,我尽可能地将这个例子简单化了。我的实际情况更加复杂和混乱。使用一个简单而琐碎的例子可以让人们更容易地回答,并且其他遇到同样问题的人也能更好地理解答案。我知道这个例子不起作用,因为没有任何东西使它更新。 - Scieur Arnaud
4个回答

9
如果这些服务变量是复杂类型,比如对象或数组,它们会自动更新,因为它们是引用类型。但由于您的服务变量是数字和布尔类型,它们不会自动更新,因为它们是原始类型,因此按值传递。
所以你需要使用BehaviorSubject并将它们公开为asObservable。您可以通过调用它们的next方法来更新这些BehaviorSubject的值。以下是示例:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class TestService {
  private myNumberValue = 0;
  private isOddValue = false;
  private myNumber: BehaviorSubject<number> = new BehaviorSubject<number>(this.myNumberValue);
  private isOdd: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

  myNumber$: Observable<number> = this.myNumber.asObservable();
  isOdd$:  Observable<boolean> = this.isOdd.asObservable();

  constructor() {}

  increaseNumber() {
    this.myNumberValue = this.myNumberValue + 1;
    this.myNumber.next(this.myNumberValue);
    this.isOddValue = !this.isOddValue;
    this.isOdd.next(this.isOddValue);
  }

  decreaseNumber() {
    this.myNumberValue = this.myNumberValue - 1;
    this.myNumber.next(this.myNumberValue);
    this.isOddValue = !this.isOddValue;
    this.isOdd.next(this.isOddValue);
  }
}

现在在你的组件中,你只需要subscribe服务公开的Observable值即可:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { TestService } from '../test.service'
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.scss']
})

export class TestComponent implements OnInit, OnDestroy {

  isOdd: boolean;
  subscription: Subscription;

  constructor(private testService: TestService) {}

  ngOnInit() {
    this.subscription = this.testService.isOdd$.subscribe(isOdd => this.isOdd = isOdd);
  }

  ngOnDestroy() {
    this.subscription && this.subscription.unsubscribe();
  }

}

现在,由于您已经在ngOnInit中订阅了isOdd$,该方法在组件初始化期间被调用,因此每当服务中的isOddValue发生更改时,组件中的isOdd都会更新。

此外,由于这是自定义的subscription,它应该分配给组件中的一个属性(subscription),它的类型应该是Subscription,这是我们从subscribe方法中获取的返回值。我们需要在ngOnDestroy中调用unsubscribe来避免内存泄漏。

PS: Angular类中的属性和方法名称应该采用小驼峰命名法,根据Angular样式指南

使用小驼峰命名法来命名属性和方法。


1
它也可以使用“Subject”来实现。在组件的ngOndestroy期间使用“unsubscribe”是一种避免内存泄漏的好方法。 - Niladri
谢谢,它帮了很大的忙。我认为这个例子非常简单,会对很多人有所帮助!^^ - Scieur Arnaud

1
为了使你所说的有效,你应该在TestComponent内部增加/减少服务中的number,而不是其他组件。
import { Component, OnInit } from '@angular/core';
import { TestService } from '../test.service'

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.scss']
})

export class TestComponent implements OnInit {

  IsOdd: boolean = false;

  constructor(MyService: TestService) {}

  ngOnInit() {
    this.IsOdd = MyService.IsOdd;
  }

  increase() {
    MyService.IncreaseNumber();
  }

  decrease() {
    MyService.DecreaseNumber();
  }

  getIsOdd() {
    return MyService.IsOdd;
  }

}

这只是一个例子,我试图找出一个简单的例子来解释我的问题。因为在我的工作中,这些事情会变得混乱和复杂。^^ - Scieur Arnaud

1

您可以使用多种解决方案,包括设置可观察对象以订阅更改。这些都是有效的解决方案。

最简单的解决方案是在模板中绑定服务而不是值:

<div> {{ MyService.IsOdd }} </div>

ngOnInit中,您正在将布尔值value分配给组件上的属性。该值永远不会更改。为了创建数据绑定并响应更改,您必须使用对象绑定属性的引用,该对象需要一个.。因此,在模板中MyService.IsOdd将起作用。

https://stackblitz.com/edit/angular-rktij7


1
谢谢,这是我最初使用的方法。 但我一直在想,“如果我需要在不直接在HTML中使用它的情况下查看它怎么办”。 所以我在寻找另一种方法^^ - Scieur Arnaud

0

Observable是一种很好的方法。

另一种简单的方法,我认为更简单的方法是在TestService中调用IncreaseNumberDecreaseNumber方法时发出事件。

然后,在TestComponent的构造函数中,您可以订阅您在TestService中创建的事件,然后更新您的IsOdd字段(或任何其他您希望在组件中更新的字段)。这种订阅是可能的,因为事件是observable的包装器。

像这样: 首先,在您的服务中创建一个事件发射器

    import { Injectable } from '@angular/core';

    @Injectable({
      providedIn: 'root'
    })
    export class TestService {
      Number: number = 0;
      IsOdd: boolean = false;
      EmitIsOdd = new EventEmitter<any>();

      constructor() {}

      IncreaseNumber() {
        this.Number = this.Number + 1;
        this.IsOdd = !this.IsOdd;
        this.EmitIsOdd.emit();
      }

      DecreaseNumber() {
        this.Number = this.Number - 1;
        this.IsOdd = !this.IsOdd;
        this.EmitIsOdd.emit();
      }
    }

在你的组件构造函数中订阅该事件

    import { Component, OnInit } from '@angular/core';
    import { TestService } from '../test.service'

    @Component({
      selector: 'app-test',
      templateUrl: './test.component.html',
      styleUrls: ['./test.component.scss']
    })

    export class TestComponent implements OnInit {

      IsOdd: boolean = false;

      constructor(MyService: TestService) {
        this.MyService.EmitIsOdd.subscribe(
          () => {
            this.IsOdd = this.MyService.IsOdd;
          }    
        );
      }

      ngOnInit() {
        this.IsOdd = MyService.IsOdd;
      }
    }

好的,简单而有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接