Presenter CS Component with Validation

I have a presenter in use against a field that also needs fragment validator like responses too.

I can see a provided validate method but it just never gets called.

So, 2 questions …

  • Does this method only work with Presenter types and not Presenter (Static) types
  • Is it possible to know which type of component you have once its been created

For this second one there appears to be no way in the UI to see.

Thanks

or is the validate method something I have to trigger / enable either with some config or by calling it in code from the frontend ?

Haven’t found anything that works as yet …

I’m pretty certain someone at Lancashire has gotten this working but i’m not sure who or in what code i’m afraid. I’ll ask around and see what I can find.

So we have one presenter that is to enforce a word count on text fields. Here is the main.js content from that:

/* Main server-side presenter code */
const DEFAULT_COUNTER_LABEL = 'Count:';
const DEFAULT_COUNT_LIMIT = 500;
const DEFAULT_COUNT_TYPE = 'word';
const DEFAULT_TEXT_ALIGN = 'left';
const DEFAULT_ERROR_MESSAGE = 'Limit exceeded.';
const WARNING_COLOR = 'rgb(197, 50, 50)';
const LABEL_STYLE_BASE = {
	fontWeight: '600',
	fontSize: 'smaller'
};
let error_message = '';
const type = presenter.get_data_type()['base_format']
cs.log(type)

return {
	get_template_data: function () {
		error_message = presenter.get_setting('default_error_message') || DEFAULT_ERROR_MESSAGE;

		let starting_content = presenter.get_editable_value();

		cs.log(`Stargin content = ${starting_content}`);

		return {
			current_content: starting_content,
			// current_content: presenter.get_editable_value(),
			current_count: 0,
			counter_label: presenter.get_setting('counter_label') || DEFAULT_COUNTER_LABEL,
			count_limit: presenter.get_setting('count_limit') || DEFAULT_COUNT_LIMIT,
			count_type: presenter.get_setting('count_type') || DEFAULT_COUNT_TYPE,
			label_style: LABEL_STYLE_BASE,
			label_style_base: LABEL_STYLE_BASE,
			warning_color: WARNING_COLOR,
			type: type,
		}
	},

	get_settings: function () {
		return {
			counter_label: {
				main_label: 'Counter label',
				base_format: 'string',
				default_value: DEFAULT_COUNTER_LABEL
			},
			count_limit: {
				main_label: 'Limit count',
				base_format: 'integer',
				default_value: DEFAULT_COUNT_LIMIT
			},
			count_type: {
				main_label: 'Count type',
				base_format: 'choice',
				choices: {
					'word': 'Word',
					'char': 'Character'
				},
				default_value: DEFAULT_COUNT_TYPE
			},
			default_error_message: {
				main_label: 'Error message',
				base_format: 'string',
				default_value: DEFAULT_ERROR_MESSAGE
			},
		};
	},

	// Used to validate input value before and after form submission
	// validate: function(value) { return false; }
	validate: function (value) {
		let error_message = presenter.get_setting('default_error_message') || DEFAULT_ERROR_MESSAGE;
		let current_content = value || presenter.get_displayable_value();
		let count_type = presenter.get_setting('count_type') || DEFAULT_COUNT_TYPE;
		let count_limit = presenter.get_setting('count_limit') || DEFAULT_COUNT_LIMIT;

		// True if no content yet
		if (!current_content || current_content.length == 0)
			return true;

		// True if count is not over limit
		if (count_type == 'word') {
			word_list = current_content.split(' ').filter((word) => word.length > 0);
			if (word_list.length > count_limit)
				return { valid: false, error: error_message };
		} else {
			if (current_content.length > count_limit)
				return { valid: false, error: error_message };
		}
		
		return true;
	}
}

If you’d like I can send you the zip of the entire presenter but I can’t see the validate function being explicitly called anywhere. We also have a Ticket type selector presenter for an event booking system we built. Happy to send you the ZIPs of the presenters if you think it would help.

Ali,

Thanks very much for that - looks very much like what I have to be honest but I will have another fiddle about in case.

I’m just not getting the method called at all - it could be a bug in our version of Create I suppose.

One other thing that may be relevant is that the presenter that uses that is set up using a v-model, which I belive allows for passing the value entered into the form back to main.js, so you may need to look into that. I’ve never doen anything with it so can only really guess at this point though I’m afraid.

Um … now im confused … nada in dev (like the function is not even called) - move the code to UAT and it works (its being called) … which is a bugger because I havent actually written the functionality yet and it now fails :rofl: because im returning hard coded false :man_facepalming:

Re your original questions:

  • It is available in both, although I’ve never tried it with a Static Presenter. It’s probably a little trickier to get the code to recognise your form input.
  • If you look in the config.json file, you will see mention of “vue” for Vue Presenters, and “handlebars” for Static.

Here is a comparison of that file for a new Vue vs Static presenter, before any changes…

It can be a bit trial and error trying to get everything right, but a couple of other pointers that might be useful…

You need to make sure you have bound the input_attributes to the input field, to make sure the core code can “find” the relevant input field to be validated, and attach the relevant onchange handler etc. The exact code may differ depending on the age of the blueprint used, but something like this for Vue:

<input v-model="value" v-bind="input_attributes({}, true)" />

or an older variant

