服务方法如何在 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 运算符来更改订阅的值,但这只是为了得到一个大概的想法
这是我的 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
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 运算符来更改订阅的值,但这只是为了得到一个大概的想法