Home Analyst Portal

How to dynamically/conditionally change the Required property of a ticket form field?

Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
It is simple to set a form field to be required or not, by adding Required: true to the field's properties.  But what if the field should only be required if certain criteria is met?

I have not yet discovered how to access the fields' Required property after the form is rendered, or how to inform the validation function that this property has changed.  So it is time to ask.

All too often, I receive the question "why isn't this field required?" and I must answer "it can't be, or else it would have to be filled in when the ticket is first created, and it is not known at that point in the process."

Best Answers

  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    Answer ✓
    @Cory_Bowe, I just figured it out, and you were very close with your last comment.  The DIV that encloses the entire control (label and all) also has the "required" attribute.  I missed it the first time through (blame multitasking...).

    Keeping with my example, $('input[name="RequestedWorkItem"]').parent().parent().removeAttr('required') will take care of it, after it has also been removed from the input element itself.  I have yet to test this with other controls to see if they have different placement of the attribute, which would cause me to write this differently.  The label was already taken care of near the top of this thread.

    I had to step through the Save() function on the form to figure out how it was identifying whether the field is required or not, when I noticed that it just iterates over every control that has the required attribute (using JQuery, so I knew that there was hope that altering it at runtime could still work, rather than having it determined on load).

    This has been a good brain exercise, even though the end result is so simple.

