Skip to content

Commit a1c4bb9

Browse files
committed
fix: fire shiny:outputinvalidated for outputs bound after invalidation (#2797)
When an output is invalidated before it's in the DOM (e.g., inside a closed modal), the shiny:outputinvalidated event was never fired. The progress state machine already tracks this — the Invalidated state means "this output was invalidated while not bound." Adds isInvalidated() to OutputProgressReporter and uses it in bindOutputs() to fire the event when an output in the Invalidated state is first bound. This is intentionally separate from the isRecalculating() check that drives showProgress(), since isRecalculating() also includes the Initial state (every output on first page load) where firing the invalidated event would be incorrect.
1 parent e07298a commit a1c4bb9

9 files changed

Lines changed: 54 additions & 24 deletions

File tree

inst/www/shared/shiny.js

Lines changed: 14 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

inst/www/shared/shiny.js.map

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

inst/www/shared/shiny.min.js

Lines changed: 16 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

inst/www/shared/shiny.min.js.map

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

srcts/src/shiny/bind.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ type BindInputsCtx = {
231231
outputBindings: BindingRegistry<OutputBinding>;
232232
initDeferredIframes: () => void;
233233
outputIsRecalculating: (id: string) => boolean;
234+
outputIsInvalidated: (id: string) => boolean;
234235
};
235236
function bindInputs(
236237
shinyCtx: BindInputsCtx,
@@ -316,7 +317,7 @@ function bindInputs(
316317
}
317318

318319
async function bindOutputs(
319-
{ outputBindings, outputIsRecalculating }: BindInputsCtx,
320+
{ outputBindings, outputIsRecalculating, outputIsInvalidated }: BindInputsCtx,
320321
scope: BindScope = document.documentElement,
321322
): Promise<void> {
322323
const $scope = $(scope);
@@ -359,6 +360,15 @@ async function bindOutputs(
359360
bindingAdapter.showProgress(true);
360361
}
361362

363+
if (outputIsInvalidated(id)) {
364+
$el.trigger({
365+
type: "shiny:outputinvalidated",
366+
// @ts-expect-error; Can not remove info on a established, malformed Event object
367+
binding: binding,
368+
name: id,
369+
});
370+
}
371+
362372
bindingsRegistry.addBinding(id, "output");
363373
$el.trigger({
364374
type: "shiny:bound",

srcts/src/shiny/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ class ShinyClass {
224224
initDeferredIframes,
225225
outputIsRecalculating: (id: string) =>
226226
this.shinyapp?.$outputProgress.isRecalculating(id) ?? false,
227+
outputIsInvalidated: (id: string) =>
228+
this.shinyapp?.$outputProgress.isInvalidated(id) ?? false,
227229
};
228230
};
229231

srcts/src/shiny/outputProgress.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ class OutputProgressReporter {
9696
return result;
9797
}
9898

99+
isInvalidated(name: string): boolean {
100+
return this.#getState(name) === OutputStates.Invalidated;
101+
}
102+
99103
// Returns whether the output is recalculating or not.
100104
isRecalculating(name: string): boolean {
101105
const state = this.#getState(name);

srcts/types/src/shiny/bind.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type BindInputsCtx = {
99
outputBindings: BindingRegistry<OutputBinding>;
1010
initDeferredIframes: () => void;
1111
outputIsRecalculating: (id: string) => boolean;
12+
outputIsInvalidated: (id: string) => boolean;
1213
};
1314
declare function bindInputs(shinyCtx: BindInputsCtx, scope?: BindScope): {
1415
[key: string]: {

srcts/types/src/shiny/outputProgress.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ declare class OutputProgressReporter {
66
private outputStates;
77
private changedOutputs;
88
takeChanges(): Map<string, boolean>;
9+
isInvalidated(name: string): boolean;
910
isRecalculating(name: string): boolean;
1011
updateStateFromMessage(message: Message): void;
1112
}

0 commit comments

Comments
 (0)