I want to ensure that one of two form fields representing a boolean value is checked. But there is no appropriate constraint to do this. nullable: false does not work.
class Organisation {
Boolean selfInspecting
static constraints = {
selfInspecting(nullable: false)
}
}
How can I check whether one of the two fields is checked or not?
Perhaps the simplest approach is to use a form that ensures a value is picked. As such, creating a radio buttons rather than checkboxes is a better solution. It would directly represent your intent as well.
You can also check this in the Controller, e.g.
if (params.checkBox1 != 'on' && params.checkBox2 != 'on')
flash.error = 'At least one value must be checked.'
return ...
you can write your own custom validator.
something like
selfInspecting(validator: {val, obj -> /*test selfInspecting here*/})
EDIT -- in response to the other answer -- you can handle this on the form, but you should also handle it on the server.
ANOTHER EDIT -- It was suggested in a comment that you might want to validate one of two fields on your Domain class. This is also easily accomplished with a custom validator. With the signature above for the custom validator closure, the val is the value selfInspecting, and obj is the domain object instance. So you could have
{ val, obj ->
if (val == null) return false // if you want to ensure selfInspecting is not null
else return true
... or ...
// if you want to check that at least 1 of two fields is not null
def oneOrTheOther = false
if (obj.field1 != null || obj.field2 != null)
oneOrTheOther = true
return oneOrTheOther
}
Related
Basically, what I want is that the user should be able to select the dropdown and not be able to change it after making and saving the selection. So it will be a one-time entry field.
Below is a screenshot of the field I want to apply this property.
So this field has a Yes or No selection. And to make the business logic from failing I have to make it a one-time entry field only.
I looked up the form editor for possible things but couldn't find anything that would let me achieve this.
UPDATE #1
Below is my onload function:
function Form_onload() {
var formType = Xrm.Page.ui.getFormType();
var p = Xrm.Page.getAttribute("opportunityid");
--------------NEW CODE--------------------------------
if(formType ==2){ //form type 2 means the form is a saved form. form type 1 is new form.
alert(formType);
var myattribute = Xrm.Page.getAttribute("var_internal");
var myname = myattribute.getName();
if (Xrm.Page.getControl(myname) != null) {
//alert(myname);
Xrm.Page.getControl(myname).setDisabled(true);
}
}
--------------NEW CODE---------------------------
if (formType == 1 && p != null && p.getValue() != null) {
alert('Child Opportunities can only be created by clicking the Create Child Opportunity button in the Opportunity ribbon.');
window.top.close();
}
}
No code solution: I think you could use an Option set with a Yes/No option and a default of Unassigned Value. Then add that field to Field Level Security with "Allow Update" set to No.
When updating the FLS field permissions, be sure that the profile is associated with the organization "team" so that all users can see the field:
Arun already gave you an hint how to proceed, I just tried this req on one of my instance.
Create one extra field (dummy field) in my case I call it new_hasfieldbeenchanged1
This field will hold data when field is changed. Lock this field (always) and keep this field on form (but visibile =false)
Now you need 2 trigger Onload and OnSave. Below function will do your work. Let me know if this helps.
function onLoad(executionContext) {
debugger;
var formContext;
if (executionContext && executionContext.getFormContext()) {
formContext = executionContext.getFormContext();
//executionContext.getEventSource()
if (formContext.getAttribute("new_hasfieldbeenchanged1") && formContext.getAttribute("new_hasfieldbeenchanged1").getValue()!=null) {
if (formContext.getControl("new_twooptionfield")) {
formContext.getControl("new_twooptionfield").setDisabled(true);
}
}
}
}
function onSave(executionContext) {
debugger;
var formContext;
if (executionContext && executionContext.getFormContext()) {
formContext = executionContext.getFormContext();
//executionContext.getEventSource()
if(formContext.getAttribute("new_hasfieldbeenchanged1") && formContext.getAttribute("new_twooptionfield") && formContext.getAttribute("new_twooptionfield").getIsDirty()){
formContext.getAttribute("new_hasfieldbeenchanged1").setValue((new Date()).toString());
if (formContext.getControl("new_twooptionfield")) {
formContext.getControl("new_twooptionfield").setDisabled(true);
}
}
}
}
Due to environment-specific settings in my DEV, I was not able to reproduce what was suggested by #Eccountable. Although, his solution worked in other environments.
#AnkUser has a good answer as well but I was looking to shorten the code and make things as simple as possible.
My solution
I was able to handle this using Javascript on client-side. using the XRM toolbox.
In the XRM toolbox, I located the javascript for the opportunity and observed field changes in formType when the Opportunity was New and when the Opportunity was Existing. This variable (formType) was =1 when the Opportunity was New and =2 when it was Existing/Saved.
Using this piece of information I was able to leverage my Javascript as follows in Form_onload()
function Form_onload() {
if (formType == 2) {
var myattribute = Xrm.Page.getAttribute("internal");
var myname = myattribute.getName();
if (Xrm.Page.getControl(myname) != null) {
Xrm.Page.getControl(myname).setDisabled(true);
}
}
}
There’s no OOB configuration for such custom requirements, but we can apply some script logic.
Assuming this is a picklist with default null and not a two-option bool field, we can use onLoad form script to check if it has value & lock it. No need to have onChange function.
If it’s a bool field, then it’s hard to achieve. You have to track the initial value & changes made to implement the logic you want. Or through some unsupported code.
I have a list view that can be sorted, searched and filtered. From that list view the user can edit items in multiple steps. Finally after editing and reviewing the changes the user goes back to the list. Now I want the list to use the same sorting, search term and filters that the user set before and show the correct results.
How can multiple paramters (sorting, search, filter) be stored and reused when showing the list action?
Possible unsatisfactory ways that I thought of:
pass through all the needed parameters. Does work hardly if there are multiple actions involved between the two list action calls
save the parameters in the session object. This seems to require a lot of code to handle multiple parameters (check if parameter was passed to action, store new value, if parameter was not passed, get old parameter from session, handle empty string parameters):
Long longParameter
if(params.containsKey('longParameter')) {
longParameter = params.getLong('longParameter')
session.setAttribute('longParameter', longParameter)
} else {
longParameter = session.getAttribute('longParameter') as Long
params['longParameter'] = longParameter
}
If you want to make it more generic you could use an Interceptor instead.
This could perhaps be generalized like this:
class SessionParamInterceptor {
SessionParamInterceptor() {
matchAll() // You could match only controllers that are relevant.
}
static final List<String> sessionParams = ['myParam','otherParam','coolParam']
boolean before() {
sessionParams.each {
// If the request contains param, then set it in session
if (params.containsKey(it)) {
session[it] = params[it]
} else {
// Else, get the value from session (it will be null, if not present)
params[it] = session[it]
}
}
true
}
}
The static sessionParams holds the parameters you want to store/retrieve from session.
If the params contains an element from the list, it is stored in session under the same name. If not, it is taken from session (given that it exists).
In your controller, you can now just access params.getLong('theParam') like you always would. You could also use Grails parameter conversion:
def myAction(Long theParam) {
}
Lots of LOC saved.
I use the session as well. Here is a sample that you may adapt to your needs:
def list() {
if (request.method == 'GET' && !request.queryString) {
if (session[controllerName]) {
// Recall params from memory
params.putAll(session[controllerName])
}
} else {
// Save params to memory and redirect to get clean URL
session[controllerName] = extractParams(params)
redirect(action: actionName)
return
}
// Do your actions here...
}
def extractParams(params) {
def ret = [:]
params.each { entry ->
if (entry.key.startsWith("filter_") || entry.key == "max" || entry.key == "offset" || entry.key == "sort" || entry.key == "order") {
ret[entry.key] = entry.value
}
}
return ret
}
Using session is your best bet. Just save the preference when preferred. I mean, when user sorts, or filter, just save that information in the session, for that particular <controller>.<action>, before returning the page. Next time, check the session, if it has anything related to that <controller>.<action>, apply those; otherwise render the default page.
You might like to use some Interceptor for this, as suggested by sbglasius, here.
I hope you're getting my point.
I have created a custom validator in Sitecore. It executes correctly, and I can debug it and step through it.
I extended my validator from StandardValidator, which in turn extends from BaseValidator.
In my validator, I get the item:
var item = GetItem();
GetItem is a method from BaseValidator. Based on my reading, this is the standard method to get the item to be validated.
The problem: the item I get back doesn't reflect the values that have been edited in Content Editor. Instead, it appears to simply be the item from the database (so, what it looked like when it first loaded in Content Editor, without reflecting any changes that might have been made to it).
Thus, I cannot validate. To validate the data an editor might have changed in Content Editor, I need to be able to see those changes. Seeing how the item sits in the database right now doesn't help me.
I did some extended debugging, which may or may not be helpful --
I decompiled the kernel, and walked through both StandardValidator and BaseValidator.
The GetItem method from BaseValidator does what I suspect -- it gets the item from the database. But then it runs it through a method called UpdateItem which appears be intended to overlay the inputted values from Content Editor on top of the stored values (thus returning an object that accurately reflects the data which is currently sitting in the editor). This is clearly what I want.
However, UpdateItem only appears to overlay anything if a property called ControlToValidate has a value...and it never does. I've tried this validator as both a Field Validator and an Item Validator. I've initiated it by both saving an item and by tabbing off a field. I put a breakpoint in Visual Studio and a watch on ControlToValidate. One time (inexplicably) it had a value of FIELD165901047 (which corresponded to a field ID in content editor), but in all other cases, it's been null.
What this means is that UpdateItem effectively does nothing, and the item is simply returned as it currently sits in a database, which doesn't help -- I'm trying to validator values entered in Content Editor before saving to the database.
Regardless of my investigation (I think I understand UpdateItem, but I concede that I might be misinterpeting it, and I'm not accounting for potential decompilation errors), I still have this basic problem:
var item = GetItem();
That never seems to return the values from Content Editor. It returns the item directly from the database, which doesn't help me.
In my validator (a field type validator) I've used
var field = this.GetField();
to get new value of the field to validate. The same should work for a field validator. It's probably a bit different for an item validator but I've never written an item validator.
It's also a method of the BaseValidator and returns a value of the type Field. You can assign this directly to the type of the target field you want, e.g. HtmlField (Sitecore has implemented the casting operators so you can do this):
HtmlField htmlField = field;
I managed to create a work-around for this, and it seems to be working for me. I'm not that happy that I don't know why controlToValidate never has a value, but my workaround manages to get the new value that hasn't yet been saved.
Basically, I've overridden the code that runs GetItem(), and I find the new value in the page's SaveArgs as shown below. The magic happens on the line that has:
field.Value = ((((SaveArgs) ((ClientPage) page)?.CurrentPipelineArgs)?.Items[0].Fields) ?? Array.Empty<SaveArgs.SaveField>()).FirstOrDefault(x => x.ID == field.ID)?.Value ?? field.Value;
protected override Item GetItem()
{
var obj = base.GetItem();
if (obj == null || obj.Versions.Count == 0)
return null;
UpdateItem(obj);
return obj;
}
private void UpdateItem(Item item)
{
Assert.ArgumentNotNull(item, nameof(item));
using (new SecurityDisabler())
item.Editing.BeginEdit();
var page = (Page)null;
var current = HttpContext.Current;
if (current != null)
page = current.Handler as Page;
foreach (Field field in item.Fields)
{
var controlToValidate = ControlToValidate;
if (string.IsNullOrEmpty(controlToValidate) || field.ID != FieldID)
{
field.Value = ((((SaveArgs) ((ClientPage) page)?
.CurrentPipelineArgs)?.Items[0].Fields) ?? Array.Empty<SaveArgs.SaveField>()).FirstOrDefault(x => x.ID == field.ID)
?.Value ?? field.Value;
}
else
{
var str = current != null ? RuntimeValidationValues.Current[controlToValidate] : null;
if (page != null && str != null)
{
var control = page.FindControl(controlToValidate);
if (control != null)
{
if (control is IContentField contentField)
str = contentField.GetValue();
else if (ReflectionUtil.GetAttribute(control, typeof(ValidationPropertyAttribute)) is ValidationPropertyAttribute attribute)
str = ReflectionUtil.GetProperty(control, attribute.Name) as string;
}
}
if (str == null && current != null)
str = current.Request.Form[controlToValidate];
if (str != null && str != "__#!$No value$!#__")
field.Value = str;
}
}
}
I have a View Model called SignUp with the EmailAddress property set like this:
[Required]
[DuplicateEmailAddressAttribute(ErrorMessage = "This email address already exists")]
public string EmailAddress { get; set; }
and the custom validator looks like this:
public class DuplicateEmailAddressAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
PestControlContext _db = new PestControlContext();
int hash = value.ToString().GetHashCode();
if (value == null)
{
return true;
}
if (_db.Users.Where(x => x.EmailAddressHash == hash).Count() > 0)
return false;
else
return true;
}
}
The problem I'm having is that if the user leaves the email address field blank on the sign up form the application is throwing a null reference exception error (I think it's looking for "" in the database and can't find it). What I don't understand is why this isn't being handled by the Required attribute - why is it jumping straight into the custom validator?
The Required attribute would have resulted in an error being added to the model state. It will not short-circuit the execution though. The framework continues to run other validators for the simple reason that all the errors about the request need to be sent out in a single shot. Ideally, you wouldn't want the service to say something is wrong to start with and when the user re-submits the request after making a correction, the service comes back and say some other thing is wrong and so on. It will be an annoyance, I guess.
The NullReferenceException is thrown because value.ToString() is called before the check against null. As you need the hash variable only after the check, you can solve this by reordering the statements:
if (value == null)
{
return true;
}
int hash = value.ToString().GetHashCode();
In addition, you could also move the PestControlContext after the check against null and use a using statement to dispose of it properly.
As also #Baldri pointed out, each validator can add Error messages and all of them are run, even if a previous one already signaled the data to be invalid. Furthermore, I'd not rely on that the validations are run in the order that you specify when marking the property with the attributes (some frameworks implement their own attribute ordering mechanism in order to assert that the order is deterministic, e.g. priorities or preceding attributes).
Therefore, I suggest reordering the code in the custom validator is the best solution.
I have a situation where I am creating an unobtrusive validator that must validate that another field is required only if the validated field is not empty (and vice versa). The problem is that there are some edge cases where the other field does not re-validate, and I would like to force it to revalidate itself without causing an infinite loop.
My validation method looks like this:
$.validator.addMethod("jqiprequired", function (value, element, params) {
if (!this.optional(element) || (this.optional(params) && this.optional(element))) {
return true;
}
return false;
});
params is my other field (both are textboxes). If both are empty, it passes, if both have values, it passes. It only fails if only one has a value.
This works fine, except that if one field is empty, and another has a value, then you delete the value from the field with a value, the empty field is not revalidated (because it's value has not changed).
I tried doing this:
if (!this.optional(element) || (this.optional(params) && this.optional(element))) {
$('form').validate().element(params);
return true;
}
But this causes an infinite loop because each time it passes, it calls the other.
How can I cause the other field to validate, without itself calling the original field?
Instead of adding an attribute to each field, try adding a variable jqip_validating in the script where you are adding this validation method. Then, change your validation as follows:
var jqip_calledFromOtherValidator = false;
if (jqip_validating) {
jqip_validating = false;
jqip_calledFromOtherValidator = true;
}
if (!this.optional(element) || (this.optional(params) && this.optional(element))) {
if (!jqip_validating && !jqip_calledFromOtherValidator) {
jqip_validating = true;
$('form').validate().element(params);
}
return true;
}
In order for the other validator to be called, both conditions must be satisfied, and they can only be satisfied when the first validator invokes the second validator.
You can add a is_validating attribute to each fields so that, if it's on you skip the validation and if not, you set it to true, do your validation and then clear it.