服务方法如何在 ngOnInit() 内部工作,甚至 ngOnInit() 钩子执行一次?
Posted
技术标签:
【中文标题】服务方法如何在 ngOnInit() 内部工作,甚至 ngOnInit() 钩子执行一次?【英文标题】:How the service method is working inside the ngOnInit() even ngOnInit() hook executed once? 【发布时间】:2021-03-31 01:11:32 【问题描述】:这是我的 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);
这个来自角度组件...
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() 方法,然后我们会通过这个方法从服务中获取数据,然后将其分配给组件内的 pObjectAray 属性。这很好。但是现在如果我通过调用组件的 addProduct() 方法添加任何新产品,然后此方法将调用服务的 addProduct() ,因此它将更新服务的产品对象,因此现在产品对象有 5 个新产品而不是 4 个因为我们刚刚添加了新的。所以我的问题是 ngOnInit 中的 getProducts() 如何自动执行并更新组件的 pObjectArray 属性?
我的意思是 ngOnIniti() 仅在我们加载组件时调用一次,因此 ngOnInit() 这次不会执行,我已经通过在 ngOnInit() 中使用 console.log() 打印一些消息来测试它。 所以请解释一下数据是如何随着每次更改在组件中自动更新的。例如,如果我们添加新数据或删除或修改数据,那么它会自动使用更新后的数据更新 pObjectArray。
对不起,我的英语不好......
【问题讨论】:
【参考方案1】:这是因为从服务文件返回数组By reference
。因此,无论您对服务文件中的数组所做的任何更改,都会反映在获取该数组的相应组件中。
并且由于 Angular 的 default change detection
策略,您将在模板或您使用该数组的任何地方看到更新。
如果您发送数组的副本,那么您将看不到您所面临的行为。
getProducts()
// return this.products.productsList.slice();
or
// return [ ...this.products.productsList ];
查看下面的 sn-p 以了解它是如何改变数组的
var a = [1, 2, 3, 4, 5]
function changeArray(arr)
arr.splice(2, 1);
changeArray(a);
console.log(a);
查看此stackblitz,了解返回数组副本未更新组件属性的原因
【讨论】:
是的,你是对的,我想现在我得到了我的问题的答案,因为数组总是按引用而不是按值传递,所以我们在 pObjectArray 中获取数组的引用或地址,所以如果我们做任何更改在服务数组内部,那么所有的都将在组件 pObjectArray 中反映出来,因为 pObjectArray 和 products.productList 都指向同一个数组。【参考方案2】:你可以使用 observables。因此,当您在服务中更新 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>
记住:
当你订阅一个 observable 时,不要忘记取消订阅它。
一种方法是在noOnDestroy
lifeCycle 中取消订阅。
【讨论】:
【参考方案3】:尝试更改您的 addProducts,如下所示:
addProduct(obj: id:number, name:string, qty:number)
this.products.productsList = [...this.products.productsList.push(obj)];
【讨论】:
【参考方案4】: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
进行模拟,因此我们正在更改您的服务以返回 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.nameitem.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.nameitem.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 返回一个成功为真或假的对象,但我们可以选择函数 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操作符来改变订阅的值,但这只是一个大概的思路
【讨论】:
是的,我明白了。但我不必在 addProduct() 中再次调用 getProducts() 方法来手动刷新列表,因为我的列表已经在我的原始代码中自动刷新。 我在原始代码中得到了问题的答案,我返回 products.productList 的引用或地址,然后将该引用分配给 pObjectArray 变量,因此我的两个属性 pObjectArray 和 products.productList 都指向到同一个数组,这就是为什么即使我的 getProducts 方法执行了一次,但我的 pObjectArray 中的数据仍然会自动更新,即使我没有在 addProduct 方法之后调用 getProducts 方法,但我的 pObjectArray 由于引用而自动更新。 Askash,如果你正在使用对象,那么你在哪里更改对象并不重要:在服务中,在组件中......当你想使用 observables 时,我的回答是,抱歉造成混淆 谢谢Eliseo,我明白了。再次感谢您的帮助。 :)以上是关于服务方法如何在 ngOnInit() 内部工作,甚至 ngOnInit() 钩子执行一次?的主要内容,如果未能解决你的问题,请参考以下文章
带有软删除的 Spring Boot GET 方法如何在服务 impl 中添加另一个异常