自定义 dojo 模块 EPiServer 后 GUI 不加载
GUI doesnt load after implementation of custom dojo module EPiServer
我是 dojo 的新手,正在尝试实现自定义 dojo 模块以在 GUI 中添加禁用词以进行 EPiServer 搜索,但没有成功。
我一直在关注这个 post by Ted Gustaf
经过一些小的修改后,我设法消除了关于从 dojo.js.
向我的自定义模块发出 404 请求的所有错误。
在我的解决方案中,我有以下结构:
我名为搜索的自定义路径指向我的自定义 ClientResources 文件夹:
<?xml version="1.0" encoding="utf-8"?>
<module>
<clientResources>
<add dependency="epi-cms.widgets.base" path="Scripts/relatedcontent/commandsInitializer.js" resourceType="Script" />
</clientResources>
<clientModule initializer="commands.relatedcontent.commandsInitializer">
<moduleDependencies>
<add dependency="CMS" type="RunAfter" />
</moduleDependencies>
</clientModule>
<dojo>
<paths>
<add name="commands" path="Scripts" />
<add name="search" path="/ClientResources" />
</paths>
</dojo>
</module>
然后,当我尝试访问 SearchPage 的 GUI 时,它会卡住并永远停止运行,就像 dojo.js 抛出有关 404 请求的错误一样。
但是,我在控制台内没有任何错误,并且所有自定义文件都已通过 200 请求正确加载。
我是不是遗漏了什么明显的东西?
如果有人想知道我的其余设置是什么样子的:
The EditorDescriptor:
[EditorDescriptorRegistration(TargetType = typeof(IList<string>))]
public class StringsEditorDescriptor : EditorDescriptor
{
public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
{
ClientEditingClass = "search/editors/stringlist/Editor";
base.ModifyMetadata(metadata, attributes);
}
}
搜索页面上的属性:
[Display(GroupName = SystemTabNames.Settings, Order = 2980)]
[EditorDescriptor(EditorDescriptorType = typeof(StringsEditorDescriptor))]
public virtual IList<string> KeyWords { get; set; }
Editor.js 文件:
define([
"dojo/_base/declare",
"dojo/aspect",
"dojo/dom-construct",
"dojo/dom-attr",
"dojo/dom-style",
"dojo/_base/connect",
"dojo/_base/lang",
"dojo/query",
"dijit/_Widget",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dijit/form/Button",
"dijit/form/Select",
"dijit/form/TextBox",
"dojo/i18n!./nls/Labels",
'xstyle/css!./Template.css'
],
function (
declare,
aspect,
domConstruct,
domAttr,
domStyle,
connect,
lang,
query,
_Widget,
_TemplatedMixin,
_WidgetsInTemplateMixin,
Button,
Select,
TextBox,
Labels
) {
return declare([_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
templateString: dojo.cache("search.editors.stringlist", "Template.html"),
labels: Labels,
value: null,
_hasSelectionFactory: false,
constructor: function () {
this.inherited(arguments);
// When the property value is set, we refresh the DOM elements representing the strings in the list
aspect.after(this, '_set', lang.hitch(this, function () {
this._refreshStringElements(this.value);
}));
},
postCreate: function () {
this.inherited(arguments);
// summary: Populates the dropdown (if selection factory options are available), otherwise the textbox is displayed
if (this.selections && this.selections.length > 0) {
this._hasSelectionFactory = true;
}
if (this._hasSelectionFactory) {
for (var i = 0; i < this.selections.length; i++) {
var item = this.selections[i];
this.stringSelector.addOption({
disabled: false,
label: (item.text && item.text !== '') ? item.text : ' ',
selected: false,
value: item.value
});
}
// Only display dropdown when we have a selection factory attached
domStyle.set(this.stringTextbox.domNode, 'display', 'none');
} else {
// Only display textbox when there is no selection factory attached
domStyle.set(this.stringSelector.domNode, 'display', 'none');
}
this.stringSelector.setDisabled(this.readOnly);
this.stringTextbox.setDisabled(this.readOnly);
this.addButton.setDisabled(true); // Disable add button by default, until string is selected or entered
},
onChange: function (value) {
this.inherited(arguments);
// summary: Notifies Episerver that the property value has changed
},
_setValue: function () {
// summary: Sets the property value based on the strings added
var strings = this._getAddedStrings();
this.set("value", strings.length > 0 ? strings : null);
this._setHelpTextVisibility();
this.onChange(strings);
},
_refreshStringElements: function (strings) {
// summary: Make the list of strings match the property (widget) value
if (strings === undefined || strings === null) {
return;
}
var that = this;
strings.forEach(function (string, index, array) {
if (strings.indexOf(string) === -1) {
that._removeStringElement(string);
}
});
// Add an element for each string in the list
strings.forEach(function (string, index, array) {
var displayName = that._getStringDisplayName(string);
that._addStringElement(string, displayName);
});
this._setHelpTextVisibility();
},
_setHelpTextVisibility: function () {
// summary: Determines whether the help text, indicating that the list is empty, should be displayed
if (!this.value || this.value.length === 0) {
domStyle.set(this.helpText, 'display', 'inline');
} else {
domStyle.set(this.helpText, 'display', 'none');
}
},
_onTextboxKeyUp: function (e) {
// summary: Handles when a keyboard key is pressed in the string textbox, primarily to enable/disable the "+" button (when not using a dropdown for a selection factory)
var value = e.target.value.trim();
this.addButton.setDisabled(value.trim() === '');
},
_onTextboxKeyDown: function (e) {
// summary: Handles when a keyboard key is pressed in the string textbox, primarily to add a string when Enter is pressed (when not using a dropdown for a selection factory)
if (e.keyCode === 13) // Enter
{
e.target.blur();
this._addString(e.target.value.trim());
}
},
_selectedStringChanged: function (value) {
// summary: Handles when the selected string in the dropdown changes
if (value) {
this.addButton.setDisabled(false);
}
},
_onRemoveClick: function (e) {
// summary: Handles when a remove ("x") button is clicked
// Get the string value that was clicked
var stringValue = domAttr.get(e.srcElement, "data-value").trim();
this._removeStringElement(stringValue);
this._setValue();
},
_onAddButtonClick: function () {
// summary: Handles when the add ("+") button is clicked
if (this._hasSelectionFactory) { // Add string selected in dropdown
var selectedValue = this.stringSelector.value;
var displayName = this.stringSelector.focusNode.innerText;
if (!selectedValue) {
return;
}
this._addString(selectedValue, displayName);
} else { // Add string from textbox
var enteredValue = this.stringTextbox.value;
if (!enteredValue) {
return;
}
this._addString(enteredValue);
}
},
_getStringElements: function () {
// summary: Gets all DOM elements representing added strings
return query(".epi-categoryButton", this.valuesContainer);
},
_getAddedStrings: function () {
// summary: Gets the values of all DOM elements representing added strings
var elements = this._getStringElements();
var strings = [];
elements.forEach(function (element, index, array) {
strings.push(domAttr.get(element, 'data-value'));
});
return strings;
},
_addString: function (value, displayName) {
// summary: Adds a string to the list and updates the property value
value = value.trim();
if (!value) {
return;
}
if (!displayName) {
displayName = value;
}
this._addStringElement(value, displayName);
this.stringTextbox.set('value', ""); // Reset textbox value
this._setValue();
},
_addStringElement: function (value, displayName) {
// summary: Adds a DOM element representing a string in the list
if (!value) {
return;
}
value = value.trim();
if (value === '') {
return;
}
if (!displayName) {
displayName = value;
}
// Don't add if it's already added
if (query("div[data-value=" + value + "]", this.valuesContainer).length !== 0) {
return;
}
var containerDiv = domConstruct.create('div', { 'class': 'epi-categoryButton' });
var buttonWrapperDiv = domConstruct.create('div', { 'class': 'dijitInline epi-resourceName' });
var categoryNameDiv = domConstruct.create('div', { 'class': 'dojoxEllipsis', innerHTML: displayName });
domConstruct.place(categoryNameDiv, buttonWrapperDiv);
domConstruct.place(buttonWrapperDiv, containerDiv);
var removeButtonDiv = domConstruct.create('div', { 'class': 'epi-removeButton', innerHTML: ' ', title: Labels.clickToRemove });
var eventName = removeButtonDiv.onClick ? 'onClick' : 'onclick';
// Add attributes to make added values easy to find and remove
domAttr.set(containerDiv, 'data-value', value);
domAttr.set(removeButtonDiv, 'data-value', value);
if (!this.readOnly) {
this.connect(removeButtonDiv, eventName, lang.hitch(this, this._onRemoveClick));
domConstruct.place(removeButtonDiv, buttonWrapperDiv);
} else {
domConstruct.place(domConstruct.create("span", { innerHTML: " " }), buttonWrapperDiv);
}
domConstruct.place(containerDiv, this.valuesContainer);
},
_removeStringElement: function (value) {
// summary: Removes the DOM element, if any, representing a string in the list
if (value.trim() === '') {
return;
}
var matchingValues = query("div[data-value=" + value + "]", this.valuesContainer);
for (var i = 0; i < matchingValues.length; i++) {
domConstruct.destroy(matchingValues[i]);
}
},
_getStringDisplayName: function (string) {
// summary: Looks up a string value among the selection factory options, returning the corresponding display name if found
if (!this._hasSelectionFactory) {
return string;
}
var displayName = string;
this.selections.some(function (selection) {
if (selection.value === string) {
if (selection.text) {
displayName = selection.text;
}
return true; // Break
}
});
return displayName;
}
});
});
最后 Labels.js 文件是空的。
一个空的 i18n
文件会导致问题,因为它的(预期)内容在代码中被引用。
在Labels.js
中添加以下JSON:
define({
root: {
textboxWatermark: 'Enter a value to add',
clickToAdd: 'Click to add',
clickToRemove: 'Click to remove',
helpText: 'List is currently empty.'
}
});
我是 dojo 的新手,正在尝试实现自定义 dojo 模块以在 GUI 中添加禁用词以进行 EPiServer 搜索,但没有成功。 我一直在关注这个 post by Ted Gustaf 经过一些小的修改后,我设法消除了关于从 dojo.js.
向我的自定义模块发出 404 请求的所有错误。在我的解决方案中,我有以下结构:
我名为搜索的自定义路径指向我的自定义 ClientResources 文件夹:
<?xml version="1.0" encoding="utf-8"?>
<module>
<clientResources>
<add dependency="epi-cms.widgets.base" path="Scripts/relatedcontent/commandsInitializer.js" resourceType="Script" />
</clientResources>
<clientModule initializer="commands.relatedcontent.commandsInitializer">
<moduleDependencies>
<add dependency="CMS" type="RunAfter" />
</moduleDependencies>
</clientModule>
<dojo>
<paths>
<add name="commands" path="Scripts" />
<add name="search" path="/ClientResources" />
</paths>
</dojo>
</module>
然后,当我尝试访问 SearchPage 的 GUI 时,它会卡住并永远停止运行,就像 dojo.js 抛出有关 404 请求的错误一样。 但是,我在控制台内没有任何错误,并且所有自定义文件都已通过 200 请求正确加载。
我是不是遗漏了什么明显的东西?
如果有人想知道我的其余设置是什么样子的:
The EditorDescriptor:
[EditorDescriptorRegistration(TargetType = typeof(IList<string>))]
public class StringsEditorDescriptor : EditorDescriptor
{
public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
{
ClientEditingClass = "search/editors/stringlist/Editor";
base.ModifyMetadata(metadata, attributes);
}
}
搜索页面上的属性:
[Display(GroupName = SystemTabNames.Settings, Order = 2980)]
[EditorDescriptor(EditorDescriptorType = typeof(StringsEditorDescriptor))]
public virtual IList<string> KeyWords { get; set; }
Editor.js 文件:
define([
"dojo/_base/declare",
"dojo/aspect",
"dojo/dom-construct",
"dojo/dom-attr",
"dojo/dom-style",
"dojo/_base/connect",
"dojo/_base/lang",
"dojo/query",
"dijit/_Widget",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dijit/form/Button",
"dijit/form/Select",
"dijit/form/TextBox",
"dojo/i18n!./nls/Labels",
'xstyle/css!./Template.css'
],
function (
declare,
aspect,
domConstruct,
domAttr,
domStyle,
connect,
lang,
query,
_Widget,
_TemplatedMixin,
_WidgetsInTemplateMixin,
Button,
Select,
TextBox,
Labels
) {
return declare([_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
templateString: dojo.cache("search.editors.stringlist", "Template.html"),
labels: Labels,
value: null,
_hasSelectionFactory: false,
constructor: function () {
this.inherited(arguments);
// When the property value is set, we refresh the DOM elements representing the strings in the list
aspect.after(this, '_set', lang.hitch(this, function () {
this._refreshStringElements(this.value);
}));
},
postCreate: function () {
this.inherited(arguments);
// summary: Populates the dropdown (if selection factory options are available), otherwise the textbox is displayed
if (this.selections && this.selections.length > 0) {
this._hasSelectionFactory = true;
}
if (this._hasSelectionFactory) {
for (var i = 0; i < this.selections.length; i++) {
var item = this.selections[i];
this.stringSelector.addOption({
disabled: false,
label: (item.text && item.text !== '') ? item.text : ' ',
selected: false,
value: item.value
});
}
// Only display dropdown when we have a selection factory attached
domStyle.set(this.stringTextbox.domNode, 'display', 'none');
} else {
// Only display textbox when there is no selection factory attached
domStyle.set(this.stringSelector.domNode, 'display', 'none');
}
this.stringSelector.setDisabled(this.readOnly);
this.stringTextbox.setDisabled(this.readOnly);
this.addButton.setDisabled(true); // Disable add button by default, until string is selected or entered
},
onChange: function (value) {
this.inherited(arguments);
// summary: Notifies Episerver that the property value has changed
},
_setValue: function () {
// summary: Sets the property value based on the strings added
var strings = this._getAddedStrings();
this.set("value", strings.length > 0 ? strings : null);
this._setHelpTextVisibility();
this.onChange(strings);
},
_refreshStringElements: function (strings) {
// summary: Make the list of strings match the property (widget) value
if (strings === undefined || strings === null) {
return;
}
var that = this;
strings.forEach(function (string, index, array) {
if (strings.indexOf(string) === -1) {
that._removeStringElement(string);
}
});
// Add an element for each string in the list
strings.forEach(function (string, index, array) {
var displayName = that._getStringDisplayName(string);
that._addStringElement(string, displayName);
});
this._setHelpTextVisibility();
},
_setHelpTextVisibility: function () {
// summary: Determines whether the help text, indicating that the list is empty, should be displayed
if (!this.value || this.value.length === 0) {
domStyle.set(this.helpText, 'display', 'inline');
} else {
domStyle.set(this.helpText, 'display', 'none');
}
},
_onTextboxKeyUp: function (e) {
// summary: Handles when a keyboard key is pressed in the string textbox, primarily to enable/disable the "+" button (when not using a dropdown for a selection factory)
var value = e.target.value.trim();
this.addButton.setDisabled(value.trim() === '');
},
_onTextboxKeyDown: function (e) {
// summary: Handles when a keyboard key is pressed in the string textbox, primarily to add a string when Enter is pressed (when not using a dropdown for a selection factory)
if (e.keyCode === 13) // Enter
{
e.target.blur();
this._addString(e.target.value.trim());
}
},
_selectedStringChanged: function (value) {
// summary: Handles when the selected string in the dropdown changes
if (value) {
this.addButton.setDisabled(false);
}
},
_onRemoveClick: function (e) {
// summary: Handles when a remove ("x") button is clicked
// Get the string value that was clicked
var stringValue = domAttr.get(e.srcElement, "data-value").trim();
this._removeStringElement(stringValue);
this._setValue();
},
_onAddButtonClick: function () {
// summary: Handles when the add ("+") button is clicked
if (this._hasSelectionFactory) { // Add string selected in dropdown
var selectedValue = this.stringSelector.value;
var displayName = this.stringSelector.focusNode.innerText;
if (!selectedValue) {
return;
}
this._addString(selectedValue, displayName);
} else { // Add string from textbox
var enteredValue = this.stringTextbox.value;
if (!enteredValue) {
return;
}
this._addString(enteredValue);
}
},
_getStringElements: function () {
// summary: Gets all DOM elements representing added strings
return query(".epi-categoryButton", this.valuesContainer);
},
_getAddedStrings: function () {
// summary: Gets the values of all DOM elements representing added strings
var elements = this._getStringElements();
var strings = [];
elements.forEach(function (element, index, array) {
strings.push(domAttr.get(element, 'data-value'));
});
return strings;
},
_addString: function (value, displayName) {
// summary: Adds a string to the list and updates the property value
value = value.trim();
if (!value) {
return;
}
if (!displayName) {
displayName = value;
}
this._addStringElement(value, displayName);
this.stringTextbox.set('value', ""); // Reset textbox value
this._setValue();
},
_addStringElement: function (value, displayName) {
// summary: Adds a DOM element representing a string in the list
if (!value) {
return;
}
value = value.trim();
if (value === '') {
return;
}
if (!displayName) {
displayName = value;
}
// Don't add if it's already added
if (query("div[data-value=" + value + "]", this.valuesContainer).length !== 0) {
return;
}
var containerDiv = domConstruct.create('div', { 'class': 'epi-categoryButton' });
var buttonWrapperDiv = domConstruct.create('div', { 'class': 'dijitInline epi-resourceName' });
var categoryNameDiv = domConstruct.create('div', { 'class': 'dojoxEllipsis', innerHTML: displayName });
domConstruct.place(categoryNameDiv, buttonWrapperDiv);
domConstruct.place(buttonWrapperDiv, containerDiv);
var removeButtonDiv = domConstruct.create('div', { 'class': 'epi-removeButton', innerHTML: ' ', title: Labels.clickToRemove });
var eventName = removeButtonDiv.onClick ? 'onClick' : 'onclick';
// Add attributes to make added values easy to find and remove
domAttr.set(containerDiv, 'data-value', value);
domAttr.set(removeButtonDiv, 'data-value', value);
if (!this.readOnly) {
this.connect(removeButtonDiv, eventName, lang.hitch(this, this._onRemoveClick));
domConstruct.place(removeButtonDiv, buttonWrapperDiv);
} else {
domConstruct.place(domConstruct.create("span", { innerHTML: " " }), buttonWrapperDiv);
}
domConstruct.place(containerDiv, this.valuesContainer);
},
_removeStringElement: function (value) {
// summary: Removes the DOM element, if any, representing a string in the list
if (value.trim() === '') {
return;
}
var matchingValues = query("div[data-value=" + value + "]", this.valuesContainer);
for (var i = 0; i < matchingValues.length; i++) {
domConstruct.destroy(matchingValues[i]);
}
},
_getStringDisplayName: function (string) {
// summary: Looks up a string value among the selection factory options, returning the corresponding display name if found
if (!this._hasSelectionFactory) {
return string;
}
var displayName = string;
this.selections.some(function (selection) {
if (selection.value === string) {
if (selection.text) {
displayName = selection.text;
}
return true; // Break
}
});
return displayName;
}
});
});
最后 Labels.js 文件是空的。
一个空的 i18n
文件会导致问题,因为它的(预期)内容在代码中被引用。
在Labels.js
中添加以下JSON:
define({
root: {
textboxWatermark: 'Enter a value to add',
clickToAdd: 'Click to add',
clickToRemove: 'Click to remove',
helpText: 'List is currently empty.'
}
});