@@ -208,10 +208,10 @@ static HxModal()
208
208
209
209
210
210
private bool _opened = false ; // indicates whether the modal is open
211
- private bool _shouldOpenModal = false ; // indicates whether the modal is going to be opened
212
211
private DotNetObjectReference < HxModal > _dotnetObjectReference ;
213
212
private ElementReference _modalElement ;
214
213
private IJSObjectReference _jsModule ;
214
+ private Queue < Func < Task > > _onAfterRenderTasksQueue = new ( ) ;
215
215
private bool _disposed ;
216
216
217
217
public HxModal ( )
@@ -224,20 +224,52 @@ public HxModal()
224
224
/// </summary>
225
225
public Task ShowAsync ( )
226
226
{
227
+ if ( ! _opened )
228
+ {
229
+ _onAfterRenderTasksQueue . Enqueue ( async ( ) =>
230
+ {
231
+ // Running JS interop is postponed to OnAfterRenderAsync to ensure modalElement is set
232
+ // and correct order of commands (Show/Hide) is preserved
233
+ _jsModule ??= await JSRuntime . ImportHavitBlazorBootstrapModuleAsync ( nameof ( HxModal ) ) ;
234
+ if ( _disposed )
235
+ {
236
+ return ;
237
+ }
238
+ await _jsModule . InvokeVoidAsync ( "show" , _modalElement , _dotnetObjectReference , CloseOnEscapeEffective , OnHiding . HasDelegate ) ;
239
+ } ) ;
240
+ }
227
241
_opened = true ; // mark modal as opened
228
- _shouldOpenModal = true ; // mark modal to be shown in OnAfterRender
229
242
230
- StateHasChanged ( ) ; // ensures render modal HTML
243
+ StateHasChanged ( ) ; // ensures rendering modal HTML
231
244
232
245
return Task . CompletedTask ;
233
246
}
234
247
235
248
/// <summary>
236
249
/// Closes the modal.
237
250
/// </summary>
238
- public async Task HideAsync ( )
251
+ public Task HideAsync ( )
239
252
{
240
- await _jsModule . InvokeVoidAsync ( "hide" , _modalElement ) ;
253
+ if ( ! _opened )
254
+ {
255
+ // this might be a minor PERF benefit, if it turns out to be causing troubles, we can remove this or make it configurable through optional method parameter
256
+ return Task . CompletedTask ;
257
+ }
258
+
259
+ _onAfterRenderTasksQueue . Enqueue ( async ( ) =>
260
+ {
261
+ // Running JS interop is postponed to OnAfterRenderAsync to ensure modalElement is set
262
+ // and correct order of commands (Show/Hide) is preserved
263
+ _jsModule ??= await JSRuntime . ImportHavitBlazorBootstrapModuleAsync ( nameof ( HxModal ) ) ;
264
+ if ( _disposed )
265
+ {
266
+ return ;
267
+ }
268
+ await _jsModule . InvokeVoidAsync ( "hide" , _modalElement ) ;
269
+ } ) ;
270
+ StateHasChanged ( ) ; // enforce rendering
271
+
272
+ return Task . CompletedTask ;
241
273
}
242
274
243
275
/// <summary>
@@ -268,26 +300,15 @@ public async Task HandleModalHidden()
268
300
[ JSInvokable ( "HxModal_HandleModalShown" ) ]
269
301
public async Task HandleModalShown ( )
270
302
{
303
+ _opened = true ;
271
304
await InvokeOnShownAsync ( ) ;
272
305
}
273
306
274
307
protected override async Task OnAfterRenderAsync ( bool firstRender )
275
308
{
276
- await base . OnAfterRenderAsync ( firstRender ) ;
277
-
278
- if ( _shouldOpenModal )
309
+ while ( _onAfterRenderTasksQueue . TryDequeue ( out var task ) )
279
310
{
280
- // do not run show in every render
281
- // the line must be prior to JSRuntime (because BuildRenderTree/OnAfterRender[Async] is called twice; in the bad order of lines the JSRuntime would be also called twice).
282
- _shouldOpenModal = false ;
283
-
284
- // Running JS interop is postponed to OnAfterAsync to ensure modalElement is set.
285
- _jsModule ??= await JSRuntime . ImportHavitBlazorBootstrapModuleAsync ( nameof ( HxModal ) ) ;
286
- if ( _disposed )
287
- {
288
- return ;
289
- }
290
- await _jsModule . InvokeVoidAsync ( "show" , _modalElement , _dotnetObjectReference , CloseOnEscapeEffective , OnHiding . HasDelegate ) ;
311
+ await task ( ) ;
291
312
}
292
313
}
293
314
@@ -363,26 +384,11 @@ protected virtual async ValueTask DisposeAsyncCore()
363
384
364
385
if ( _jsModule != null )
365
386
{
366
- // We need to remove backdrop when leaving "page" when HxModal is shown (opened).
367
- if ( _opened )
368
- {
369
- try
370
- {
371
- await _jsModule . InvokeVoidAsync ( "dispose" , _modalElement ) ;
372
- }
373
- catch ( JSDisconnectedException )
374
- {
375
- // NOOP
376
- }
377
- catch ( TaskCanceledException )
378
- {
379
- // NOOP
380
- }
381
- }
382
-
383
387
try
384
388
{
389
+ await _jsModule . InvokeVoidAsync ( "dispose" , _modalElement , _opened ) ;
385
390
await _jsModule . DisposeAsync ( ) ;
391
+
386
392
}
387
393
catch ( JSDisconnectedException )
388
394
{
0 commit comments