fix(ui-templates): isolate reference view context (#10415)

This commit is contained in:
Zeke Zhang
2026-08-25 22:25:17 +08:00
committed by GitHub
parent 61940292e4
commit fcf6bedd63
2 changed files with 54 additions and 1 deletions
@@ -0,0 +1,41 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { FlowContext, FlowEngine } from '@nocobase/flow-engine';
import { describe, expect, it } from 'vitest';
import { ensureBlockScopedEngine, ensureScopedEngineView } from '../referenceShared';
describe('referenceShared', () => {
it('keeps the bridged view lookup local after the host view delegate is removed', () => {
const parentEngine = new FlowEngine();
const hostModel = parentEngine.createModel({ use: 'FlowModel', uid: 'host-model' });
const hostViewContext = new FlowContext();
const hostView = { uid: 'host-view' };
hostViewContext.defineProperty('view', { value: hostView });
hostModel.context.addDelegate(hostViewContext);
const scopedEngine = ensureBlockScopedEngine(parentEngine);
ensureScopedEngineView(scopedEngine, hostModel.context);
expect(scopedEngine.context).not.toBe(parentEngine.context);
expect(scopedEngine.context.engine).toBe(scopedEngine);
expect(scopedEngine.context.view).toBe(hostView);
expect(parentEngine.context.getPropertyOptions('view')).toBeUndefined();
const nextHostView = { uid: 'next-host-view' };
hostViewContext.defineProperty('view', { value: nextHostView });
expect(scopedEngine.context.view).toBe(nextHostView);
hostModel.context.removeDelegate(hostViewContext);
expect(() => scopedEngine.context.view).not.toThrow();
expect(scopedEngine.context.view).toBeUndefined();
expect(parentEngine.context.getPropertyOptions('view')).toBeUndefined();
});
});
@@ -22,7 +22,19 @@ import {
import { NAMESPACE } from '../locale';
export function ensureBlockScopedEngine(flowEngine: FlowEngine, scopedEngine?: FlowEngine): FlowEngine {
return scopedEngine ?? createBlockScopedEngine(flowEngine);
if (scopedEngine) return scopedEngine;
const engine = createBlockScopedEngine(flowEngine);
// BlockScopedFlowEngine shares the parent engine context. Add a reference-local overlay so view bridging stays local while other context values still delegate to the parent.
const parentContext = engine.context;
const context = new FlowContext();
context.defineProperty('engine', { value: engine });
context.addDelegate(parentContext);
Object.defineProperty(engine, 'context', {
configurable: true,
value: context,
});
return engine;
}
export function ensureScopedEngineView(engine: FlowEngine, hostContext?: FlowContext): void {