<input v-bind="input_attributes({value: mats.editable_value})" />

Also worth noting the validate function is only fired if there is a value in the input, it won’t fire on empty fields.

The error message itself will be injected immediately after the input in the HTML, I think it’s always a div with class “validation-inline-error-message”. Depending on your own CSS there is a chance this could be hidden or outside a visible area etc, so also worth checking if the error is actually there but just not visible, and adjust CSS accordingly.

As for why it would work on one environment and not another, that could be fun trying to figure out :grimacing:

Thanks Bob - something for me to look out for :+1:

In terms of the validation, it feels like it might be due to my html not being 100% correct - im hoping to come across / get a fuller example I can copy at some point. Will definitely look at your points above.

It looks to me though like the validate() method only fires when the form is saved and doesn’t act like a field validator which means it might not work for me anyway.

Ive figured out one part - vue has a handly built in trim function which solves my leading and trailing spaces :grinning_face: problem.

If the input has the correct attributes it should fire on value change too, not just submission.

Here is my very basic test Vue input presenter, it was created from new in 26.1, with only these modifications…

main.htm

<div>
    <input
        v-model="value"
        v-bind="input_attributes({}, true)"
    />
</div>

main.js

/* Main server-side presenter code */

return {

    get_template_data: function() {
        return {
            value: presenter.get_editable_value()
        }
    },

    get_settings: function() {
        return {
        };
    },

    // Used to validate input value before and after form submission
    validate: function(value) {
        cs.log('validate() called with value: ' + value);
        if (!value) {
            return "Invalid value";
        } else if (value.length < 6) {
            return "Code must be at least 6 characters long";
        }
        return true;
    }
}

And in the frontend I’ve used this on a basic single line text Property, and I get this when I focus away from the field (or submit the form)…

This gets me closer !!!

I am now getting a validation of some kind - but its still not running my actual function …

Kind of looks like the html is now correct …

Try getting your code to return a string rather than false, which will be shown as the error text instead of the default “Invalid format”. That way you can tell if it definitely ran your code or not.

Maybe it’s just a logging issue, and it’s actually running fine?

Make sure “Detective logging” option is enabled on your basics tab too, should be on by default but just in case.

Tried that - no difference. Seems a bit weird to return text if false but a boolean if true and isnt all that consistent. Not sure that really matters however - ive seen other examples showing the return as would expected from a fragment validator so I think its probably fine.

Ive got other logging in the validate function so I know its not being called.

<div class="col-sm-12 font-xs-3xl">

    <div class="fragment_presenter_template_edit">

        <span>

            <input v-if="mandatory == '0'" style="display:inline; max-width:90%;" class="col-form-value col-form-value valid_string valid_max_length form-control" :name="mats.input_name" :id="mats.input_name" :data-max_length="255" :maxlength="255" type="text" v-model="value" v-bind="input_attributes({}, true)" @keydown="prevent_submit" @keyup="match(value, matching_enable, blacklist_enable)" autocomplete="off"/>

            <input v-else style="display:inline; max-width:90%;" class="col-form-value required col-form-value valid_string valid_max_length form-control" :name="mats.input_name" :id="mats.input_name" :data-max_length="255" :maxlength="255" type="text" v-model="value" v-bind="input_attributes({}, true)" @keydown="prevent_submit" @keyup="match(value, matching_enable, blacklist_enable)" autocomplete="off"/>

            <i id="checked" class="fas fa-check colour-icon-success pad-icon-left-only-10 make-opaque"></i>

        </span>

    </div>

</div>
// Used to validate input value before and after form submission

validate: function(value) 

{

    cs.log("Validate: " + value);

    let fLen = value.length;

    cs.log("Length: " + fLen);

    let fLenTrim = value.trim().length;

    cs.log("Trimmed Length: " + fLenTrim);

    let spaceCount = (value.trim().split(" ").length - 1);

    cs.log("Spaces Count: " + spaceCount);  

    let wordCount = value.trim().split(/\\s+/).length;   

    cs.log("Words Count: " + wordCount);

    // is the reported length of the field greater than the trimmed version of the string - suggests extra spaces

    if (fLen > fLenTrim) {

        cs.log("Remove Trim Spaces");

        //return { valid: false, error: "Please remove any leading or trailing whitespace characters" }

        return "Please remove any leading or trailing whitespace characters";

    }

    // warn the user if there is potential unecessary padding spaces between words

    if (spaceCount > (wordCount-1)) {

        cs.log("Remove Spaces Padding");

        //return { valid: false, error: "Please remove any unnecessary whitespace characters between words" }       

        return "Please remove any unnecessary whitespace characters between words";                     

    }       

    return true; 

}

ill strip back my html to see if makes any difference

yeah its the html construction causing the issue.

Will now play about with that !!!

specifically it looks like the classes assigned to some of my encapsulating divs - that realistically I probably dont need anyway.

Thanks again Bob - much appreciated - got there in the end !!!

Yes, it supports all the same return formats as a Fragment Validator. So the “full” return format is a JSON object where you can specify the true/false outcome and any error/warning message to show. But for simplicity it also supports simplified return formats of just a string for errors, or true/false if you don’t need to specify your own error message text…

Not included above but you could also return:

{valid:true}

for consistency with the more advanced error/warning format.