微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

服务方法如何在 ngOnInit() 甚至 ngOnInit() 钩子中工作一次?

如何解决服务方法如何在 ngOnInit() 甚至 ngOnInit() 钩子中工作一次?

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

export class DataService{

     products = {
            'productsList': [
                 {id: 101,name: 'Apple',qty: 2},{id: 102,name: 'Mango',qty: 1},{id: 103,name: 'Grapes',{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 component...

export class AppComponent implements OnInit{
   pId!:number;
   pName!:string;
   pQty!:number;
   pObject!:{id:number,qty:number};
   pObjectArray:{id:number,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() 方法,然后我们将通过这个方法从服务中获取数据并将将其分配给组件内的 pObjectaray 属性。这很好。但是现在,如果我通过调用组件的 addProduct() 方法添加任何新产品,然后此方法调用服务的 addProduct(),因此它将更新服务的产品对象,因此现在产品对象有 5 个新产品而不是 4 个因为我们刚刚添加了新的。所以我的问题是 ngOnInit 中的 getProducts() 如何自动执行并更新组件的 pObjectArray 属性

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

抱歉我的英语不好...

解决方法

那是因为从服务文件中返回了数组 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 以了解如何返回数组副本未更新组件属性

,

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

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

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

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

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

addProduct(obj: {id:number,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>

记住: 当你订阅一个 observable 时,不要忘记取消订阅它。 一种方法是在 noOnDestroy 生命周期内取消订阅。

,

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

addProduct(obj: {id:number,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);
  }

好吧,你没有 observables - 通常你想要返回 observables 并订阅。您可以使用 rxjs 运算符 of 进行模拟,因此我们正在更改您的服务以返回可观察对象 - 通常您使用 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,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>

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

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

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 返回一个对象,成功为 true 或 false,但我们可以选择函数 addProduct 返回插入的产品 - 例如,值“id”自动递增,或者返回整个列表。

好吧,直到现在我们都在使用带有 httpClient 的 observables 或使用 'of'。我确定你问的是在 ngOnInit 中创建一个刷新列表的订阅。一些 observables 您可以订阅一个独特的时间并收到更改。在自己的 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 不返回任何东西,它们只生成一个 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操作符来改变订阅的值,但这只是为了得到一个大概的想法

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。