0

i validate my formular with ajax and get back the following json object:

{"username":["Please enter a username"],"email":["Please enter an email"],
"plainPassword":{"first": ["Please enter a password"]},"firstname":
["This value should not be blank."],"lastname":["This value should not be blank."],
"terms":["This value should be true."],"privacy":["This value should be true."],
"captcha":["Code does not match"],"securityQuestion":["This value should not be blank."],
"plainSecurityAnswer":["This value should not be blank."],"intention":
["This value should not be blank."],"addresses":[{"state":["This value should not be blank."],
"city":["This value should not be blank."],"zipcode":["This value should not be blank."],
"country":["This value should not be blank."]}]}

The keys are mapped to the input fields id always by: var id = "fos_user_registration_form_" + key;

I want to present these errors in an efficient way as tooltips to the fields. For that, i've written the following jQuery code (where callback is the returned json object):

$.each( callback, function( key, entry ) {
    if(key != "addresses" && key != "plainPassword")
      {
         var id = "#fos_user_registration_form_" + key;
         $(id).tooltip('destroy');
         $(id).tooltip({'title': entry});
         $(id).closest('div[class="form-group"]').addClass('has-error');
      }else if(key == "addresses"){
         $.each( entry[0], function( keyAddress, entryAddress ) {
             var id = "#fos_user_registration_form_" + key + "_0_" + keyAddress;
             $(id).tooltip('destroy');
             $(id).tooltip({'title': entryAddress});
             $(id).closest('div[class="form-group"]').addClass('has-error');
         });
       }else if(key == "plainPassword")
         {
           var id= "#fos_user_registration_form_plainPassword_first,#fos_user_registration_form_plainPassword_second";
           $(id).tooltip('destroy');
           $(id).tooltip({'title': entry.first});
           $(id).closest('div[class="form-group"]').addClass('has-error');
}});

It is working, but i think not very dynamic because i know in this case that the entries of the key "addresses" and "plainPassword" aren't strings and that i have to iterate on them again (here only on addresses).

Is there a nicer way to do this by only using the key and entry variable of the loops, without knowing the "key" names of the json ?

I thought of something like: While entry !== "string", iterate as long threwthe entries as there is another array or object in it and build up the "id" variable. When there is a string field as "entry", use it as tooltip text.

Hope you guys can help me.

Regards.

nova.cp
  • 455
  • 1
  • 9
  • 22

2 Answers2

0

Recursion will do this!

eg http://repl.it/3hK/5

Code -

var id_stub = "#fos_user_registration_form_"

// Here's the recursive function - we kick it off below.
function process(thing, id) {
    var key
    for (key in thing) {
        // Handle the arrays
        if ('length' in thing[key]) {

            // Handle the end - we found a string
            if (typeof thing[key][0] == "string") { 
                var html_id = id_stub + id + key
                var err_msg = thing[key][0]

                console.log(html_id, ":", err_msg)

                // Now do your jquery using the html_id and the err_msg...
            }
            // Else we found something else, so recurse.
            else {
                var i = 0;
                while (i < thing[key].length) {
                    process(thing[key][i], key + "_" + i + "_")
                    i++
                }
            }
        }

        // Handle the objects by recursing.
        else {
            process(thing[key], key + "_")
        }
    }
}

// Start the recursion from here.
process(callback, "")

I added an extra address to test how this code handles nested addresses, and using that I get this in the console:

#fos_user_registration_form_username : Please enter a username
#fos_user_registration_form_email : Please enter an email
#fos_user_registration_form_plainPassword_first : Please enter a password
#fos_user_registration_form_firstname : This value should not be blank.
#fos_user_registration_form_lastname : This value should not be blank.
#fos_user_registration_form_terms : This value should be true.
#fos_user_registration_form_privacy : This value should be true.
#fos_user_registration_form_captcha : Code does not match
#fos_user_registration_form_securityQuestion : This value should not be blank.
#fos_user_registration_form_plainSecurityAnswer : This value should not be blank.
#fos_user_registration_form_intention : This value should not be blank.
#fos_user_registration_form_addresses_0_state : This value should not be blank.
#fos_user_registration_form_addresses_0_city : This value should not be blank.
#fos_user_registration_form_addresses_0_zipcode : This value should not be blank.
#fos_user_registration_form_addresses_0_country : This value should not be blank.
#fos_user_registration_form_addresses_1_state : This value should not be blank.
#fos_user_registration_form_addresses_1_city : This value should not be blank.
#fos_user_registration_form_addresses_1_zipcode : This value should not be blank.
#fos_user_registration_form_addresses_1_country : This value should not be blank.

and sets up the variables you need to do your jQuery work.

sifriday
  • 4,342
  • 1
  • 13
  • 24
  • Amazing! Thank you a lot sifriday! I also tried to use recursion but couldn't get a satisfying resultat. i've got one more question to your solution: My IDE (phpstorm) tells me, that accessing the key with "[key]" is not so good: "Possible iteration over unexpected members, possibly missing check hasOwnProperty". Is there missing a check? – nova.cp Nov 10 '14 at 17:31
  • the hasOwnProperty thing is useful to make sure you aren't iterating over properties you've inherited from a prototype chain. See http://stackoverflow.com/questions/3010840/loop-through-array-in-javascript ... as your original obj is under your control and comes from JSON, you can be sure that you don't need this check. Hope that helps! – sifriday Nov 10 '14 at 18:13
0
   function isValidationMessage(entry) {
      return entry.length === 1 && typeof entry[0] === 'string';
    }

function displayValidationMessage(key, message){
  var id = "#fos_user_registration_form_" + key;
             $(id).tooltip('destroy');
             $(id).tooltip({'title': message});
             $(id).closest('div[class="form-group"]').addClass('has-error');
}

function displayValidationMessageForArray(key, entries) {
   for(var i = 0; i < entries.length; i++) {
     $.each(entries[i], function(keyAddress, entryAddress) {
       displayValidationMessage(key + "_i_" + keyAddress, entryAddress);
     })
   }

}

function displayValidationMessageForObject(key, entries) {
   $.each(entries, function(entry, message) {
     displayValidationMessage(key + "_" +entry, message);
   })
}

function displayAllValidationMessages(callback) {

    $.each( callback, function( key, entry ) {
        if(isValidationMessage(entry)) {
          displayValidationMessage(key, entry);
        }else if($.isArray(entry)){
           displayValidationMessageForArray(key, entry);
        }else {
           displayValidationMessageForObject(key, entry);
        }
    });
}

not fully tested, idea is to extract the if else to small function, and reuse them as much as possible

Sean
  • 2,990
  • 1
  • 21
  • 31