This is the error I get when I drop a page in the content selector widget:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'EPiServer.Core.ContentReference' because the type requires a JSON string value to deserialize correctly. To fix this error either change the JSON to a JSON string value or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path '[0].Property.isPreferredLanguageAvailable', line 1, position 45.
I've checked your source code sample and found that in the case of drag and drop, the widgets[0].value (line 143 in your javascript widget) will be an object rather than an integer value. I did modified your code to make it work with drag and drop. Please see the code below:
Javascript widget:
define([ "dojo/_base/declare", "dojo/_base/lang", "dijit/registry", "dijit/_Widget", "dijit/_Container", "dijit/form/NumberTextBox", "dijit/form/CheckBox", "dijit/form/Button", "dijit/layout/ContentPane", "dojox/layout/TableContainer", "epi-cms/widget/ContentSelector", "epi/dependency" ], function ( declare, lang, registry, _Widget, _Container, NumberTextBox, CheckBox, Button, ContentPane, TableContainer, ContentSelector, dependency ) { return declare("hemso.capifast.CustomContentSelector", [_Widget, _Container], { contentPane: null, postCreate: function () { if (this.value == null) { this.value = []; } contentPane = new ContentPane({ style: "width: 450px;" }); var newBtn = new Button({ label: "Add", style: "margin: 0 0 10px 0;", iconClass: "epi-iconPlus epi-primary", onClick: lang.hitch(this, "_addRow") }); contentPane.addChild(newBtn); contentPane.placeAt(this.containerNode); if (this.value != null) { for (var i = 0; i < this.value.length; i++) { this._addRow(this.value[i]); } } }, isValid: function () { var isValid = true; var layouts = registry.findWidgets(contentPane.domNode); for (var i = 0; i < layouts.length; i++) { var widgets = registry.findWidgets(layouts[i].domNode); if (widgets.length === 4) { isValid = isValid && widgets[0].isValid(); // contentSelector isValid = isValid && widgets[2].isValid(); // ratingTextBox } } return isValid; }, _addRow: function (val) { var layout = new TableContainer({ cols: 4, showLabels: false, orientation: "vert" }); var contentRepositoryDescriptors = dependency.resolve("epi.cms.contentRepositoryDescriptors"); var contentSelector = new ContentSelector({ title: "Key property", required: true, missingMessage: "You must select a page", showSearchBox: true, searchArea: contentRepositoryDescriptors["pages"].searchArea, roots: contentRepositoryDescriptors["pages"].roots, // I did modified you js code here to make the property be able to select any content type. allowedTypes: ["episerver.core.icontentdata"], allowedDndTypes: ["episerver.core.icontentdata"] }); var hbCheckBox = new CheckBox({ label: "HP", title: "High performance" }); var ratingTextBox = new NumberTextBox({ label: "Rating", title: "Rating", style: "width: 100px;", required: true, missingMessage: "You must specify a rating", constraints: { min: 1, max: 7, places: 0 }, invalidMessage: "You must specify a range between 1 and 7" }); var removeBtn = new Button({ label: "Remove", showLabel: false, iconClass: "epi-iconTrash", onClick: lang.hitch(this, "_removeRow") }); if (val != null) { contentSelector.set("value", val.property); hbCheckBox.set("checked", val.isHighPerformance); ratingTextBox.set("value", val.rating); } layout.addChild(contentSelector); layout.addChild(hbCheckBox); layout.addChild(ratingTextBox); layout.addChild(removeBtn); contentPane.addChild(layout); }, _removeRow: function (e) { var button = registry.getEnclosingWidget(e.srcElement); var layout = button.getParent(); layout.destroyRecursive(); }, _getValueAttr: function () { var val = []; var layouts = registry.findWidgets(contentPane.domNode); for (var i = 0; i < layouts.length; i++) { var widgets = registry.findWidgets(layouts[i].domNode); if (widgets.length === 4) { // This is the main modified code var objectContentLink; if (typeof (widgets[0].value) != 'object') objectContentLink = widgets[0].value; else objectContentLink = widgets[0].value.contentLink; var item = { Property: objectContentLink || "", IsHighPerformance: widgets[1].checked || false, Rating: widgets[2].value || 0 } // End of modified code val.push(item); } } return val; } }); });
The Editor Descriptor:
using EPiServer.Core; using EPiServer.Shell.ObjectEditing.EditorDescriptors; using EPiServerSite2.Models.Properties; using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace EPiServerSite2.Business.EditorDescriptors { [EditorDescriptorRegistration(TargetType = typeof(IEnumerable<LineItem>), UIHint = "CustomContentSelector")] public class CustomContentSelectorEditorDescriptor : EditorDescriptor { public CustomContentSelectorEditorDescriptor() { ClientEditingClass = "hemso.capifast.CustomContentSelector"; } } }
The Property Class LineItem:
using EPiServer.Core; using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace EPiServerSite2.Models.Properties { public class LineItem { public string Property { get; set; } public bool IsHighPerformance { get; set; } public int Rating { get; set; } } }
The property class PropertyCustomContentSelector:
using EPiServer.Core; using EPiServer.PlugIn; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Script.Serialization; namespace EPiServerSite2.Models.Properties { [PropertyDefinitionTypePlugIn(Description = "A property for list of content reference.", DisplayName = "Multi page property")] public class PropertyCustomContentSelector : PropertyLongString { public override Type PropertyValueType { get { return typeof(IEnumerable<LineItem>); } } public override object Value { get { var value = base.Value as string; if (value == null) { return null; } var serializer = new JavaScriptSerializer(); return serializer.Deserialize(value, typeof(IEnumerable<LineItem>)); } set { if (value is IEnumerable<LineItem>) { var serializer = new JavaScriptSerializer(); base.Value = serializer.Serialize(value); } else { base.Value = value; } } } public override object SaveData(PropertyDataCollection properties) { return LongString; } } }
After you deployed the code above, to create a multi-content selector property, you can use the following declaration on page:
[Display(GroupName = SystemTabNames.Content, Order = 330)] [UIHint("CustomContentSelector")] [BackingType(typeof(PropertyCustomContentSelector))] public virtual IEnumerable<LineItem> SelectedContent { get; set; }
Thanks,
Linh Doan
I've corrected the display of drag and drop item with dndSourcePropertyName: "contentLink" in ContentSelector's constructor, and added your code to check for null widgets[0].value. The completed javascript file is below:
define([ "dojo/_base/declare", "dojo/_base/lang", "dijit/registry", "dijit/_Widget", "dijit/_Container", "dijit/form/NumberTextBox", "dijit/form/CheckBox", "dijit/form/Button", "dijit/layout/ContentPane", "dojox/layout/TableContainer", "epi-cms/widget/ContentSelector", "epi/dependency" ], function ( declare, lang, registry, _Widget, _Container, NumberTextBox, CheckBox, Button, ContentPane, TableContainer, ContentSelector, dependency ) { return declare("hemso.capifast.CustomContentSelector", [_Widget, _Container], { contentPane: null, postCreate: function () { if (this.value == null) { this.value = []; } contentPane = new ContentPane({ style: "width: 450px;" }); var newBtn = new Button({ label: "Add", style: "margin: 0 0 10px 0;", iconClass: "epi-iconPlus epi-primary", onClick: lang.hitch(this, "_addRow") }); contentPane.addChild(newBtn); contentPane.placeAt(this.containerNode); if (this.value != null) { for (var i = 0; i < this.value.length; i++) { this._addRow(this.value[i]); } } }, isValid: function () { var isValid = true; var layouts = registry.findWidgets(contentPane.domNode); for (var i = 0; i < layouts.length; i++) { var widgets = registry.findWidgets(layouts[i].domNode); if (widgets.length === 4) { isValid = isValid && widgets[0].isValid(); // contentSelector isValid = isValid && widgets[2].isValid(); // ratingTextBox } } return isValid; }, _addRow: function (val) { var layout = new TableContainer({ cols: 4, showLabels: false, orientation: "vert" }); var contentRepositoryDescriptors = dependency.resolve("epi.cms.contentRepositoryDescriptors"); var contentSelector = new ContentSelector({ title: "Key property", required: true, missingMessage: "You must select a page", showSearchBox: true, dndSourcePropertyName: "contentLink", searchArea: contentRepositoryDescriptors["pages"].searchArea, roots: contentRepositoryDescriptors["pages"].roots, // I did modified you js code here to make the property be able to select any content type. allowedTypes: ["episerver.core.icontentdata"], allowedDndTypes: ["episerver.core.icontentdata"] }); var hbCheckBox = new CheckBox({ label: "HP", title: "High performance" }); var ratingTextBox = new NumberTextBox({ label: "Rating", title: "Rating", style: "width: 100px;", required: true, missingMessage: "You must specify a rating", constraints: { min: 1, max: 7, places: 0 }, invalidMessage: "You must specify a range between 1 and 7" }); var removeBtn = new Button({ label: "Remove", showLabel: false, iconClass: "epi-iconTrash", onClick: lang.hitch(this, "_removeRow") }); if (val != null) { contentSelector.set("value", val.property); hbCheckBox.set("checked", val.isHighPerformance); ratingTextBox.set("value", val.rating); } layout.addChild(contentSelector); layout.addChild(hbCheckBox); layout.addChild(ratingTextBox); layout.addChild(removeBtn); contentPane.addChild(layout); }, _removeRow: function (e) { var button = registry.getEnclosingWidget(e.srcElement); var layout = button.getParent(); layout.destroyRecursive(); }, _getValueAttr: function () { var val = []; var layouts = registry.findWidgets(contentPane.domNode); for (var i = 0; i < layouts.length; i++) { var widgets = registry.findWidgets(layouts[i].domNode); if (widgets.length === 4) { // This is the main modified code if (widgets[0].value != null) { var objectContentLink; if (typeof (widgets[0].value) != 'object') objectContentLink = widgets[0].value; else objectContentLink = widgets[0].value.contentLink; var item = { Property: objectContentLink || "", IsHighPerformance: widgets[1].checked || false, Rating: widgets[2].value || 0 } // End of modified code val.push(item); } } } return val; } }); });
Regards,
Linh Doan
For the [AllowedTypes] attribute, from EPiServer CMS 8 we have a breaking change and the attribute now is applicable for only ContentReference and ContentArea. You can check the document here:
http://world.episerver.com/documentation/Items/Upgrading/EPiServer-CMS/8/Breaking-changes/
Regards,
Linh Doan
Hi,
There's a bug of ContentSelector in EpiServer version 8.9.0, if I just only select a page or picture then autosave doesn't trigger. This work fine in 8.0.0.
Thanks & best regards,
An Le
Hi An,
You should report that as a bug and not post it here in the thread. The product team doesn't read all the threads.
So, I've developed my first dijit (yay) and have some questions.
The widget is a composite of other widget, and it looks like this http://i.imgur.com/conw2M1.png. As you can see I'm using the epi-cms/widget/ContentSelector widget. But I'm not able to drag n' drop to work. This is the code I have for the widget:
Do I need to set something else besides allowedDndTypes?
Speaking of which, I'm using the AllowedTypes attribute and since I updated to EPiServer 8 it throws an exception! "AllowedTypesAttribute is only supported on ContentReference,ContentArea" :( I get that not all property types supports AllowedTypes, but is it necessary to throw an exception. Do I have to develop my own attribute now to be able to pass allowed types to my widget?
And what is the best way/approach to actually set and get the widget's data? Right now I'm setting the initial state of the widget in postCreate and then updating the value in _getValueAttr. It works, but is this the best approach?
This is the compelete code:
Thanks!