Hey guys,

i am working on a project, which requires to change some fields dynamically with the conditional_fields module.
I discovered, that a field, which is added by the "manage fields" admin panel and which is "required" by default will always throw an error. Even if I say (with conditions): make this field optional, if "somefield" has "somevalue". I think this is related to the way, clientside_validation works. You write all "states / rules" inside the javascript code, am I right?

So how could this problem be solved? How can I set a (by default "required") field to "optional", which is not validated by CSV?

I hope you can follow my description. =) If not, just ask.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

kaidawai’s picture

maybe this helps. something like this toggles the required state:
.....

$(element).bind('state:required', function(event){
   if(!event.trigger) return;
   if(event.value){
      ///... add required 
   }else{
      ///..... remove required
   }
});
attiks’s picture

I assume you're using http://drupal.org/project/conditional_fields which is (for the moment) not integrated with clientside validation, but it should be possible to do.

iTiZZiMO’s picture

@attiks: Yes, i am using this module. Would be great, to be integrated into clientside validation!
@kaidawai: Thanks, I´ll give it a try!

iTiZZiMO’s picture

@attiks: How do you determine in JavaScript, which field has to be validated in case it is required? I am trying to remove the required "flag" from a field dynamically by editing Drupal.myClientsideValidation or Drupal.settings.ClientsideValidation... but this does not work. Here is my code which does not work.

$(document).bind('state:required', function(event){
  if(!event.trigger) return;
  var element = $(event.target).find('select, input'),
  elementName = $(element).attr('name'),
  clientsideValidation = Drupal.myClientsideValidation;
  if(event.value){
    ///... add required 
    clientsideValidation.forms['FORM_NAME'].rules[elementName].required = true;
    $(element).addClass('required');
  }else{
    ///..... remove required
    clientsideValidation.forms['FORM_NAME'].rules[elementName].required = false;
    $(element).removeClass('required');
  }
});
kaidawai’s picture

looking at it, you change the clientside object, but don't update the rules ....

but from top of my head, actually something like that is the key(as a /sitespecific/ solution):

 $(formhookwhateverselector).validate().settings.your.path.to.the.object.with.the.rule.required = false;

or

 $(yourelement).rules('remove', 'required');
rsp. 
 $(yourelement).rules('add', 'required');

or the like ... you know your /sitespecific/ rules, the messages and the elements to change. with that things become much simpler. then you put the whole thing in your own behavior. hth

iTiZZiMO’s picture

Hey kaidawai,

thanks for your answer, but the solutions you have given to me do not work either...
The field is still validated as required. Any other hint?

attiks’s picture

After changing the rules you have to rebind them, so remove all validation of the form and add the rules, clientside_validation does this after Ajax callbacks, I'll try to have a closer look tomorrow.

iTiZZiMO’s picture

Could you be a little more specific attiks? =)
So, if i understand you right, there is no easy way to change / remove a rule from an element?

Thank you for your help so far!

kaidawai’s picture

@xeconsulting: sorry, i'vo got no further hints :( . imo the rule needs to be removed rsp. added( "rebound" ). should do if you have got the correct element and the correct rule.
actually it could be done in a single line:

  $('#elid').rules(ev.value?'add':'remove','required');

ok, that is unreadable and ugly .... but it does work. it sets the field required according to ev.value.
so if that doesn't work you got a different check or something else is wrong. see if attiks put some additional magic in.

@ attiks: nb: i recheck this, but i see required not checked clientside, they are submitted and come back with serveside errors. but i need to recheck what i did to that form. and file a seperate issue in case )

kaidawai’s picture

@attiks, for the record: the nb was a quirks i intrudced. works as expected.

iTiZZiMO’s picture

@attiks: have you found any solution for me? kaidawais trick did not work for me...

attiks’s picture

Assigned: Unassigned » Jelle_S

@xeconsulting, you have to do something like to rebind everything

  1. Change rule inside Drupal.myClientsideValidation.forms
  2. Call Drupal.myClientsideValidation.bindForms();

I'll ask Jelle_S to have a look as well.

The 'proper' solution would be to integrate it, but I have no idea when we can do it.

attiks’s picture

Other possibility is to write a custom javascript validate function that changes the required check, see
http://docs.jquery.com/Plugins/Validation/Methods/required#dependency-ex...

iTiZZiMO’s picture

okay, thank you. I´ll give it a try!

iTiZZiMO’s picture

I figured out, that if i remove the "required" rule or set it to "false" the validate method runs for this element trough the "blacklist" rule, which returns false and this ends up in validating the field as required. Does this sounds familiar to you?

Debugging this whole thing almost blows my head. =) Some ideas? It just does not work...

Jelle_S’s picture

Aha! The element you want to "de-require" is a select element I presume? Here the blacklist validation is used (see #1585554: Validating lists (select-fields) not working correctly for more info on that)

You can disable that rule the same as you were disabling the required rule

iTiZZiMO’s picture

@Jelle_S: You f** made my day! Thank you so much!
Just had to figure out, how to re-enable the blacklist-rule =) Is it correct, if I set the rules value to ["_none"] back, if I want to have a validation?

