MSPO新建项目页面People Picker报错:SPClientPeoplePickerDict未定义原因?
为什么在MSPO新建项目页面的iframe中SPClientPeoplePickerDict未定义?
核心原因
- 上下文隔离限制:iframe是独立的浏览器执行上下文,主页面加载的
clientpeoplepicker.js等SharePoint组件脚本不会共享到iframe内部。你的脚本在主页面加载这些资源,但实际要操作的DOM和需要的全局对象都在iframe中,导致iframe的window里不存在SPClientPeoplePicker相关对象,自然找不到SPClientPeoplePickerDict。 - 脚本加载目标错误:原代码在主页面的
myJQueryCode中加载PeoplePicker依赖脚本,但这些脚本被加载到主页面而非iframe的上下文中,iframe无法访问这些全局方法和对象。 - DOM操作上下文混淆:你在主页面的函数中操作iframe的DOM,但初始化PeoplePicker时调用的
SPClientPeoplePicker_InitStandaloneControlWrapper是主页面的全局方法,无法在iframe的DOM元素上正确初始化组件,因此不会生成SPClientPeoplePickerDict对应的条目。
修复方案
关键修改思路
- 必须将PeoplePicker的依赖脚本注入到iframe内部的上下文,而非主页面
- 等待iframe完全加载完成后再执行DOM操作和脚本初始化
- 所有针对iframe的操作,都要使用iframe的
window和document对象,而非主页面的全局上下文
修改后的完整脚本
$(document).ready(function () { ExecuteOrDelayUntilScriptLoaded(CustomAction, "sp.js"); }); function CustomAction() { var targetFields = ['Project Sponsor']; var iframe = document.querySelector('iframe.pwa-animate-in'); // 等待iframe完全加载完成 iframe.addEventListener('load', function() { var innerWindow = iframe.contentWindow; var innerDoc = innerWindow.document; // 向iframe注入jQuery(如果未加载) if (typeof innerWindow.jQuery == 'undefined') { var s = innerDoc.createElement("script"); s.src = '//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'; s.addEventListener("load", function () { loadPeoplePickerScripts(innerWindow, innerDoc, targetFields); }, false); innerDoc.getElementsByTagName('head')[0].appendChild(s); } else { loadPeoplePickerScripts(innerWindow, innerDoc, targetFields); } }); } function loadPeoplePickerScripts(innerWindow, innerDoc, targetFields) { var $ = innerWindow.jQuery; var scriptRoot = innerWindow._spPageContextInfo.siteAbsoluteUrl + '/_layouts/15/'; // 在iframe内部加载PeoplePicker依赖脚本 $.when( $.getScript(scriptRoot + "clienttemplates.js"), $.getScript(scriptRoot + "clientforms.js"), $.getScript(scriptRoot + "clientpeoplepicker.js"), $.getScript(scriptRoot + "autofill.js") ).done(function () { renderPeoplePickers(innerWindow, innerDoc, $, targetFields); }).fail(function (message) { innerWindow.console && console.error('加载脚本失败: ' + message); }); } function renderPeoplePickers(innerWindow, innerDoc, $, targetFields) { var $innerDoc = $(innerDoc); for (var fieldIndex = 0; fieldIndex < targetFields.length; fieldIndex++) { $innerDoc.find("input[type='text'][title='" + targetFields[fieldIndex] + "']").each(function () { this.style.color = "green"; renderPeoplePicker(innerWindow, $, this); innerWindow.console && console.log('PeoplePicker已渲染: ' + targetFields[fieldIndex]); }); } } function renderPeoplePicker(innerWindow, $, targetInput) { var divPeoplePicker = innerWindow.document.createElement('div'); var idPeoplePicker = targetInput.id + '_PeoplePicker'; var targetValue = $(targetInput).attr('value'); divPeoplePicker.id = idPeoplePicker; $(targetInput).parent().append(divPeoplePicker); // 使用iframe的window上下文初始化PeoplePicker innerWindow.SPClientPeoplePicker_InitStandaloneControlWrapper(idPeoplePicker, null, { 'PrincipalAccountType': 'User', 'SearchPrincipalSource': 15, 'ResolvePrincipalSource': 15, 'AllowMultipleValues': false, 'MaximumEntitySuggestions': 50, 'Width': '360px' }); // 从iframe的全局对象中获取PeoplePicker实例 var newPeoplePicker = innerWindow.SPClientPeoplePicker.SPClientPeoplePickerDict[idPeoplePicker + '_TopSpan']; $(targetInput).hide(); if (typeof targetValue !== "undefined" && targetValue !== "") { var selectedUsers = targetValue.split(";"); for(var i=0;i<selectedUsers.length;i++) { if(selectedUsers[i] && selectedUsers[i] != "") { newPeoplePicker.AddUnresolvedUser( { Key: selectedUsers[i], DisplayText: selectedUsers[i] }, true); } } } newPeoplePicker.OnControlResolvedUserChanged = function () { if (this.TotalUserCount > 0) { var fieldVal = ''; for(var i=0;i<this.GetAllUserInfo().length;i++) { var resolvedUser = this.GetAllUserInfo()[i]; fieldVal = fieldVal + resolvedUser.DisplayText + ";"; } $('input#' + (this.TopLevelElementId.split("_PeoplePicker_TopSpan")[0])).attr('value', fieldVal); // 标记页面为脏状态(适配MSPO新建页面逻辑) if ($('input#' + (this.TopLevelElementId.split("_PeoplePicker_TopSpan")[0])).attr('origvalue') !== $('input#' + (this.TopLevelElementId.split("_PeoplePicker_TopSpan")[0])).attr('value')) { innerWindow.WPDPParts?.[0]?.IsDirty = true; } } else { $('input#' + (this.TopLevelElementId.split("_PeoplePicker_TopSpan")[0])).attr('value', ''); } } }
修改说明
- 监听iframe的
load事件,确保iframe内部DOM和基础脚本完全就绪后再执行操作 - 所有脚本注入、API调用都使用iframe的
window和document上下文,避免跨上下文访问问题 - 直接调用iframe全局对象中的
SPClientPeoplePicker_InitStandaloneControlWrapper方法初始化组件 - 从iframe的
SPClientPeoplePicker.SPClientPeoplePickerDict中获取正确的PeoplePicker实例
内容的提问来源于stack exchange,提问作者Provira
相关产品推荐
相关产品推荐

