我是dojo的新手,并试图实现一个自定义dojo模块,用于在GUI中为EPiServer搜索添加禁止的单词,但没有成功。 我一直关注这篇文章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文件为空。
答案 0 :(得分:1)
空的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.'
}
});