我正在使用服务在多个组件之间进行数据共享。 在这里,在服务中,我有一个吸气剂,正在初始化基础数据,并有一个主题,通知其他组件任何更改。
@Injectable({
providedIn: "root"
})
export class ExpenseHandlerService {
expenseDataChanged = new Subject<ExpenseDetails>();
private expenseData: Array<ExpenseDetails> = [];
constructor(private http: HttpClient) {}
/**
* Fetched expense data and stored in the this.expenseData
*/
getExpenseData() {
console.log("Starting...");
this.http
.get("assets/model/data.json")
.pipe(map(result => result))
.subscribe(data => {
this.expenseData = data as Array<ExpenseDetails>;
console.log(this.expenseData);
});
return this.expenseData.slice();
}
/**
* Notifies other components of any change in the data
*
* @param data
*/
addExpenseData(data: ExpenseDetails) {
//console.log(this.expenseData.length);
//this.expenseData.push(data);
this.expenseDataChanged.next(data);
//console.log(this.expenseData);
}
}
我已经声明了一个使用getExpenseData方法初始化现有数据的组件。
该组件的init方法如下:
ngOnInit() {
this.expenseData = this.expenseHandlerService.getExpenseData();
this.expenseHandlerService.expenseDataChanged.subscribe(res => {
this.receiveExpenseData(res);
});
}
但是在执行该应用程序时,我只会得到我定义的空数组,并且在执行ngOnInit之后,服务中的订阅者将完成并记录实际数据。
由于我试图将服务作为几个组件之间的中央通信链接,因此我希望在服务本身中具有api调用。
答案 0 :(得分:1)
有多种方法可以解决此问题(即解析器/防护),但我将根据您当前的代码提出解决方案。您应该在ExpenseHandlerService.getExpenseData
的构造函数中调用ExpenseHandlerService
。另外,将您的expenseDataChanged
类型更改为BehaviorSubject
。话虽如此,让我们像这样更改ExpenseHandlerService
[请参见代码注释以获取说明]:
@Injectable(
{
providedIn: 'root'
}
)
export class ExpenseHandlerService {
//I think BehaviorSubject type should be Array<ExpenseDetails> instead of ExpenseDetails
//As your question was not clear so I am assuming as Aray<ExpenseDetails>; If you want type as ExpenseDetails
//then you will have to adjust the code accordinlgy
//LETS KEEP THIS VARIABLE AS PRIVATE SO THAT NOONE FROM OUTSIDE CAN EMIT NEW VALUE
//AND EXPOSE AN OBSERVABLE as public property [see below]
private expenseDataChanged = new BehaviorSubject<Array<ExpenseDetails>>([]);
//This should be subscribed by the consumers i.e. Components
get expenseDataObs() {
return this.expenseDataChanged.asObservable();
}
//NO NEED TO MAINTAIN THIS ARRAY AS BehaviorSubject.value will give us the last emitted array
//private expenseData: Array<ExpenseDetails> = [];
constructor( private http: HttpClient) {
this.getExpenseData();
}
/**
* Fetched expense data and stored in the this.expenseData
*/
getExpenseData() {
console.log('Starting...');
this.http.get("assets/model/data.json")
.pipe(map(result => result)).subscribe(data => {
//I AM NOT SURE IF YOU API CAN RETURN NULL OR NOT
//STILL IF API RETURNS NULL THEN EMIT EMPTY ARRAY
//ADJUST THIS CODE AS PER YOUR APP LOGIC
if(!data) {
data = [];
}
const arr = data as Array<ExpenseDetails>;
//I AM NOT USING SLICE (with no argument will return same array) AS IT WAS NOT DOING ANYTHING;
//IF YOU WANT TO USE THEN USE AS PER YOUR LOGIC
this.expenseDataChanged.next(arr);
});
}
/**
* Notifies other components of any change in the data
*
* @param data
*/
addExpenseData(data: ExpenseDetails) {
//Get the last emited array from behaviorsubject
const lastEmittedArr = this.expenseDataChanged.value;
//push the data in to newArray
//NOTICE I AM CREATING NEW ARRAY - This is to make sure that array is rendered; again it is NOT mandatory
//but it depends on kind of changedetection stretegy is used.
const newArrayToEmit = [...lastEmittedArr, data];
this.expenseDataChanged.next(newArrayToEmit);
}
}
现在在您的组件中,像这样订阅expenseDataObs
:
ngOnInit() {
this.expenseHandlerService.expenseDataObs
.pipe(
//As soon as you subscribe the initial value could be an null because the Behavior subject was initialized with null
//THIS IS BECAUSE OF THE NATURE OF BehaviorSubject; It may happen because by the time you subscribe
//response of HTTP API might not have recieved; but as soon as it is received
//Using the filter will ensure to avoid null in callback
filter(arr => !!arr)
)
.subscribe((res) => {
//Process the array recieved;
this.receiveExpenseData(res);
});
}
注意:在以上方法中,可能存在这样一种可能性,即在初始化时间组件之前,将不会呈现任何数据,直到HTTP响应返回为止。因此用户可能会看到空白页。为避免出现这种情况,您可以显示一个加载程序,也可以使用resolver / guard加载数据。
[我个人使用某种存储区,即NGRX / NGXS / AKITA进行状态管理,而不是管理服务中的状态。
希望有帮助。