1

I have a form where a user can update his details. This update is done by using the jQuery focusout function and Ajax to update the database.

In this example, i have three text inputs. I have tested the first input where it is updated when the user goes to the next input. A loader icon appears while that input is being updated.

For that first input i have created a controller and its corresponding JQuery code.

However, if i had a form of 10 inputs, i would have to do this 10 times. My question is: how do i simplify this? so i just have to code it once.

Here is the form:

<form role="form" class="form-horizontal" action="{{URL::route('user-edit-profile-post2')}}" method="post" id="formedit2">
    <!---------------------------SECCIÓN USUARIOS---------------------->
    <!-----Nombre de pila---------->
    <div class="form-group @if ($errors->has('nombre_pila')) has-error @endif">
        <label for="nombre_pila" class="col-sm-2 control-label">Nombre(s) de pila:</label>
        <div class="col-sm-6">
            <input type="text" class="form-control" id="nombre_pila" name="nombre_pila" maxlength="45" value="@if(Input::old('nombre_pila')){{e(Input::old('nombre_pila'))}}@elseif(isset($nombrepila)){{$nombrepila}}@endif">
        </div>    
    @if($errors->has('nombre_pila'))
        <p class="help-block">{{$errors->first('nombre_pila')}}</p>
    @endif
    </div><!-- ----------fin nombre de pila ------------ -->

    <div class="form-group @if ($errors->has('ap_paterno')) has-error @endif"><!--apellido paterno-->
        <label for="ap_paterno" class="col-sm-2 control-label">Apellido paterno:</label>
        <div class="col-sm-6">
            <input type="text" class="form-control" id="ap_paterno" name="ap_paterno" maxlength="45" value="{{ (Input::old('ap_paterno') ? e(Input::old('ap_paterno')) : (isset($ap_paterno) ? $ap_paterno : '')) }}"><!--Referencias: https://stackoverflow.com/questions/26960929/laravel-4-when-i-retrieve-info-from-database-into-input-text-1-extra-space-is-a/26961224?noredirect=1#comment42683960_26961224-->
        </div>
    @if($errors->has('ap_paterno'))
        <p class="help-block">{{$errors->first('ap_paterno')}}</p>
    @endif    
    </div><!-- ----------fin de apellido paterno ------------ -->

    <div class="form-group @if ($errors->has('ap_materno')) has-error @endif"><!--apellido materno-->
        <label for="ap_materno" class="col-sm-2 control-label">Apellido materno:</label>
        <div class="col-sm-6">
            <input type="text" class="form-control" id="ap_materno" name="ap_materno" maxlength="45" value="@if(Input::old('ap_materno')){{e(Input::old('ap_materno'))}}@elseif(isset($ap_materno)){{$ap_materno}}@endif">
        </div>
    @if($errors->has('ap_materno'))
        <p class="help-block">{{$errors->first('ap_materno')}}</p>
    @endif    
    </div><!-- ----------fin de apellido paterno ------------ -->

    <!------------------GUARDAR--------------------- -->
    <div class="form-group"><!--Botón de guardar-->
        <div class="col-sm-offset-2 col-sm-10">
            <button type="submit" class="btn btn-success">Guardar</button>
        </div>
    </div><!--Fin del botón de guardar-->
    <!--------------Fin de GUARDAR ---------------->
    <!--TOKEN-->
    {{ Form::token() }}
</form>

And below the form, i have this JQuery code:

<script type="text/javascript">
    $('#nombre_pila').on('focusout',function(){
        var editform = $('#formedit2').serializeArray();
        var url = $("#formedit2").attr('action');//con esto se llama al controlador correspondiente!
        $('#nombre_pila').addClass('loadinggif');
        $.post(url, editform, function(data,status){
            $('#nombre_pila').removeClass('loadinggif');
            //alert(data);
            if(data=='fail'){
                alert('No se pudo actualizar el campo. Status:' + status);
            }else if(data=='success'){
                alert('Dato actualizado exitosamente! Status: ' + status);
            }else if(status=='timeout'){
                alert('El tiempo de espera se ha agotado. Recargue la página.');
            }
        });
    });
