自定义 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 : '&nbsp',
                        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: '&nbsp;', 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: "&nbsp;" }), 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.'
    }
});