My code does something like this:

// STATE: has to be required
clientsideValidation.forms['FORM_NAME'].rules[elementName].required = true;
clientsideValidation.forms['FORM_NAME'].rules[elementName].blacklist = ["_none"];
// STATE: has to be optional
clientsideValidation.forms['FORM_NAME'].rules[elementName].required = false;
clientsideValidation.forms['FORM_NAME'].rules[elementName].blacklist = [""];

Is this the way, you ment?

Thank you for your help! Your module is just awesome and the support is just fantastic!

Jelle_S’s picture

Is it correct, if I set the rules value to ["_none"] back, if I want to have a validation?

That should work in theory... Haven't tested it though

Let me know if it works, so I can close this issue (or try to help you further)

iTiZZiMO’s picture

Hey Jelle_S,

the way you told me is working, with one exception.
The field is by default "required", but not visible through conditional_fields.

I try to describe my scenario:

Field 1: Select-field with "Yes" and "No" as options
Field 2: Depends on Field 1 and is by default "required", but hidden by conditional_fields

Visiting the page for the first time:

Selecting Field 1['No'] should make Field 2 "optional" and it should stay hidden. The only thing here that works is, that the field stays hidden.
If I want to submit my form, clientside_validation scrolls to the hidden fields position.

If I define Field 2 per default as "optional", the whole conditional_field mechanism does not work.

Can you imagine, if this is a bug in clientside_validation?

This scenario works -->

Visiting the page for the first time:

Selecting Field 1['Yes'] -> Field 2 gets visible and stays "required"
Selecting Field 1['No'] -> Field 2 gets invisible and "optional"

But I think this is not the way, a customer wants to handle a form =)

This code does not get invoked in scenario 1 at the first time:

$(document).bind('state:required', function(event) {
  // reconfiguring rules of triggered element
}

Maybe someone can help me with this problem.

kaidawai’s picture

hmm needs initializing at correct time...
untested from top of my head:

 (function($){
     Drupal.behaviors.myBehavior = {};
     Drupal.behaviors.myBehavior.attach = function(context){
          //if(context != document) return; //only if you need to avoid multiple execution
          $('#myelement').rules('remove');
          ......
          $(elem).bind('state:required',function(event){
             ....
          });
     } 
 })(jQuery);
kaidawai’s picture

oh you can be even trickier(if you want)

(function($){
  if (Drupal.behaviors.clientsideValidation){
      var orgattach = Drupal.behaviors.clientsideValidation.attach;
      Drupal.behaviors.clientsideValidation.attach = function(context){
              //do what you like prior to clientsideValidation,
              orgattach(context);
              // do what you like after clientsideValidation,
      }
  } 
})(jQuery);

that of course needs to be loaded after clientside validation is loaded!

iTiZZiMO’s picture

@kaidawai: Your solution (second) works, thank you! But now I have to find a way, to make this initialization more dynamic, because I have more than one case where the field is by default "required" but hidden. I don´t want to hardcode them all. =)

kaidawai’s picture

hi,
actually the first aproach is more stable, if you get it running rather use that. chaining(2nd) e.g. might need some guarding against duplicate execution(ajax!) and races or gets chained multiple times or the like...

for the dynamic part: well that works essentially the same way. just take an array of elements and/or use event bubbling ... difference is only how to grab the selectors, but boils down to the same key functions. good luck!

Jelle_S’s picture

FileSize
17.14 KB

I just did some tests with the conditional fields module. Am I correct in saying that the conditional fields module doesn't provide any validation by itself, but just the #states?

I took a quick glance at their code and it seems like they try to validate, but it isn't triggered when I try it (clientside validation disabled).
My dependencies:
Selection_033.png

It's quite hard to support a module that doesn't have a stable version.
I could see what I can do for the D6 version (which does have a stable release), but since you opened this issue for the 7.x version I'm assuming you need that more?

attiks’s picture

Status: Active » Postponed

Let's wait for a (more) stable version for Drupal 7

akamaus’s picture

I'm trying to make clientside_validation work with '#states' form api attributes. After reading the thread, I tried the following snippet in firebug:

Drupal.myClientsideValidation.forms['FORM_NAME'].rules[elementName].required = true;
Drupal.myClientsideValidation.bindForms();

Looks like it works, I get the error on submitting the form if the field is empty, but the message reads: "This field is required."

How it's supposed to be customized?

Also, if I understand correctly, this issue is postponed until conditional fields module stabilizes. But states mechanism I'm using is a part of the Form API. Maybe the problem is better to be solved on this level?

Jelle_S’s picture

A new submodule has been added (Clientside Validation States) that supports Form API's #states (only optional and required, since those are the only states we can add validation to).

attiks’s picture

Status: Postponed » Active
akamaus’s picture

Wow! That was quick. I'm still to fix a bug in my own custom solution :)