</script>

And the controller is:

public function postEditProfile2(){
    //save data and send to form page or display a success message
    //instanciando al usuario actual:
    $id_user=Auth::user()->id;
    $usuario=User::find($id_user);
    $nombre_pila=Input::get('nombre_pila');
    //preparando el dato para guardarlo
    $usuario_data=array(
        'nombre_pila'=>Input::get('nombre_pila')
        );
    // saving...
    $usuario->fill($usuario_data);
    if($usuario->save()){
        return 'success';
    }else{
        return 'fail';
    }
    return 'posted: '.$nombre_pila;
}

So far i have this code to update the first input, nombre_pila, on focus out. Now i have two inputs more left: ap_paterno and ap_materno. How can i arrange my code so that it updates the information of the most recent input that has been focused out?

EDIT:

What I want is to update only each input. To achieve this, i have set up identifiers in each input in such a way i can get easily which input has been recently focused out. But first i need to detect if the value in input text has been changed (in this edit form, each input displays the data saved in the database previously from the user).

So, before the focusout function, i have added another function to detect changes of a specific input (through its ID attribute) like in this fiddle . Finally, i decided not to use the focusout() here, since it turned out to be annoying, especially if you put the mouse in any input, the post() function will be unavoidably called, for example, even if i go to another window or to another tab.

So here is the edit part of the JQuery, which is working fine and it is updating in the database only the corresponding input value:

//Primero detectar si hubo cambios en un input dado
                var timerid;
                $("input").on("input",function(e){
                    var currentId = $(this).attr('id');
                    //alert('Current ID value is: '+currentId);
                    var value = $(this).val();
                    //preguntar si el dato del input en donde está el cursor ha cambiado:
                    if($(this).data('lastval')!=value){
                        $(this).data('lastval',value);
                        nuevoValor=$(this).val();
                        clearTimeout(timerid);
                        timerid=setTimeout(function(){
                            //change action
                            alert('vamos a actualizar la info...Identificador: '+currentId+' Nuevo valor:'+nuevoValor);
                            //AQUÍ PONEMOS EL PROCEDIMIENTO DE ACTUALIZACIÓN DE LA INFO DEL CAMPO CORRESPONDIENTE.
                            //var currentID = $('#formedit2 input').attr('id');//https://stackoverflow.com/a/23266812/1883256, http://jsfiddle.net/xstqLkz4/59/, http://www.tequilafish.com/2007/12/04/jquery-how-to-get-the-id-of-your-current-object/
                            //var newValue = $(currentId).val();
                            alert('ID es: '+currentId+' and current value is: '+nuevoValor);
                            //var editform = {identifier:currentID,value:newValue};
                            var editform = $('#formedit2').serializeArray();
                            editform.push({name:'identifier',value:currentId});//https://stackoverflow.com/a/13362942/1883256
                            //alert(editform);
                            var url = $("#formedit2").attr('action');//con esto se llama al controlador correspondiente!
                            $(this).addClass('loadinggif');
                            $.post(url, editform, function(data,status){
                                $('#formedit2 input').removeClass('loadinggif');
                                //alert(data);
                                if(data=='fail'){
                                    alert('Could not update the input field. Status:' + status);
                                    $('#formedit2 input').removeClass('loadinggif');
                                }else if(data=='success'){
                                    alert('Data updated successfully! Status: ' + status);
                                    $('#formedit2 input').removeClass('loadinggif');
                                }else if(status=='timeout'){
                                    alert('Time out. Please reload');
                                    $('#formedit2 input').removeClass('loadinggif');
                                }else if(status=='error'){
                                    alert('There has been an error. Try again later.');
                                    $('#formedit2 input').removeClass('loadinggif');
                                }else{
                                    $('#formedit2 input').removeClass('loadinggif');
                                    alert(data);
                                }
                            });//end of post() function
                            //END OF UPDATING INFO PROCESS
                        },2500);
                    };
                });//end of input part

