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.