While trying to test new module, I've run into troubles. It would be rather typical issue, I guess, so I post here.

I added states attribute to a field group using a hook_group_build_pre_render_alter in my custom module. After enabling Clientside Validation States nothing changed. Even clientside_validation_states.js was not included on a page.

Looks like it has to do with an order of calling hooks. Mine hook altering the form is called after clientside_validation_form_alter which seems to be responsible to enabling the js behavior. I looked into field_group code, the process is started in hook_field_attach_form which sets '#pre_render' and it's handler calls mine hook_group_build_pre_render_alter.

Can you please advice what to do in that situation?

Jelle_S’s picture

Can you provide me example code (the way you're adding the states), so I can debug this?

Jelle_S’s picture

Status: Active » Postponed (maintainer needs more info)
akamaus’s picture

Status: Postponed (maintainer needs more info) » Active

Hi! My actual snippet was:

function westppu_custom_field_group_build_pre_render_alter(& $element) {
  // only for groups inside field_collection customer_info
  if ($element['#bundle'] != "field_customer_info")
    return;
  // customer type switch
  foreach (array('corporate', 'individual') as $status) {
    $group = & $element["group_" . $status];

    // group visibility
    $group['#states'] = array(
      'visible' => array(
        '#edit-field-customer-info-und-0-field-legal-status-und' => array('value' => $status),
      )
    );

    // required flag
    foreach ($group as $key => & $value) {
      if (preg_match('/^field_/', $key)) {
        $value['#states'] = array(
          'required' => array(
            '#edit-field-customer-info-und-0-field-legal-status-und' => array('value' => $status),
          )
        );
      }
    }
  }
}

It was a field collection with one select box and two field groups inside. Select box controlled the visibility of groups and required flag of an individual fields inside groups.

After some thinking I decided the problem is on the field_group's side and abandoned it. But still it's interesting to find a way to use field groups with states together with clientside validation.

rv0’s picture

I'm running into the similar issue as @akamaus using field_collection

And apparently I'm not the only one, someone goes into more detail here:
http://drupal.stackexchange.com/questions/40285/how-fix-an-inverted-exec...

I've been trying some workarounds, but no luck so far.

Jelle_S’s picture

FileSize
36.09 KB
34.33 KB

@akamaus, @rv0: The easiest thing to do is to add the states in hook_form_alter() or in hook_form_FORM_ID_alter().

I created a similar setup as #32 based on the given code and got Clientside Validation to work on fields within a group, within a field collection.

Here's my code:

/**
 * Implements hook_form_FORM_ID_alter().
 */
function fgtest_form_article_node_form_alter(&$form, $form_state) {
  $group = 'group_customgroup';
  $collection_bundle = 'field_my_collection';
  $fields = array_keys($form[$collection_bundle]['und'][0]['#group_children'], $group);
  foreach (element_children($form[$collection_bundle]['und']) as $delta) {
    $collection = &$form[$collection_bundle]['und'][$delta];
    foreach ($fields as $field) {
      $collection[$field]['und'][0]['value']['#states'] = array(
        'required' => array(
          "#edit-field-my-collection-und-{$delta}-field-make-required-und" => array('checked' => TRUE),
        ),
      );
    }
  }
}

And here's my setup:
Node Article:
Selection_006.png

Field Collection field_my_collection:
Selection_007.png

rv0’s picture

Hi Jelle

So basically what you are saying is we should try proposing a patch for conditional_fields module so that the adding of the states gets moved from conditional_fields_form_after_build to an implementation of hook_form_alter, most likely also with a needed increase of module weight?

Jelle_S’s picture

No, this was just an example of how you could achieve what @akamaus was trying to do with the field_groups module.

I think we should be able to support the conditional fields module without having to patch it, because they do everything in a form_after_build, and so do we... I just need to figure out how ;-)

rv0’s picture

Hm I was partially wrong in my explanation, the site I was working on was using field_collection module, not field_groups.
I'm not sure it matters that much here.

Custom coding the states in a form_alter is not an option. It's a really complex form with hundreds of fields and needs to be client-configurable.

I spent quite a few hours today trying all sorts of workarounds and different approaches, but no luck so far.

I'm now experimenting with a hacked version of states.js thrown in with hook_js_alter to have some sort of quick fix,
but a better solution would be nice

Jelle_S’s picture

Status: Active » Postponed

I had another look today but this will take a significant amount of time to develop. So if or when I find the time to do this, I'll have a look at it.

astanley86’s picture

Is this feature going to be added? I would love to see this module work with conditional_fields module.

  • Jelle_S committed 7613f0d on 7.x-2.x
    Issue #1619462 by xeconsulting: Added Compatibility problem with...
kyuubi’s picture

Issue summary: View changes

Hi guys,

I am having the same issue with conditional fields.

Basically I'm setting a field to be required with conditional fields, but it's not picked up by clientsidevalidation.

Any workaround that you suggest?

Thanks,