Answers

  • Brett_MoffettBrett_Moffett Cireson PACE Super IT Monkey ✭✭✭✭✭
    Hi @Tom_Hendricks
    This enters the world of custom java script that can be added to the Custom.js file in the Custom Space folder.

    You would have to add an evaluation function that would be triggered by the Lose Focus event for a control that you want to check. For example: If you wanted to make the Affect CI field required if someone chooses "Hardware fault" as the category then you would have a custom java script that would trigger when the Category drop down list "Loses Focus" (Users selects it then tabs away or clicks on another control)
    You would then have the code look for the rules you want (Category Like "Hardware *") then run the code to add the required tag to the Affected CI control.

    I'm no guru of Java Script and don't have a handy example....   Sorry.
  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    Thanks!

    @Brett_Moffett, I am decent with JS and setting up the events I need, etc., but was not sure where the property lived or how to access it--whether there was a Cireson object property to change, if it was in the Kendo control, etc.  I did not phrase my question as well as I could have, I think!

    @Cory_Bowe, I think you just cleared it up for me.  Just one follow-up question: I see that you are not declaring your element as a kendo control (e.g., $('elementselectors').data('kendoControlOfSomeKind').removeAttr('required');), so this works against your plain vanilla DOM elements, right?

    I am about to give this a whirl.  I knew that I was probably staring right at the solution and just not seeing it for whatever reason, so I greatly appreciate the nudge! :)
  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    I marked this as answered, but in my testing, this did not work.

    I can remove the "Required" attribute and change the label text, but the form will not save if the field was Required at load time.  The opposite is true too.  I can make the field "look" required, but the form will save if it is empty, as long as it wasn't required when the form loaded.

    I think we still have part of the answer--how to change the appearance of the form--but I need the behavior to change too.  If I find the answer I will post it here, but perhaps someone already knows...
  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    Let's take the affected user field on the Incident form as an example ("RequestedWorkItem" in the viewModel).  It is a Kendo Autocomplete control, which renders as a strange combination of various different tags, including the label.  There is, more importantly, an input tag inside this mess, which is where the value is stored, and can be accessed directly as $('input[name="RequestedWorkItem"]') or as the whole Kendo control like $('input[name="RequestedWorkItem"]').data('kendoAutoComplete').  It is also possible to get and set the value using boundObj.RequestedWorkItem, which will also take care of updating the viewModel for you, but since there is no "Required" property of that object, I will make no other mention of it here.

    You are correct that for some Kendo controls there is a second, hidden element somewhere on the page that is also tied to the control, such as the dropdown list that appears when you begin searching for a user in an AutoComplete control.  It is a couple DIVs with a UL list inside.  However, none of these elements have a "required" property to set or remove, like the input element does.

    @Cory_Bowe, have you been able to get this to work with a different type of control, perhaps? Or even better, with this one?  I can successfully add/remove the "required" attribute from all applicable DOM elements, but it has zero effect on whether the field is validated on save or not.  I am clearly missing something and I am hoping that it is something you have not missed.
  • Cory_BoweCory_Bowe Customer Adept IT Monkey ✭✭
    Sure. I use this to hide and remove the tech reviewer field from my change request offering:

    jQuery.techreviewdiv = $(".control-label:contains('Tech Reviewer')").parent();
    jQuery.divTechReviewRequireds = $.techreviewdiv.last().find("*").filter('[required]');
    $.techreviewdiv.hide();
    $.divTechReviewRequireds.removeAttr("required");

    I personally like to work from required to not, as its easier to just get all items tagged with required, hang them off jQuery so they persist outside of that script execution and then t
    o add them back I'd just dump required back on with:
    $.divTechReviewRequireds.attr('required', true);
  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    I am looking at this the same way.  In my custom form, I have the fields marked as Required, but there are some exceptions where they shouldn't be (e.g., a SCOM alert that applies to several hundred users across the several different countries and time zones--should I just arbitrarily pick the first user in the address book, or just make it not required?  In all other cases, it is absolutely required without question).

    This code did not work, however.  The attribute is removed, but the form still cannot be saved until a value is entered into the field.  The form validation does not see that the field is no longer required, so something has been missed somewhere.  I feel like I have looked in every possible place, but clearly I have not.
  • Cory_BoweCory_Bowe Customer Adept IT Monkey ✭✭
    If it's not needed I hide the entire div that contains that element from the page. Maybe that's what allows mine to go through?  Otherwise, if you still need to leave it as it is an option then I'm out of ideas. We need some Cireson brain power.
  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    Answer ✓
    @Cory_Bowe, I just figured it out, and you were very close with your last comment.  The DIV that encloses the entire control (label and all) also has the "required" attribute.  I missed it the first time through (blame multitasking...).

    Keeping with my example, $('input[name="RequestedWorkItem"]').parent().parent().removeAttr('required') will take care of it, after it has also been removed from the input element itself.  I have yet to test this with other controls to see if they have different placement of the attribute, which would cause me to write this differently.  The label was already taken care of near the top of this thread.

    I had to step through the Save() function on the form to figure out how it was identifying whether the field is required or not, when I noticed that it just iterates over every control that has the required attribute (using JQuery, so I knew that there was hope that altering it at runtime could still work, rather than having it determined on load).

    This has been a good brain exercise, even though the end result is so simple.
  • Alex_MarshAlex_Marsh Premier Partner Advanced IT Monkey ✭✭✭
    Is there a way of utilising this with the standard forms? I'd like to remove the required function on a change form until a specific activity has been completed but am struggling with where/how to implement the above.
  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    This is done in the custom.js file, not in the form itself.

    The example I wrote above is against a (nearly) out-of-box Incident form. 

    If you have the following code in your custom.js (make sure it only fires for the correct type of form, etc.) it will change the affected user field from required (as it is defined in my incident.js file) to optional:

    $('input[name="RequestedWorkItem"]').parent().parent().removeAttr('required');
    "RequestedWorkItem" is the name of the Affected user relationship object.  More importantly to this example, that is the property that the field is bound to, and also the value of the "name" attribute.

    If you wanted to make the assignee field conditional on completion of the first activity, that would look something like:
    if (pageForm.viewModel.Activity[0].Status.Id == '9de908a1-d8f1-477e-c6a2-62697042b8d9') {<br>    $('input[name="AssignedWorkItem"]').parent().parent().removeAttr('required');<br>}<br>
    Note that for fields that are not userpickers like my two examples, .parent().parent() in the middle of that statement may not be correct.  It all depends on how the INPUT tag is nested and/or its relationship to the DIV tag with the "required" attribute.

    There may be a more elegant way of accomplishing this that does not require counting div tags, of course.
  • Alex_MarshAlex_Marsh Premier Partner Advanced IT Monkey ✭✭✭
    So I've put this in my custom.js and set the affected user field as required but it doesn't work. What am I missing?

    app.custom.formTasks.add('Incident', null, function (formObj, viewModel) {
    formObj.boundReady(function () {
    $('input[name="RequestedWorkItem"]').parent().parent().removeAttr('required');
    }
    }
  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    Did you try saving without entering a value in that field?

    The code in this thread does not remove the "(Required)" text in the field label.  You would need to add another line to do that.  Here is an example function that puts it all together:
    // Accepts an array of strings which are the name(s) of field(s) that should have their "Required" parameter changed
    // isRequired indicates that "Required" should be added if not present (true) or removed if not present (false)
    function toggleFieldValidation(isRequired, fieldArray) {
    	// Iterate through all fields that are supplied to the function.
    	for (i = 0; i < fieldArray.length; i++) {
    		var addToLabelIfRequired = ' (Required)';
    		var field = $('[name="' + fieldArray[i] + '"]');
    		var fieldContainer = field.parent().parent();
    		var label = $('label[for="' + fieldArray[i] + '"');
     
    		// " (Required)" is always the second <span> inside of the labels, so it can be targeted as the 2nd child, if it exists
    		var reqLabel = label.children('span:nth-child(2)');
    		console.log(reqLabel);
     
    		// If the field should be required and the label does not indicate this, add the appropriate text
    		if (isRequired && reqLabel.length < 1) {
    			label.append('<span>' + addToLabelIfRequired + '</span>');
    		}
    		// If the field should not be required and the label does not indicate this, remove the (Required) text from the label
    		if (!isRequired && reqLabel.length > 0) {
    			console.log('Removing label...');
    			reqLabel.remove();
    		}
     
    		// Set the form field in accordance with isRequired
    		if (isRequired) {
    			field.prop('required', true);
    			fieldContainer.prop('required', true);
    		}
    		else {
    			field.removeAttr('required');
    			fieldContainer.removeAttr('required');
    		}
    	}
    }
    

  • Alex_MarshAlex_Marsh Premier Partner Advanced IT Monkey ✭✭✭
    Sorry my js knowledge is non-existent. How do I call the function? I've got it working outside the function but can't call the function to specify the field to target
  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    If you have this function in your custom.js file somewhere, then you can do something like the following (stealing your code example and re-writing it to use this function):

    app.custom.formTasks.add('Incident', null, function (formObj, viewModel) {<br>    formObj.boundReady(function () {<br>        toggleFieldValidation(false,'RequestedWorkItem');<br>    });<br>});


  • Alex_MarshAlex_Marsh Premier Partner Advanced IT Monkey ✭✭✭
    Sorry @Tom_Hendricks that doesn't seem to work. If I put in an alert in to check the value being applied (window.alert(fieldarray[i]) after the vars are declared at the start of the function each letter of the fieldArray parameter is returned instead of the fieldArray so it doesn't apply the required

  • Alex_MarshAlex_Marsh Premier Partner Advanced IT Monkey ✭✭✭
    I finally got something working with a bit of cobbling together of the above. Thanks for all your help so far

    What I have noticed is that the ('span:nth-child(2)' is always returning a value of 1. After examining the incident form it seems that the <span></span> is always there whether or not the field is required. Does anyone know of a way to get the value of the field and to only remove if the contents contain 'is required'?
  • Alex_MarshAlex_Marsh Premier Partner Advanced IT Monkey ✭✭✭
    edited June 2017

    with some help from @Konstantin_Slavin-Bo and @Tom_Hendricks I've managed to sort the following:

    <p>app.custom.formTasks.add('Incident', null, function (formObj, viewModel) {<br>formObj.boundReady(function () {<br>var vm = pageForm.viewModel<br>var fields = ["Description","Classification","RequestedWorkItem"];<br>var addToLabelIfRequired = ' (Required)';<br>if (vm.Priority <= 3){<br>for (i = 0; i < fields.length; i++){<br>var label = $('label[for="' + fields[i] + '"');<br>var reqlabel = $('label[for="' + fields[i] +'"] > span:eq(1)');<br>$('[data-role="' + fields[i] + '"]').prop('required', true);<br>$('[name="' + fields[i] + '"]').prop('required', true);<br>$('.control-label[for="' + fields[i] + '"]').parent().parent().find('input').attr("required", true);<br>$('.control-label[for="' + fields[i] + '"]').parent().parent().find('a').attr("required", true);<br>if (reqlabel.is(":empty") == true){<br>reqlabel.html('<span>'+ addToLabelIfRequired + '</span>');<br><span>}</span><br>}<br>}<br>}); });</p><p>//enable/disable fields on priority value change</p><p>app.custom.formTasks.add('Incident', null, function (formObj, viewModel) {<br>formObj.boundChange("Priority",function (formObj, viewModel) {<br>var vm = pageForm.viewModel<br>var fields = ["Description","RequestedWorkItem","Classification"];<br>var addToLabelIfRequired = ' (Required)';<br>if (vm.Priority <= 3){<br>for (i = 0; i < fields.length; i++){<br>var label = $('label[for="' + fields[i] + '"');<br>var reqlabel = $('label[for="' + fields[i] +'"] > span:eq(1)');<br>$('[data-role="' + fields[i] + '"]').prop('required', true);<br>$('[name="' + fields[i] + '"]').prop('required', true);<br>$('.control-label[for="' + fields[i] + '"]').parent().parent().find('input').attr("required", true);<br>$('.control-label[for="' + fields[i] + '"]').parent().parent().find('a').attr("required", true);<br>if (reqlabel.is(":empty") == true){<br>reqlabel.html('<span>'+ addToLabelIfRequired + '</span>');<br>}<br>}<br>} else {<br>for (i = 0; i < fields.length; i++){<br>var label = $('label[for="' + fields[i] + '"');<br>var reqlabel = $('label[for="' + fields[i] +'"] > span:eq(1)');<br>$('[name="' + fields[i] + '"]').parent().parent().removeAttr('required');<br>$('[name="' + fields[i] + '"]').removeAttr('required');<br>$('.control-label[for="' + fields[i] + '"]').parent().parent().find('input').attr("required", false);<br>$('.control-label[for="' + fields[i] + '"]').parent().parent().find('a').attr("required", false);<br>if (reqlabel.is(":empty") == false){<br>reqlabel.html('<span></span>');<br>}&nbsp;<br>}<br>}</p><p>}); });</p>

    This will enable the specified fields when loading the incident form if the priority <=3 and remove them if not. If the form loads with a priority < 3 and it gets changed the specified fields will enable.

    As mentioned before my JS is pretty ropey and the adding " (Required)" has been a bit of a fudge seeing as the class is now there permanently regardless of whether the value is there or not, meaning I've had to go down another child layer. I'll have to go through the various scripts to see how the (Required) gets added to the second child as this will duplicate the span if a field is already set to required

  • Alex_MarshAlex_Marsh Premier Partner Advanced IT Monkey ✭✭✭
    Edited my original with a fix to the child span I pointed out
Sign In or Register to comment.