Dojo widget/Dijit custom validation

Vote:
 

Hi,

I'm trying to implement my own validation in a widget, i.e. overriding isValid method. The method returns correct value, but even if the value is fale it's possible to save the page and no warnings are shown. I've decorated the property with [Required], so there is a validation message and required parameter passed to the editor.

I'm extending a string property and showing my own textarea, which has no initial value. But I guess the widget still has a value, and that's why it's possible to save the page even though my isValid method returns false?

I have tried to set the widget's value to null in various methods and events with no luck.

Do I have to create a custom property type as well and reset the value from code instead, and not just have a custom editor?

#121632
May 15, 2015 22:56
Vote:
 

Hi Johan,

The isValid method implemented in the widget

 isValid: function() {
     return false;
 },

should work. I checked this scenario on one of my custom widgets. Changing the value always execute the error popup. 

You could try to debug javascript and see if this method is executed.

But if you don't find a way how to solve this on client site, you could always use custom server validator

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class MyWidgetRequiredValidationAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        return string.IsNullOrEmpty(value as string) == false;
    }
}

or try to use Required attribute, but with empty values blocked (AllowEmptyStrings):

 [Required(AllowEmptyStrings = false)]
 public virtual string LongString { get; set; }
#121686
May 17, 2015 21:28
Vote:
 

Hi,

Yes isValid is called every time and false is returned if the value in my textarea is empty. Setting AllowEmptyStrings to false did no difference. I actually think the default value is false.

Custom validation attribute didn't work either. I think the problem is that the widget already has the value, sent from backend. I need to do my own custom backing type and return an empty string to the frontend. Or if there is a way to set the value to null in my widget (which I've already tried in various methods and events)?

#121886
May 19, 2015 17:08
Vote:
 

Btw, this is the widget:

define([
	"dojo/_base/declare",
	"dojo/_base/lang",
	"dijit/registry",

	"dijit/_Widget",
	"dijit/_Container",

	"dijit/form/Textarea",
	"dijit/layout/ContentPane",
	"dijit/TitlePane",

	"epi-cms/_ContentContextMixin",
	"epi/dependency",

	"epi-cms/core/ContentReference"
], function (
	declare,
	lang,
	registry,

	_Widget,
	_Container,

	Textarea,
	ContentPane,
	TitlePane,

	_ContentContextMixin,
	dependency,

	ContentReference
) {
	return declare("xxx.LogPropertyEditor", [_Widget, _Container, _ContentContextMixin], {
		contentPane: null,
		textarea: null,
		contentStore: null,
		versionStore: null,
		contentLink: null,
		postMixInProperties: function () {
			this.inherited(arguments);

			var registry = dependency.resolve("epi.storeregistry");
			this.contentStore = registry.get("epi.cms.contentdata");
			this.versionStore = registry.get("epi.cms.contentversion");

			this.contentLink = new ContentReference(this._currentContext.id);
		},
		postCreate: function () {
			var self = this;

			contentPane = new ContentPane({
				style: "width: 582px;"
			});

			textarea = new Textarea({
				style: "width: 582px;",
				required: this.params.required,
				missingMessage: this.params.missingMessage,
			});

			contentPane.addChild(textarea);

			this._getVersionList().forEach(function (version) {
				dojo.when(self.contentStore.get(version.contentLink), function (log) {
					var date = new Date(log.versionCreatedTime);

					var tp = new TitlePane({
						title: date.toLocaleString() + " " + log.versionCreatedBy,
						content: log.properties.log.replace("\n", "<br/>"),
						open: false
					});

					contentPane.addChild(tp);
				});
			});

			contentPane.placeAt(this.containerNode);
		},
		isValid: function () {
			if (this.params.required) {
				return textarea.get("value").length > 0;
			}

			return true;
		},
		_getValueAttr: function () {
			return textarea.get("value");
		},
		_getVersionList: function () {
			return this.versionStore.query({
				contentLink: this.contentLink.createVersionUnspecificReference().toString(),
			},
			{
				start: 0,
				count: 100,
				sort: [{ attribute: "savedDate", descending: true }]
			});
		},
	});
});
#121887
May 19, 2015 17:11
Vote:
 

Hi,

In line [56], [82] and [88] it should be this.textarea intead of textarea, because it's not a global scope.

You could also try to implement setValue and check what happened inside:

_setValueAttr: function (value) {
            this.textarea.set("value", value);
        },

Do you get any console errors that you could share?

#121896
May 20, 2015 9:53
Vote:
 

Adding 'this' does not work... It didn't work in any of those places. I guess you can't work with widgets in that way, referring to them in a backing field.

Anyway, I refactored the code slightly:

define([
	"dojo/_base/declare",
	"dojo/_base/lang",
	"dijit/registry",

	"dijit/_Widget",
	"dijit/_Container",

	"dijit/form/Textarea",
	"dijit/layout/ContentPane",
	"dijit/TitlePane",

	"epi-cms/_ContentContextMixin",
	"epi/dependency",

	"epi-cms/core/ContentReference"
], function (
	declare,
	lang,
	registry,

	_Widget,
	_Container,

	Textarea,
	ContentPane,
	TitlePane,

	_ContentContextMixin,
	dependency,

	ContentReference
) {
	return declare("xxx.LogPropertyEditor", [_Widget, _Container, _ContentContextMixin], {
		contentStore: null,
		versionStore: null,
		contentLink: null,
		postMixInProperties: function () {
			this.inherited(arguments);

			var registry = dependency.resolve("epi.storeregistry");
			this.contentStore = registry.get("epi.cms.contentdata");
			this.versionStore = registry.get("epi.cms.contentversion");

			this.contentLink = new ContentReference(this._currentContext.id);
		},
		postCreate: function () {
			var self = this;

			var contentPane = new ContentPane({
				style: "width: 582px; height: 300px; overflow-y: auto;"
			});

			var textarea = new Textarea({
				style: "width: 582px;",
				required: this.params.required,
				missingMessage: this.params.missingMessage,
				intermediateChanges: true,
				onChange: lang.hitch(this, "_updateValue")
			});

			contentPane.addChild(textarea);

			contentPane.placeAt(this.containerNode);

			this._getVersionList().forEach(function (version) {
				dojo.when(self.contentStore.get(version.contentLink), function (log) {
					var date = new Date(log.versionCreatedTime);

					var tp = new TitlePane({
						title: date.toLocaleString() + " " + log.versionCreatedBy,
						content: log.properties.log.replace(/(\r\n|\n|\r)/gm, "<br />"),
						open: false
					});

					contentPane.addChild(tp);
				});
			});
		},
		_getVersionList: function () {
			return this.versionStore.query({
				contentLink: this.contentLink.createVersionUnspecificReference().toString(),
			},
			{
				start: 0,
				count: 100,
				sort: [{ attribute: "savedDate", descending: true }]
			});
		},
		_updateValue: function (value) {
			this.set("value", value);
		}
	});
});

Now the validation works if I first change the value in the textarea and then remove it. But I still don't know how to validate the initial value, i.e. setting it to null so it's not possible to save the page without entering a value.

#121937
May 21, 2015 0:00
* 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.