Combining all the feedback into a comprehensive answer for you:
As mentioned in the comments, resorting to forcefully reloading a window should be an absolute last measure.
Ideally, in situations like this, it's best to update the data client-side (since you have control over what has been created/updated/deleted).
Although this approach usually requires more effort, it is often the most effective.
Another option is simply making the API request again. It's straightforward and quick, making it my preferred method in many cases.
Only as a final resort should you refresh the entire page. In most cases, individual components can be updated with new data or instructed to fetch fresh data without needing a full page reload.
Addressing the issue of continued force reloading even on close:
According to the comments, you can control whether the component should reload when closed by using the this.dialogRef.close()
method. You can pass a boolean value to decide if it should reload.
const dialogRef = this.dialog
.open(AddUserRoleDialogComponent,
{
width: '500px',
data: ''
}
)
.afterClosed()
.subscribe((shouldReload: boolean) => {
this.dialogRef.unsubscribe();
if (shouldReload) window.location.reload()
});
When closing:
public onCancelSave(): void {
this.dialogRef.close(false);
}
public onSave(): void {
this.dialogRef.close(true);
}
For handling the reloading itself:
Rather than performing a window reload, consider making the API request again instead of refreshing the window...
if (shouldReload) this.myService.getData(...);
And lastly, regarding Angular change detection:
The response depends on how your data is displayed. Most systems handle changes within arrays through some form of loop. The key is understanding what triggers these checks and how often they occur.
Angular typically doesn't detect property-level changes directly (e.g., changing a specific property of an object). However, it does react when an entire object is replaced. The spread operator in newer ES versions simplifies creating new objects that effectively trigger such updates, both for objects and arrays:
this.someObject = {name: 'hello'};
this.someObject.name = 'world';
// Changes at property level may not trigger template update
this.someObject = {...this.someObject, name: 'world'};
// This assigns a completely new object, prompting re-evaluation throughout
If, for instance, the someObject
was an input
to a component, only the second scenario would trigger its change detection, not the first.
The same principle applies to arrays -
this.someArray = [...this.someArray]
.
Note:
These adjustments were necessary due to the structure of your new user request function.
Typically, you'd return the observable (this.http.get/post/etc(...)
) and let subscribers handle the request via .subscribe()
. It's the subscription mechanism that actually triggers the action.
In my practice, I employ this methodology for all requests to maintain asynchronous behavior without blocking (the debate between promises/async-await versus observables is a separate topic).
A regular service setup (I've encapsulated such logic in a base class for brevity) might appear as follows:
@Injectable()
export class MyService {
private readonly someUrl: string = 'some/url';
private obs: Observer<MyType>;
private _someData$: Observable<MyType>;
constructor(private readonly http: HttpClient) {
this._someData$ = new Obseravable<MyType>((x: Observer<MyType>) => {this.obs = x;}).pipe(share());
}
public get someData$(): Observable<MyType> { return this._someData$; }
}
public getMyDataPlease(): void {
const sub = this.http.get(this.someUrl)
.subscribe({
next: (response: ResponseType) => {
sub.unsubscribe();
if (this.obs) this.obs.next(response);
},
error: (err) => {
sub.unsubscribe();
console.error(err);
}
});
}
Any interested component can subscribe to receive notifications whenever new data arrives (like in a call to getMyDataPlease()
). The setup involves firing a single request, subscribing, and then unsubscribing afterward (typically suitable for one-time responses). If a continuous stream is required, the architecture could be adjusted accordingly.
@Component({...})
export class MyComponent implements OnInit, OnDestroy {
private subs: Subscription[];
constructor(private readonly service: MyService) {
this.subs = [];
}
public ngOnInit(): void {
this.subs.push(this.service.someData$
.subscribe((x: ResponseType) => this.onSomeDataResponse(x)));
this.service.getMyDataPlease();
}
public ngOnDestroy(): void {
this.subs.forEach((x: Subscription) => x.unsubscribe());
}
private onSomeDataResponse(response: ResponseType): void {
console.log('Get data response: ', response);
}
}
ngOnInit
prepares the component to process responses and initiates the data retrieval process.
ngOnDestroy
ensures proper cleanup upon destruction of the component.
To briefly address async/await usage: async/await introduces sequential execution.
It's common to perform varying setups in methods like ngOnInit
, possibly obtaining data from multiple sources concurrently. Using async/await for such operations may cause each step to wait for completion before proceeding, hindering concurrency.
async/await doesn't inherently enable parallelism.
Observables, on the other hand, enable a fire-and-forget approach, allowing the program to keep running without waiting for each operation to finish. Once data is available, it's appropriately handled, ensuring smooth operation without blocking subsequent code execution.
By embracing event-driven architectures and maintaining consistent subscription practices, you'll benefit from seamless data flow and responsive applications.