服务方法如何在 ngOnInit() 内部工作,甚至 ngOnInit() 挂钩执行一次?

How the service method is working inside the ngOnInit() even ngOnInit() hook executed once?

这是我的 Angular 服务中的代码....

export class DataService{

     products = {
            'productsList': [
                 {id: 101, name: 'Apple', qty: 2},
                 {id: 102, name: 'Mango', qty: 1},
                 {id: 103, name: 'Grapes', qty: 1},
                 {id: 104, name: 'Banana', qty: 3}
             ]
      };

     constructor() {}

     getProducts(){
          return this.products.productsList;
     }

     addProduct(obj: {id:number, name:string, qty:number}){
         this.products.productsList.push(obj);
     }
 }

而这个来自 angular 组件...

export class AppComponent implements OnInit{
   pId!:number;
   pName!:string;
   pQty!:number;
   pObject!:{id:number, name:string, qty:number};
   pObjectArray:{id:number, name:string, qty:number}[] = [];

   constructor(private dataService: DataService) {};

   ngOnInit(){
       this.pObjectArray = this.dataService.getProducts();
   }

   addProduct(){
       this.pObject = {
       id: this.pId,
       name: this.pName,
       qty: this.pQty
       }
       this.dataService.addProduct(this.pObject);
  }
}

所以我只想知道我们什么时候加载这个组件,然后 ngOnIniti() 会调用一次,它会从服务中调用 getProducts() 方法 class 然后我们会从通过此方法服务并将其分配给组件内的 pObjectAray 属性。这很好。但是现在,如果我通过调用组件的 addProduct() 方法添加任何新产品,然后此方法将调用服务的 addProduct() ,因此它将更新服务的产品对象,所以现在产品对象有 5 个新产品而不是 4 个因为我们刚刚添加了新的。所以我的问题是 ngOnInit 中的 getProducts() 如何自动执行并更新组件的 pObjectArray 属性?

我的意思是 ngOnIniti() 只在我们加载组件时调用一次,所以这次 ngOnInit() 不会执行,我已经通过在 ngOnInit() 中使用 console.log() 打印一些消息来测试它. 所以请解释一下数据是如何随着每次更改在组件中自动更新的。例如,如果我们添加新数据或删除或修改数据,那么它会自动使用更新后的数据更新 pObjectArray。

抱歉我的英语不好...

您可以使用可观察对象。因此,当您在服务中更新 pObjectArray 时,您在组件中订阅它并获得新值。

一种方法是在您的服务中定义一个主题:

 private pObjectArraySubject = new BehaviorSubject<{id:number, name:string, qty:number}[]>([]);

 public pObjectArray$ = this.pObjectArraySubject .asObservable();

然后在 addProduct 方法中更新您的主题:

addProduct(obj: {id:number, name:string, qty:number}){
    let ps = this.pObjectArraySubject.getValue();
    ps.push(obj);
    this.pObjectArraySubject.next(ps);
 }

在您的组件中订阅它 ngOnInit 生命周期:

this.dataService.pObjectArray$.subscribe((obj) => {
      this.pObjectArray = obj;
 });

所以每次值更新时,您的 this.pObjectArray 也会更新。 您也可以在模板中使用 asyc 管道:

this.pObjectArray = this.dataService.pObjectArray$;
<a-component [data]="pObjectArray  | async"></a-component>

记住: 当您订阅一个可观察对象时,不要忘记取消订阅。 一种方法是在 noOnDestroy lifeCycle 中取消订阅。

那是因为从服务文件中返回数组By reference。因此,无论您对服务文件中的数组进行何种更改,都会反映在获取该数组的相应组件中。

并且由于 angular 的 default change detection 策略,您将在模板或使用该数组的任何地方看到更新。

如果您发送数组的副本,那么您将看不到您所面临的行为。

getProducts() {
  // return this.products.productsList.slice();
  or
  // return [ ...this.products.productsList ];
}

检查下面关于它如何改变数组的片段

var a = [1, 2, 3, 4, 5]

function changeArray(arr) {
  arr.splice(2, 1);
}

changeArray(a);
console.log(a);

检查此 stackblitz 以了解返回数组副本如何不更新组件 属性

尝试更改您的 addProducts,如下所示:

addProduct(obj: {id:number, name:string, qty:number}){
this.products.productsList = [...this.products.productsList.push(obj)];
}

