重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
widget-coherence by groeimetai/snow-flow
npx skills add https://github.com/groeimetai/snow-flow --skill widget-coherence服务门户小部件必须在服务器脚本、客户端控制器和 HTML 模板之间实现完美通信。这是强制性的——当这些组件无法正确相互通信时,小部件将失效。
每个小部件都需要同步通信:
data.* 属性c.server.get() 发送的 每一个 input.actionng-click 调用的 每一个 方法c.server.get({action: 'name'}) 与服务器通信c.data广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
data.* 属性// SERVER SCRIPT
;(function () {
data.incidents = []
data.loading = true
var gr = new GlideRecord("incident")
gr.addQuery("active", true)
gr.setLimit(10)
gr.query()
while (gr.next()) {
data.incidents.push({
sys_id: gr.getUniqueValue(),
number: gr.getValue("number"),
short_description: gr.getValue("short_description"),
})
}
data.loading = false
})()
// CLIENT CONTROLLER
api.controller = function ($scope) {
var c = this
c.selectIncident = function (incident) {
c.selectedIncident = incident
}
}
<!-- HTML TEMPLATE -->
<div ng-if="data.loading">加载中...</div>
<div ng-if="!data.loading">
<div ng-repeat="incident in data.incidents" ng-click="c.selectIncident(incident)">
{{incident.number}}: {{incident.short_description}}
</div>
</div>
// CLIENT CONTROLLER
c.saveIncident = function () {
c.server
.get({
action: "save_incident",
incident_data: c.formData,
})
.then(function (response) {
if (response.data.success) {
c.data.message = "保存成功"
}
})
}
// SERVER SCRIPT
if (input && input.action === "save_incident") {
var gr = new GlideRecord("incident")
gr.initialize()
gr.setValue("short_description", input.incident_data.short_description)
data.new_sys_id = gr.insert()
data.success = !!data.new_sys_id
}
部署小部件前,请验证:
data.property 都在 HTML 或客户端中被使用ng-click="c.method()" 在客户端都有对应的 c.method 方法c.server.get({action: 'x'}) 在服务器都有对应的 if(input.action === 'x') 条件判断data.* 属性都在服务器中初始化(即使为空)// CLIENT - 发送 'saveIncident'
c.server.get({ action: "saveIncident" })
// SERVER - 期望 'save_incident' (不匹配!)
if (input.action === "save_incident") {
}
<!-- HTML - 调用 saveData() -->
<button ng-click="c.saveData()">保存</button>
// CLIENT - 定义了 save() (不匹配!)
c.save = function () {}
<!-- HTML - 引用 user.email -->
<span>{{data.user.email}}</span>
// SERVER - 只设置了 user.name (user.email 未定义!)
data.user = { name: userName }
| 指令 | 用途 |
|---|---|
ng-if | 条件渲染元素 |
ng-show/ng-hide | 切换可见性(元素保留在 DOM 中) |
ng-repeat | 遍历数组 |
ng-click | 处理点击事件 |
ng-model | 双向数据绑定 |
ng-class | 动态 CSS 类 |
ng-disabled | 禁用表单元素 |
每周安装量
54
代码仓库
GitHub 星标数
59
首次出现
2026年1月22日
安全审计
安装于
claude-code50
gemini-cli49
opencode48
github-copilot48
codex48
cursor48
Service Portal widgets MUST have perfect communication between Server Script, Client Controller, and HTML Template. This is not optional - widgets fail when these components don't talk to each other correctly.
Every widget requires synchronized communication:
data.* properties that HTML will referenceinput.action that client sends via c.server.get()ng-click in HTMLc.server.get({action: 'name'}) for server communicationc.data when server respondsdata.* properties that server provides// SERVER SCRIPT
;(function () {
data.incidents = []
data.loading = true
var gr = new GlideRecord("incident")
gr.addQuery("active", true)
gr.setLimit(10)
gr.query()
while (gr.next()) {
data.incidents.push({
sys_id: gr.getUniqueValue(),
number: gr.getValue("number"),
short_description: gr.getValue("short_description"),
})
}
data.loading = false
})()
// CLIENT CONTROLLER
api.controller = function ($scope) {
var c = this
c.selectIncident = function (incident) {
c.selectedIncident = incident
}
}
<!-- HTML TEMPLATE -->
<div ng-if="data.loading">Loading...</div>
<div ng-if="!data.loading">
<div ng-repeat="incident in data.incidents" ng-click="c.selectIncident(incident)">
{{incident.number}}: {{incident.short_description}}
</div>
</div>
// CLIENT CONTROLLER
c.saveIncident = function () {
c.server
.get({
action: "save_incident",
incident_data: c.formData,
})
.then(function (response) {
if (response.data.success) {
c.data.message = "Saved successfully"
}
})
}
// SERVER SCRIPT
if (input && input.action === "save_incident") {
var gr = new GlideRecord("incident")
gr.initialize()
gr.setValue("short_description", input.incident_data.short_description)
data.new_sys_id = gr.insert()
data.success = !!data.new_sys_id
}
Before deploying a widget, verify:
data.property in server is used in HTML or clientng-click="c.method()" has matching c.method in clientc.server.get({action: 'x'}) has matching if(input.action === 'x') in serverdata.* properties are initialized in server (even if empty)// CLIENT - sends 'saveIncident'
c.server.get({ action: "saveIncident" })
// SERVER - expects 'save_incident' (MISMATCH!)
if (input.action === "save_incident") {
}
<!-- HTML - calls saveData() -->
<button ng-click="c.saveData()">Save</button>
// CLIENT - defines save() (MISMATCH!)
c.save = function () {}
<!-- HTML - references user.email -->
<span>{{data.user.email}}</span>
// SERVER - only sets user.name (user.email is undefined!)
data.user = { name: userName }
| Directive | Purpose |
|---|---|
ng-if | Conditionally render element |
ng-show/ng-hide | Toggle visibility (element stays in DOM) |
ng-repeat | Iterate over array |
ng-click | Handle click events |
ng-model | Two-way data binding |
ng-class |
Weekly Installs
54
Repository
GitHub Stars
59
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
claude-code50
gemini-cli49
opencode48
github-copilot48
codex48
cursor48
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
125,600 周安装
Excalidraw图表生成器 - AI驱动,自然语言描述一键生成流程图、架构图、思维导图
212 周安装
API模糊测试漏洞赏金指南:REST、SOAP、GraphQL安全测试与IDOR绕过技术
208 周安装
macOS应用设计开发指南:原生体验、UI规范与最佳实践
205 周安装
NeuroKit2:Python生理信号处理工具包,用于ECG、EEG、EDA、EMG分析
205 周安装
Nansen Trading 命令行工具:Solana 和 Base 链上代币交易与报价执行指南
207 周安装
NuxtJS Vue TypeScript 开发指南:Vue 3 组合式 API、TypeScript 与性能优化最佳实践
206 周安装
| Dynamic CSS classes |
ng-disabled | Disable form elements |