And here is the controller where by using the switch statement, the controller knows which field must be updated in the database:

public function postEditProfile2(){
    //save data and send to form page or display a success message
    //instanciando al usuario actual:
    $id_user=Auth::user()->id;
    $usuario=User::find($id_user);
    $identifier=Input::get('identifier');
    //$nombre_pila=Input::get('nombre_pila');
    $identificando=explode('_',$identifier);
    $tabla=$identificando[0];
    $num=end($identificando);
    if($tabla=='usuario'){
        switch ($num) {
            case '01':
                $usuario_data=array('nombre_pila'=>Input::get('nombre_pila'));
                break;
            case '02':
                $usuario_data=array('ap_paterno'=>Input::get('ap_paterno'));
                break;
            case '03':
                $usuario_data=array('ap_materno'=>Input::get('ap_materno'));
                break;
            default:
                # code...
                break;
        }
        // saving...*/
        $usuario->fill($usuario_data);

        /*$usuario->fill(Input::all());*/
        if($usuario->save()){
            return 'success';
        }else{
            return 'fail';
        }
    }else{
        return 'error';
    }
 }//end of postEditProfile2() function

But now the problem is that the ajax-loader.gif, the part where i add it as a class, $(this).addClass('loadinggif'); no longer appears. Why? Any help?

SOLVED: I was not using the selector correctly for the ajax loader, it was a simple + sign that was missing, like shown here, so the line is:

$('#'+currentId).addClass('loadinggif');

Final question: The only problem left is that the timeout status won't appear. For example, if one hour passes by without any action, i try to modify the data and nothing happens. Nothing is updated. So i have to refresh the page again. And this doesn't happen in Stackoverflow, even if the computer has been "sleeping" and i wake it up again, i can still update information without refreshing the page. How is that possible? What should i do to keep alive the page in my case?

Community
  • 1
  • 1
Pathros
  • 10,042
  • 20
  • 90
  • 156

1 Answers1

1

I hope I understood you right...

First, make your jQuery code generic so it works with every input. All you need to do is change the selectors:

$('#formedit2 input[type=text]').on('focusout',function(){
    var editform = $('#formedit2').serializeArray();
    var url = $("#formedit2").attr('action');//con esto se llama al controlador correspondiente!
    $(this).addClass('loadinggif');
    $.post(url, editform, function(data,status){
        $(this).removeClass('loadinggif');
        //alert(data);
        if(data=='fail'){
            alert('No se pudo actualizar el campo. Status:' + status);
        }else if(data=='success'){
            alert('Dato actualizado exitosamente! Status: ' + status);
        }else if(status=='timeout'){
            alert('El tiempo de espera se ha agotado. Recargue la página.');
        }
    });
});

As you can see I changed the first selector to match all text inputs and replaced the ones that referred to nombre_pila with $(this).

After that, change your controller a bit so it will fill any input passed into the model. Just make sure you set the fillable/guarded attribute in your model right.

public function postEditProfile2(){    
    $id_user=Auth::user()->id;
    $usuario=User::find($id_user);
    $usuario->fill(Input::all());
    if($usuario->save()){
        return 'success';
    }else{
        return 'fail';
    }
}

(I also removed the last return statement since it will be never reached)

lukasgeiter
  • 147,337
  • 26
  • 332
  • 270
  • Thanks @lukasgeiter, i have fixed it a bit, based on your comment. But now i have another issue. The loader image no longer appears. See my edit. – Pathros Jan 04 '15 at 01:28
  • @pathros I see you solved the issue yourself. Regarding the timeout problem, I suggest you ask a completely new question and add more details. Thanks. – lukasgeiter Jan 04 '15 at 11:50