Carlos Robles frikiblog

Form dentro de otro form

| 0 comments

Hay veces en las que puedes necesitar tener un formulario dentro de otro, algo de este tipo

<form action="a">
    <input.../>
    <form action="b">
        <input.../>
        <input.../>
        <input.../>
    </form>
    <input.../>
</form>

por ejemplo porque el formulario interior es una imagen, que envía por ajax, mientras que el resto es un formulario normal.

Lo primero que hay que saber es que esto no se puede hacer. Anidar forms está especificamente prohibido, como podemos ver en la especicificacion XHTML oficial de W3C, Section B. “Element Prohibitions”, states that:

 “form must not contain other form elements.” 
http://www.w3.org/TR/xhtml1/#prohibitions

Dependiendo del navegador, el resultado será uno u otro. Lo mas probable es que nos descarte la apertura del form interno, y su etiqueta de cierre, nos este cerrando en realidad el form princial.

Lo primero que se nos puede ocurrir,  es sacar el formulario fuera, y simplemente posicionarlo donde queremos con css .
Esto en muchos casos será una solucion, aunque no ideal, al menos válida.

Pero nos encontraremos algun escenario en que necesitamos que los elementos del form interior esten realmente dentro del primer form,  por ejemplo porque tenemos un sistema de pestañas , en el que mediante JS creemos tabs a partir de las distintas divisiones. si nuestro formulario está fuera, por mucho que con CSS lo coloquemos dentro, estará fuera de los tabs y el resultado no será el esperado.

Se nos podria ocurrir otro rodeo, como por ejemplo anidarlo de forma que el segundo form quede fuera del primero, pero dentro de la divisiion adecuada, algo del tipo,  pero tampoco es correcto 

<div class="tabs">
<div class="tab1"><form action="b">
</form><form action="a"></form></div>
<div class="tab2"></div>
</div>

Con esto las tabs funcionarán correctamente, pero el formulario a, definido dentro de la division tab1, debería cerrarse tambien dentro de tab1. por tanto volvemos a un HTML no válido.

En fin, no tenemos más remedio que dejar el formulario interno totalmente fuera.

