London Dev Meetup Rescheduled! Due to unavoidable reasons, the event has been moved to 21st May. Speakers remain the same—any changes will be communicated. Seats are limited—register here to secure your spot!

Dojo widget/Dijit best approach to set and get the value

Vote:
 

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:

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,
	allowedTypes: this.params.allowedTypes|| [],
	allowedDndTypes: this.params.allowedDndTypes || []
});

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:

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("keyproperty.KeyPropertyEditor", [_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,
				allowedTypes: this.params.allowedTypes|| [],
				allowedDndTypes: this.params.allowedDndTypes || []
			});

			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) {
					var item = {
						Property: widgets[0].value || "",
						IsHighPerformance: widgets[1].checked || false,
						Rating: widgets[2].value || 0
					}

					val.push(item);
				}
			}

			return val;
		}
	});
});

Thanks!

#119454
Mar 28, 2015 0:42
Vote:
 

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.
#119456
Mar 28, 2015 0:54
Vote:
 

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

#120319
Apr 15, 2015 10:20
Vote:
 

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

#120399
Edited, Apr 16, 2015 8:55
Vote:
 

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

#120404
Apr 16, 2015 10:07
Vote:
 

Thanks Linh!

#120436
Apr 16, 2015 18:55
Vote:
 

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

#123300
Jul 01, 2015 10:33
Vote:
 

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.

#123301
Jul 01, 2015 10:37
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.