'Data in Angular services persists across projects
I am an aspiring FE developer with a lot to learn. Previously, a bug emerged in our system where data from one project would appear in another. Imagine looking at data for Animal A, a deer, then navigating to Animal B, a dog, and seeing information about antler size from Animal A in the UI on Animal B's screen.
The pattern of this type of bug, and the subsequent fix, is that the Angular component is not managing the data in a service it uses properly and must reset it.
For example:
@Component
export class AnimalComponent implements OnInit, OnDestroy {
traitSummary: string;
constructor(animalTraitService: AnimalTraitService) {}
getAnimalTraitData(animalId: Number) {
this.animalTraitService.getData(animalId).pipe(
tap((traits) => {
this.animalTraitService.formattedData = formatDataForAnimalType(traits);
this.traitSummary = extractTraitSummary(traits);
}),
catchError(error => {
// this is the call we added to fix "antlers" from appearing on dog
// this.animalTraitService.formattedData = null;
return of(null);
})
).subscribe();
}
formatDataForAnimalType(traits: AnimalTraits): AnimalTraits {
var formattedData = _.cloneDeep(traits);
... apply some formatting logic
return formattedData;
}
...
}
In short, components are not managing data in these services properly and, upon injection into a component for a new page for the service we see the old formattedData.
Here is the service for reference:
@Injectable()
export class AnimalTraitService {
private _formattedData;
get formattedData() {
return this._formattedData;
}
set formattedData(value) {
this._formattedData = value;
}
getResults(id) {
return this.httpService.get(`localhost:888/baseapp/results`);
}
}
In the above example AnimalComponent loads the info for Deer triggered by some event. The component then formats the trait data and stores it back in the service. Upon loading the information for Dog, the AnimalTraitService still has deer info stored in its _formattedData which subsequently appears on the UI until some process replaces it.
Resetting the data, in this case in an error handler, but also for example in an onDestroy event handler is necessary to properly reset the service.
I have observed this issue occur twice in our codebase and it worries me.
Mostly because it relies on the developer to be diligently aware of the data defined in their services and seems to rely on the components utilizing these services to manage it correctly.
In both cases it was observable from the UI but there could be a dire consequence if users are visualizing data in the wrong context in a less obvious way. In the Java world when working with injectable services we avoid this problem by not using class instance data inside of services since they can be instantiated once and reused by multiple proccesses concurrently. Are there similar design patterns in Angular one could use to prevent this from occurring?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