Esto nos deja unas cuantas posibilidades.

  1. Podemos  usar el atributo form de los elementos input, algo nuevo en HTML5, que no funcionará en todos los navegadores
    <form id="fileuploads" action="http://yo.ur/target/" method="POST"></form>
    <form id="mainform">
       <div id="first_tab">
       </div>
       <div id="second_tab">
       </div>
       <div id="fileuploads">
            <input type="text" name="desc" form="fileuploads">
            <input type="file" name="file" form="fileuploads">
            <input type="submit" value="Submit" form="fileuploads">
       </div>
    </form>
    
  2. Algo parecido, podemos dejar solo los campos, y en el momento del submit, desabilitamos el externo,  creamos un formulario en el aire que envuelva los campos internos, y lo enviamos con  $(form_id).submit o document.getElementById(form_id).submit
    pongo este ejemplo de código que he visto en http://stackoverflow.com/questions/3430214/form-inside-a-form-is-that-alright

     $('#div_form').wrap('<form id="form2" action="/la/acion/" method="post" target="_blank"></form>')
    

    con esto podremos trabajar bien con cualquier tipo de elemento, pero no es del todo limpio y puede que nos falle en algun naegador.

  3. Si solo tenemos campos de texto, podemos crear el formulario interno totalmente en el aire. Es decir, no existe su etiqueta form. Ponemos los campos dentro del externo, y calculamos el input del usuario en variables js que despues enviaremos tambien por javascript, por ajax o con un post. Podemos ver un ejemplo completo y muy bien explicado aqui:
    http://blog.avirtualhome.com/how-to-create-nested-forms/
    básicamente, el código seria algo como :

    <html>
    <head>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript"><!--
    jQuery(document).ready(function() {
    jQuery('#search').click( submit_search );
    jQuery('#nestedform).find('input').keydown(keypressed);
    }
    );
    function submit_search( event ) {
    var values = new Array;
    event.preventDefault;
    values[0] = jQuery('#item').attr('value');
    if (values[0]) {
    do_submit('#output',values);
    } else {
    alert ('No Item given');
    }
    return false;
    }
    
    function keypressed( event ) {
    var charcode = (event.which) ? event.which : window.event.keyCode ;
    if ( charcode == 13 ) {
    return submit_search( event );
    }
    return true;
    }
    function do_submit(submit_output, submit_values) {
    jQuery(submit_output).hide();
    jQuery.post(
    "search.php",
    { action: 'search_values[]': submit_values },
    function(data, textStatus) {
    jQuery(submit_output).html(data.substr(0,data.length));
    });
    jQuery(submit_output).show();
    }
    );
    }
    // --></script>
    </head>
    <body>
    <form method="post" action="post.php">
    <textarea rows="5" cols="20" wrap="physical" name="post">
    <input type="submit" value="Post">
    <div id="nestedform">
    Item: <input type="text" size="10" id="text" name="item">
    <input type="button" id="search" value="Search">
    <div id="output"></div>
    </div>
    </form>
    </body>
    </html>
    

    Esto nos dará problemas con inputs complejos, como por ejemplo los de type=file

  4. Y llegados aquí tenemos 3 opciones, pero ninguna acaba de ser completa. Después de este vistazo rápido, y de ver que no encontraba ninguna solución, me he visto en la obligación de desarrollar la primera solución que me vino a la mente, pero que me daba mucha pereza.  Es una solucion similar a la 3, pero distinta, y más completa: Se trata de crear el formulario fuera, es decir, hay un formulario fisicamente, y ademas duplicar los campos en el formulario de dentro, y hacer que estos actuen como proxy del formulario de fuera. Es decir, para los campos te texto capturamos el input y se lo asignamos al campo real de fuera, y para los demas, como file submit, button, etc, capuramos el clic, y lanzamos el evento clic del campo asociado.
    <script>
     $("#avatar_form").ajaxForm(options); //no explico mas sobre esto, seria una forma normal de capturar el formulario para enviarlo por ajax
    $('#nestedinput).keydown(keypressed);
    function pickFile(){
    
     $('#file_browse').click();
     }
     function sendFile(){
    
     $('#avatar_form').submit();
     return false;
     }
    function keypressed( event ) {
    $("#outter_input").attr("value",$("#nested_input".attr("value"));
    }
    
    </script>
    <form id="avatar_form" style="visibility: hidden; position: absolute;"  action="/perfil/jugador/addfoto" method="post"
    enctype="multipart/form-data">
    
    <input type="file" id="file_browse" name="foto">
    <input type="text"  id="outter_input" value="">
    </form>
    <form action="" method="POST" name="perfil\profesional" id="perfil\profesional">
    <div class="tabss" >
       <div class="tab1" >
    
    <div id="avatar"  >
    
    <button   id="boton_enviar" onclick="sendFile()"
    style="display: none">subir imagen</button>
    </div>
    
    <div class="file_browse_tip" onclick="sendFile()">Clic para cambiar imagen</div>
    <input type="text"  id="nested_input" value="">
    
    </div>
    
    <input type="text" name="text1" value="">
    <input type="text" name="text2" value="">
     </div> <div class="tab2" >
    <input type="text" name="text3" value="">
    <input type="text" name="text4" value="">
     </div>
     </div >
     <input name="submit" type="submit" id="submitbutton" value="Guardar">
    
     </div>
     </form>
    
    

    Y esto nos soluciona perfectamente todos los casos y de una forma muy cómoda para tratar con el resto de eventos asociados al formulario, como validacion, etc.

Facebook Twitter Linkedin Plusone Digg Delicious Reddit Email

Leave a Reply

Required fields are marked *.