Service Portal的技术底座是AngularJS,而它的DOM工具层却大量依赖jQuery。很多从传统CMDB表单迁移过来的脚本,直接复制到Service Portal里就会失效,问题的根源往往不在语法,而在脚本运行环境和依赖加载方式的变化。要写好Portal里的客户端逻辑,就必须弄清楚jQuery、GlideAJAX和Client Script三者之间的依赖关系。

Service Portal中jQuery的加载方式与$j别名
Service Portal在页面初始化时会自动引入jQuery,但为了不与可能存在的其他库冲突,ServiceNow把它挂载在$j这个别名上,而不是常见的$。这意味着在Portal的Client Script里直接写$('#element')大概率拿不到任何东西,必须改用$j('#element')。
需要注意的另一点是脚本的运行位置。Service Portal的Client Script有Table和View两种类型,它们最终都会在浏览器端执行,但执行上下文被包进Angular的控件作用域里。如果你直接在脚本顶层写$j(document).ready(),可能遇到DOM还未渲染完成就执行的情况,因为Portal的渲染由Angular的digest循环驱动,而不是传统的DOM加载事件。更稳妥的做法是借助spUtil或者widget的controller生命周期钩子去触发逻辑。
// 错误示例:Portal中直接使用 $ 可能报 undefined
// $('#user_name').val();
// 正确示例:使用 $j 别名
api.controller = function($scope, $element, spUtil) {
var userName = $element.find('#user_name').val();
if (userName) {
console.log('当前用户输入: ' + userName);
}
};还有一个容易踩的坑是CDN引入。有些团队习惯在主题或页面里手动再引一份jQuery,导致页面上出现两个版本,$j指向旧版而插件依赖新版,出现莫名奇妙的兼容问题。Service Portal自带了完整可用的jQuery,除非有极其特殊的插件需求,否则不要再额外引入。
GlideAJAX的调用链与依赖顺序
GlideAJAX是Service Portal与服务端Script Include通信的标准通道。它本身并不依赖jQuery,但调用它的时机依赖DOM和脚本加载顺序。在传统UI中,Client Script由表单加载器保证顺序执行;而在Portal中,widget脚本、Client Script和页面脚本由Angular按依赖注入顺序实例化,如果GlideAJAX调用发生在Script Include尚未注册的阶段,就会报找不到类的错误。
典型的GlideAJAX调用分三步:创建实例、addParam传参、getCallback等待回调。服务端的Script Include必须继承AbstractAjaxProcessor,并通过getXML回调中解析answer节点拿值。下面是一个完整可用的双向示例。
// 客户端:widget controller 中发起 GlideAJAX 请求
api.controller = function($scope, spUtil) {
var ga = new GlideAjax('MyInfoUtil');
ga.addParam('sysparm_name', 'getUserDept');
ga.addParam('sysparm_user', 'abel.tuter');
ga.getXML(function(response) {
var answer = response.responseXML.documentElement.getAttribute('answer');
$scope.dept = answer;
// 触发 Angular 绑定刷新
if (!$scope.$root.$$phase) {
$scope.$apply();
}
});
};
// 服务端:Script Include
var MyInfoUtil = Class.create();
MyInfoUtil.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getUserDept: function() {
var userId = this.getParameter('sysparm_user');
var gr = new GlideRecord('sys_user');
if (gr.get(userId)) {
return gr.department.getDisplayValue();
}
return '未找到';
},
type: 'MyInfoUtil'
});依赖顺序问题的典型表现是间歇性失败:刷新快时正常,网络稍慢就回调为空。排查时可以先在浏览器Network面板确认XMLHttpProcessor.do请求是否发出、返回体里是否包含answer属性,再确认Script Include的Client callable选项是否勾选,三者缺一不可。
替代方案选型与最佳实践
在Portal环境里,GlideAJAX并非唯一选择。spUtil没有直接封装Ajax,但widget自身的数据获取可以通过server script的data对象完成,走的是服务端渲染路径,根本不需要浏览器端发请求。只有需要响应用户交互、动态刷新数据时,才值得用GlideAJAX或者Angular封装的$http。
三种方式的取舍可以这样判断:初始化时就要展示的数据,放在widget的server script里;用户操作触发的实时查询,用GlideAJAX,因为它能复用服务端权限模型和Script Include;需要与第三方REST接口交互时,用$http更直接。混用时要小心重复请求,比如server script已经查过一次,controller里又用GlideAJAX查同一份数据,会造成明显的性能浪费。
关于jQuery依赖的最后一条建议:尽量把DOM操作收敛到widget内部。Portal鼓励组件化,跨widget去操作别人的DOM,等于把隐式依赖散落到整个页面,一旦对方widget改版,你的脚本就会静默失效。如果确实需要通信,优先使用$broadcast和$on这类Angular消息机制,让依赖关系显式化,这比在两个widget之间共享一个jQuery选择器可靠得多。
总结来说,Service Portal里jQuery是基础设施而非主角,GlideAJAX是数据通道,Client Script是粘合层。理清三者的加载顺序和职责边界,脚本迁移和新建开发的稳定性都会显著提升。
ServiceNowService PortaljQuery修改时间:2026-09-14 07:34:33