Metabase Embedding SDK CreateQuestion 组件属性完全指南:嵌入式新建问题的创建、保存与回调机制
【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase
CreateQuestion是 Metabase Embedding SDK(frontend/src/embedding-sdk-bundle)中用于在宿主应用中内嵌"新建问题"编辑器的公开组件,它允许终端用户直接在你的产品界面里从零创建、编辑并保存一个问题(Question)。本文以其属性参考文档 CreateQuestionProps.md 为骨架,结合仓库源码逐项解析全部 23 个属性(Props)的含义、类型、默认行为与回调触发时机,并给出可直接运行的 React 集成示例,帮助你完全掌控嵌入问题的数据源选择、SQL 参数联动、保存流程与可视化切换等细节。
CreateQuestion 是什么:与 InteractiveQuestion 的关系
CreateQuestion本质上是一个轻量封装。在 CreateQuestion.tsx 中可以看到它的完整定义:
export type CreateQuestionProps = Omit< Partial<InteractiveQuestionBaseProps>, "children" >; const CreateQuestionInner = (props: CreateQuestionProps = {}) => ( <InteractiveQuestion {...props} questionId="new" /> ); export const CreateQuestion = withPublicComponentWrapper(CreateQuestionInner, { supportsGuestEmbed: false, }) as typeof CreateQuestionInner;从源码结构可以推断出三个关键事实:
CreateQuestionProps是InteractiveQuestion基础属性的子集:它通过Omit<Partial<InteractiveQuestionBaseProps>, "children">派生出自己的类型,因此本篇文章属性表中几乎所有属性,在InteractiveQuestion组件上同样适用。- 内部固定传入
questionId="new":组件在渲染时强制将问题 ID 设为"new",这正是"新建问题"模式的标志。在 InteractiveQuestion.tsx 中,resolvedQuestionId === "new" || resolvedQuestionId === "new-native"会被判定为isNewQuestion,从而驱动新建状态下的 UI 与行为。 - 不支持 Guest Embed(访客嵌入):
supportsGuestEmbed: false表明该组件要求认证用户环境,这一点与InteractiveQuestion一致。
另外,官方文档在 CreateQuestion.md 中明确标记该组件为Deprecated(已废弃),推荐改用<InteractiveQuestion questionId="new" />直接实现相同能力。即便如此,由于CreateQuestion仍在 SDK 公开 API 中保留,且其属性体系完整继承了InteractiveQuestion,理解这份属性表对两种用法都有直接价值。
属性总览(完整属性表)
CreateQuestion接受的唯一参数props类型为CreateQuestionProps | undefined,返回值是 React 的Element。下表完整列出全部属性:
| Property | Type | Description |
|---|---|---|
className? | string | 添加到根元素的自定义 CSS 类名。 |
dataPicker? | EmbeddingDataPicker | 控制问题中数据源选择菜单的形态;设置为dataPicker = "staged"可启用完整版数据选择器。 |
entityTypes? | EmbeddingEntityType[] | 指定数据选择器中可用的实体类型(如表、模型、问题、指标等)数组。 |
height? | Height<string \| number> | 数值或字符串形式的 CSS 尺寸值,指定组件高度。 |
hiddenParameters? | string[] | 需要隐藏的参数列表。 |
initialCollection? | SdkCollectionId | 保存弹窗的收藏夹选择器中预选的目标集合。与targetCollection不同,此时选择器仍然可见,用户可改选其他集合;当targetCollection已设置时该属性被忽略。 |
initialSqlParameters? | SqlParameterValues | SQL 参数的初始值(以 slug 为键)。仅在挂载时应用一次,之后用户在组件内的修改不会回传给宿主应用。每个参数的具体行为见下文"SQL 参数"一节。 |
isSaveEnabled? | boolean | 是否显示保存按钮。 |
onBeforeSave? | (question:MetabaseQuestion|undefined,context: {isNewQuestion:boolean; }) =>Promise<void> | 保存前触发的回调函数,仅在isSaveEnabled = true时相关。 |
onNavigateBack? | () =>void | 用户点击返回按钮时触发的回调函数。 |
onRun? | (question:MetabaseQuestion|undefined) =>void | 问题更新时触发,包括用户点击问题编辑器中的Visualize按钮。 |
onSave? | (question:MetabaseQuestion,context: {dashboardTabId?:number;isNewQuestion:boolean; }) =>void | 用户保存问题时触发的回调,仅在isSaveEnabled = true时相关。 |
onSqlParametersChange? | (payload:SqlParameterChangePayload) =>void | SQL 参数变化时触发。payload.source区分初始加载状态('initial-state')、用户 UI 编辑('manual-change')与自动更新('auto-change')。 |
onVisualizationChange? | (display:"object"|"table"|"bar"|"line"|"pie"|"scalar"|"row"|"area"|"combo"|"pivot"|"smartscalar"|"gauge"|"progress"|"funnel"|"map"|"scatter"|"boxplot"|"waterfall"|"sankey"|"treemap"|"list") =>void | 可视化类型切换时触发的回调。 |
plugins? | MetabasePluginsConfig | 插件配置对象(官方文档未给出额外说明,具体能力取决于插件配置类型定义)。 |
sqlParameters? | SqlParameterValues | 受控的 SQL 参数值(以 slug 为键)。每次渲染时该对象都会替换问题的参数值;需配合onSqlParametersChange保持与用户编辑同步。 |
style? | CSSProperties | 添加到根元素的自定义样式对象。 |
targetCollection? | SdkCollectionId | 问题保存到的目标集合。设置后将隐藏保存弹窗中的集合选择器,仅对交互式问题适用。 |
title? | SdkQuestionTitleProps | 控制问题标题是否显示,并允许用自定义标题替换默认问题标题。默认显示。 |
width? | Width<string \| number> | 数值或字符串形式的 CSS 尺寸值,指定组件宽度。 |
withAlerts? | boolean | 是否允许在问题上设置告警(Alerts)。 |
withChartTypeSelector? | boolean | 是否显示图表类型选择器及对应的设置按钮。仅在使用默认布局时相关。 |
withDownloads? | boolean | 是否允许在问题中下载结果。 |
withEditorButton? | boolean | 是否显示编辑器按钮。仅在使用默认布局时相关。 |
布局与外观:尺寸、类名、样式与标题
CreateQuestion在宿主页面中渲染为一个根元素,你可以通过三类属性完全控制它的外观:
width/height:接受数字或字符串形式的 CSS 尺寸值,例如width={800}、height="60vh"。这对应底层SdkQuestion根容器对外暴露的尺寸控制。className:自定义类名会被追加到根元素上,便于你用全局样式表或 CSS 模块做定位与覆写。style:直接传入 ReactCSSProperties对象,适合在组件内部做内联样式定制。title:类型为SdkQuestionTitleProps。默认情况下问题标题是显示的;你可以通过该属性隐藏标题,或用自定义文本替代默认的问题标题,适合在已有自身页头设计的宿主应用中避免标题重复。
数据源选择:dataPicker、entityTypes 与集合预选
新建问题的第一步是让用户挑选数据源,相关属性共同控制这一交互:
dataPicker:类型为EmbeddingDataPicker,取值"staged" | "flat"。文档明确指出:设置dataPicker = "staged"可启用"完整版"(分步式)数据选择器;而默认的扁平数据选择器只列出可直接选择的实体。从 InteractiveQuestion.unit.spec.tsx 的测试用例可以看到,当questionId="new"且使用默认数据选择器时,界面会渲染出 "Pick your starting data" 按钮和对应的数据选择弹窗,弹窗内列出可用的模型(如 "Orders model");当传入dataPicker: "staged"时会走另一套分步决策逻辑。entityTypes:类型为EmbeddingEntityType[],用于白名单式地指定数据选择器里可用的实体类型(例如只允许选模型和原生问题,而隐藏底层的数据库表)。它配合dataPicker一起决定"能选什么"。initialCollection:指定保存弹窗中收藏夹选择器的预选集合(SdkCollectionId)。关键语义是:选择器仍然可见,用户可以改选其他集合,因此它只是一个"初始选中项"。文档同时强调:一旦设置了targetCollection,initialCollection会被忽略。targetCollection:直接把问题固定保存到某个集合,并隐藏保存弹窗里的集合选择器(仅对交互式问题适用)。适合在"保存到固定工作区"的产品流程中使用。
SQL 参数:初始值、受控值与变更回调
对于原生 SQL 问题,参数控制是CreateQuestion最值得关注的能力之一,涉及三个属性:
initialSqlParameters(一次性初始值)
类型为SqlParameterValues,本质上是Record<string, string | number | boolean | (string | number | boolean | null)[] | null | undefined>,以参数 slug 为键。它的行为是:
- 设置为某个值 → 应用该值;
- 设置为
null→ 严格清空该参数,忽略其默认值; - 省略该键(或设为
undefined)→ 回退到参数默认值(无默认值则为null)。
关键限制是:只在组件挂载时应用一次,用户在问题编辑器中的后续修改不会反向同步到宿主应用。它适合"进入页面时携带一份预设筛选条件"的场景。
sqlParameters(受控值)
同样是 slug 键控的SqlParameterValues,但语义完全不同:每次渲染时,该对象都会整体替换问题的参数值,是真正的"受控组件"模式。其逐参数行为与initialSqlParameters一致(值 /null严格清空 / 省略回退默认)。文档建议将其与onSqlParametersChange配对使用,从而把组件内的用户编辑状态"提升"到宿主应用,实现完全受控的参数双向同步。
onSqlParametersChange(变更回调)
回调载荷类型为SqlParameterChangePayload,其source字段用于区分三种触发来源:
'initial-state':加载时的初始状态;'manual-change':用户在 UI 中的手动编辑;'auto-change':由系统自动更新(如参数联动、默认值解析等)。
借助source,宿主应用可以精确判断应如何响应:例如只在'manual-change'时更新自己的受控状态,避免把回显动作再次写回组件造成循环。
保存流程:isSaveEnabled、onBeforeSave 与 onSave
新建问题的终点是"保存",这一流程由三个属性共同编排:
isSaveEnabled:布尔开关,决定是否显示保存按钮。关闭后用户只能创建与编辑问题,无法落库。onBeforeSave:保存动作发生前触发的异步回调,接收(question, context),其中question为MetabaseQuestion(可能为undefined),context提供isNewQuestion标志。返回Promise<void>意味着你可以在真正保存前执行异步校验、弹窗确认或附加副作用。仅当isSaveEnabled = true时相关。onSave:保存成功后触发的同步回调,接收(question, context)。与onBeforeSave不同,这里的question必然存在;context除了isNewQuestion外还包含可选的dashboardTabId?(当问题保存进某个仪表盘标签页时携带)。典型用途是保存后通知宿主路由跳转、刷新列表或埋点上报。
导航与运行:onNavigateBack、onRun 与 onVisualizationChange
这组回调对应问题编辑过程中的关键交互节点:
onNavigateBack:用户点击返回按钮时触发,无参数。宿主应用通常用它退出内嵌的编辑视图、切回自己的页面。onRun:问题更新时触发(含点击编辑器中的Visualize按钮),回调携带最新的MetabaseQuestion(可能为undefined)。它是"数据结果变化"的通用监听点,可用于在宿主侧同步展示元信息。onVisualizationChange:可视化类型切换时触发,回调参数是 21 种显示类型之一:object、table、bar、line、pie、scalar、row、area、combo、pivot、smartscalar、gauge、progress、funnel、map、scatter、boxplot、waterfall、sankey、treemap、list。宿主应用可据此联动自己的图表切换器或分析偏好设置。
功能开关:hiddenParameters、withAlerts、withDownloads 与布局开关
最后一组属性用于裁剪编辑器暴露给终端用户的能力面:
hiddenParameters:string[],列出要隐藏的参数(按参数 slug),适合把内部参数从用户界面中屏蔽掉。withAlerts:布尔值,启用后在问题上可设置告警(Alerts),让用户把问题结果转为定时通知。withDownloads:布尔值,启用后允许用户下载问题结果(导出 CSV 等)。withChartTypeSelector:布尔值,控制图表类型选择器与对应设置按钮是否显示。仅在使用默认布局时相关——当你通过子组件自定义布局(如InteractiveQuestion.ChartTypeDropdown)时该开关不生效。withEditorButton:布尔值,控制编辑器按钮是否显示,同样仅在使用默认布局时相关。
从源码看 "new" 问题的判定与渲染
理解questionId="new"的底层逻辑有助于正确使用上述属性。在 InteractiveQuestion.tsx 中,组件通过resolvedQuestionId === "new" || resolvedQuestionId === "new-native"判定isNewQuestion,并以此为依据决定埋点上报的id_new/id_new_native标记。其中resolvedQuestionId由 SdkAdHocQuestion/utils.ts 中的resolveQuestionId函数计算:
- 从 URL slug 提取实体 ID(如
"42-my-question"→42); - 若无 slug 且反序列化卡片是原生查询(
dataset_query.type === "native"),返回"new-native"(即新建 SQL 编辑器模式); - 其余情况返回
null,即新建笔记本模式。
这解释了为什么<CreateQuestion />与<InteractiveQuestion questionId="new" />会直接进入数据源选择与新建编辑器流程——它们都绕过了已保存问题的加载分支。测试用例 InteractiveQuestion.unit.spec.tsx 验证了questionId="new"时会渲染 "Pick your starting data" 数据选择器与查询编辑器;SDK 自带的 CreateQuestion.stories.tsx 则展示了组件在 Storybook 环境下的标准用法(包裹CommonSdkStoryWrapper、以Flex布局承载组件实例)。
完整示例:一个可运行的 CreateQuestion 集成
综合以上属性,下面给出一个贴近实战的完整示例,覆盖数据源控制、SQL 参数受控、保存回调与可视化监听:
import { useState } from "react"; import { CreateQuestion, MetabaseProvider, type CreateQuestionProps, } from "@metabase/embedding-sdk-react"; const authConfig = { metabaseInstanceUrl: "https://metabase.example.com", getToken: () => Promise.resolve("<your-jwt-token>"), }; export default function QuestionCreator() { const [sqlParams, setSqlParams] = useState({ category: "Gadgets" }); const props: CreateQuestionProps = { // —— 布局与外观 —— width: 960, height: "70vh", title: false, // 隐藏默认标题,由宿主页面自行提供标题 // —— 数据源选择 —— dataPicker: "staged", // 启用完整版分步数据选择器 entityTypes: ["model", "question"], // 只允许选择模型与已保存问题 targetCollection: 5, // 固定保存到集合 5,并隐藏集合选择器 // —— SQL 参数(受控) —— sqlParameters: sqlParams, onSqlParametersChange: (payload) => { // payload.source: 'initial-state' | 'manual-change' | 'auto-change' if (payload.source === "manual-change") { setSqlParams(payload.params as typeof sqlParams); } }, // —— 保存流程 —— isSaveEnabled: true, onBeforeSave: async (question, { isNewQuestion }) => { console.log("即将保存(新建:%s)", isNewQuestion, question?.id()); // 在这里做异步校验,例如检查权限或命名规范 }, onSave: (question, { isNewQuestion, dashboardTabId }) => { console.log("已保存,question id =", question.id(), dashboardTabId); }, // —— 导航与运行 —— onNavigateBack: () => history.back(), onRun: (question) => console.log("问题已运行", question?.displayName()), onVisualizationChange: (display) => console.log("可视化切换为", display), // —— 功能开关 —— hiddenParameters: ["internal_tenant_id"], withAlerts: true, withDownloads: true, withChartTypeSelector: true, withEditorButton: true, }; return ( <MetabaseProvider authConfig={authConfig}> <CreateQuestion {...props} /> </MetabaseProvider> ); }迁移建议:从 CreateQuestion 到 InteractiveQuestion
官方已在 CreateQuestion.md 中标记CreateQuestion为废弃,推荐的迁移方式是在组件树中替换为<InteractiveQuestion questionId="new" />:
import { InteractiveQuestion } from "@metabase/embedding-sdk-react"; export default function QuestionCreator() { return <InteractiveQuestion questionId="new" /* 其余属性与本文章属性表完全一致 */ />; }由于CreateQuestionProps本质上是InteractiveQuestionBaseProps的部分子集,本文属性表中的绝大多数属性(dataPicker、entityTypes、sqlParameters、isSaveEnabled、onSave、withAlerts等)在迁移后无需改动即可继续生效;只有极少数仅存在于InteractiveQuestion的专属能力(如通过children自定义子组件布局、query内部属性)需要额外关注。结合本文对每个属性语义的拆解,你可以直接对照属性表完成迁移与验证。
【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考