Aakash,正如你所说,ngOnInit 上的指令不更新列表,只执行一次。您需要在添加新产品时手动刷新列表。您可以重复调用 this.dataService.getProducts

  addProduct() {
      ...
      this.dataService.addProduct(this.pObject);
      this.pObjectArray = this.dataService.getProducts();
  }

或者直接添加到你的数组中

  addProduct() {
      ...
      this.dataService.addProduct(this.pObject);
      this.pObjectArray.push(this.pObject);
  }

好吧,您没有可观察对象 - 通常您需要 return 可观察对象并订阅。您可以使用 rxjs 运算符 of 进行模拟,因此我们将您的服务更改为 return observables - 通常您使用 httpClient

调用 API
import {of,Observable} from 'rxjs'

export class DataService {

     products = {...}

     constructor() {}

     getProducts():Observable<any[]> {
          //possible you'll change by a return this.httpClient.get('....')
          return of(this.products.productsList);
     }

     addProduct(obj: {id:number, name:string, qty:number}):Observable<any>{
         //we are going to return an object {success:true} or {success:false}
         //if we add successful the element
         bool ok=true;
         this.products.productsList.push(obj);
         if (ok)
           return of({success:true})
         
         return of({success:false})
     }
 }

当我们收到 observables 时,我们有两个选择,使用 pipe async 或订阅。看到不一样了。

使用管道异步

pObjectArray$:Observable<any[]>
ngOnInit(){
       this.pObjectArray$ = this.dataService.getProducts();
   }

还有你的.html

<div *ngFor="let item of pObjectArray$ |async as >
     {{item.name}}{{item.qty}}
</div>

通常我们调用的变量有带“$”后缀的observable,但它是可选的

订阅并获取变量中订阅的值

pObjectArray:any[]=[] //see that it's an array and we can initialize with an empty array
ngOnInit() {
       this.dataService.getProducts().subscribe(res=>{
            this.pObjectArray=res.productList
       })
   }

还有你的.html

<div *ngFor="let item of pObjectArray>
     {{item.name}}{{item.qty}}
</div>

好吧,这不会解决这个问题,当您添加一个元素时,列表会更新,我们需要再次更改我们的函数 addProduct

addProduct() {
   ...
   this.dataService.addProduct(this.pObject).subscribe(res => {
       if (res.success)
       {
          //if you're using async pipe again repeat 
          this.pObjectArray$ = this.dataService.getProducts();
          //or if you're are subscribing you can
              //subscribing again (*)
              this.dataService.getProducts().subscribe(res=>{
                 this.pObjectArray=res.productList
              })
              //or adding manually the element to the array
              this.pObjectArray.push(this.pObject);
       }
   })
  }

好吧,我们选择函数 addProduct return 成功为真或假的对象,但我们可以选择函数 addProduct return 插入的产品 - 例如值为“id” " 自动递增,或者 return 整个列表。

好吧,到目前为止,我们一直在使用带有 httpClient 的可观察对象或使用 'of'。我确定您是在询问在 ngOnInit 中创建一个刷新列表的订阅。您可以订阅一些可观察到的唯一时间并收到更改。在自己的 Angular 中,您有一个 FormControl,您可以订阅 valueChanges 或 ActiveRouted 并订阅 paramMap。

我们正在更改您的一些服务

import {of,Observable,Subject} from 'rxjs'

export class DataService{
  productSubject:Subject<any[]>=new Subject<any[]>();
  products = {...}

  init() {
    this.productSubject.next(this.products.productsList); 
  }

  addProduct(obj: { id: number; name: string; qty: number }) {
      obj.id=this.products.productsList.length+1;
      obj.name=obj.name+ " "+obj.id
      this.products.productsList.push(obj);
      this.productSubject.next(this.products.productsList);
  }

看到函数 addProduct 或 init 没有 return 任何东西,它们只生成一个 this.productObservables.next

我们可以在ngOnInit中订阅

   ngOnInit(){
       this.pObjectArray$ = this.dataService.productSubject;
   }
   //or 
   ngOnInit(){
       this.dataService.productSubject.subscribe(res=>{
         this.pObjectArray=res;
       })
   }

并随时初始化服务,例如在 ngAfterViewInit 中(包含在 setTimeout 中)

ngAfterViewInit() {
    setTimeout(() => {
      this.dataService.init();
    });
  }

你看,当你添加一个项目时,列表是如何刷新的,但记住原因是因为我们的服务使“下一个”

this stackblitz我写了最后一个方法

(*)在实际应用中,我们使用 switchMap rxjs 运算符来更改订阅的值,但这只是为了得到一个大概的想法