Dojo widget/Dijit custom validation

Member since: 2007

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
  • 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
  • Member since: 2007

    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
  • Member since: 2007

    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
  • 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
  • Member since: 2007

    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