我的应用程序中有两个组件-“过滤器”和“家”。这两个组件是应用程序组件的子组件。
我有一个我要通过服务从过滤器组件传递到家庭组件的以下对象。而且,由于我希望每当过滤器组件发生更改时就在home组件中更新这些数据,因此我在服务中使用BehaviorSubject。
型号:filter-response.model.ts
export interface FilterResponse{
filter:string;
filterlist:string[];
}
filter.component.ts
export class FilterComponent implements OnInit {
filterMainList:FilterResponse[]=[
{
"filter": "abc",
"filterlist": ["a","b","c"]
},
{
"filter": "def",
"filterlist":["d","e","f"]
},
{
"filter": "ghi",
"filterlist": ["g","h","i"]
}
];
constructor(private filterService:FilterService,) {
this.filterService.setFilterConditions(this.filterMainList);
}
ngOnInit() {
}
}
在家庭组件中,我收到了数据,我想对其进行一些更改。
home.component.ts
export class HomeComponent implements OnInit {
constructor(private filterService:FilterService) {
this.filterService.getFilterConditions().subscribe((data)=>{
let tc:FilterResponse[]=data
for(let f in tc){
tc[f].filterlist=['changes'];//this is where I make the changes
}
});
}
ngOnInit() {
}
}
我有一个包含行为主题的过滤器服务。这就是我感到困惑的地方。我将数据记录在服务中next()之前的服务中,而我实际获得的数据是我在home组件中更改的,这意味着我对home组件中的数据实例所做的更改也影响(更改)了服务中的数据。
filter.service.ts
@Injectable({
providedIn:'root'
})
export class FilterService{
private filterConditions = new BehaviorSubject<FilterResponse[]>(null);
constructor(){}
setFilterConditions(filterList:FilterResponse[]){
console.log(filterList);//data changes incorrectly
this.filterConditions.next(filterList);
}
getFilterConditions():Observable<FilterResponse[]>{
return this.filterConditions.asObservable();
}
}
我的控制台日志:
实际上应该是我刚刚在过滤器组件中传递的数据。但它会显示我在家庭组件中修改的数据。
如果有帮助的话,最后是app.component.ts
app.component.ts
<app-filter></app-filter>
<app-home></app-home>
我想提醒您的另一件事是。我试图在stackblitz中重新创建方案。但是问题在于它在这里工作正常。
https://stackblitz.com/edit/angular-ivy-9th5pj
请解释一下我在项目中缺少的内容。还是这是服务将按角度工作的实际方式。
答案 0 :(得分:2)
在您的服务文件中进行此更改
setFilterConditions(filterList:FilterResponse[]){
this.filterConditions.next(JSON.parse(JSON.stringify(filterList)));
}
这将克隆您的阵列。还有很多其他方法可以在JS中克隆对象/数组 Different ways to Clone object in JS
原因:
在TS中,将对象作为函数参数传递时,您正在传递对该对象的引用。因此,在修改传递给函数的对象时,您就是在修改原始对象。
类似地,通过引用接收在 HomeComponent 的订阅函数中接收的数组/对象。因此, HomeComponent 中的值更改也会在服务中受到影响。
答案 1 :(得分:2)
我误解了您的问题:我想您的问题是,为什么console.log
输出在修改后 之前显示修改后的数组。
就像khush's answer中解释的那样,非原始类型通过引用传递:JS不会将数组的副本传递给函数/方法,而是传递数组本身。因此,这就是filterMainList
传递的数组实例(next
),并且在您的HomeComponent
中对其进行了修改。
现在,在修改数据之前您有console.log(filterList)
,但是日志输出仍然显示更改。这是因为当您在浏览器的控制台中单击Array(3)
时,调试器此时将评估变量的值,该值此时已被修改。
这在您的stackblitz示例中不可见,因为您没有记录数组,而是JSON.stringify(arr)
。在这种情况下,您无需在控制台中扩展该值,该值已经显示为字符串,并且不会重新评估。
如果我们仅记录变量,则stackblitz demo将重现相同的“问题”
如果这种行为不是所希望的,则可以克隆按照khush的建议传递的数据;可以从发送它的组件中获得,也可以直接在服务中调用next
之前,如果代码中多次调用setFilterConditions
答案 2 :(得分:0)
我只想扩展库什的回答。因此,如果创建深层副本无济于事,则可以从loadash中使用cloneDeep函数。在最坏的情况下(修复程序问题),请使用此选项。 由于浅拷贝而发生此问题。 因此,在每种情况下返回数组时,都不会返回新数组,而是返回数组的地址。因此,阵列将发生任何变化,原始阵列也会发生变化。而且,当您使用JSON.stringigy或deepClone函数时,会在内部创建具有不同地址的数组副本
setFilterConditions(filterList:FilterResponse[]){
this.filterConditions.next(cloneDeep(filterList));
}