AJAX

¿Te gustaría aprender Diseño Web?
Tenemos los cursos que necesitas. ¡Haz clic aquí!

Lo que vamos a hacer, respecto a cómo lo ve el usuario de la aplicación, es lo siguiente.

El usuario tiene una imagen de perfil por defecto.

Imagen por defecto

El usuario hace clic sobre su imagen de perfil, y de pronto puede seleccionar una nueva imagen (aunque también puede presionar cancelar y no pasa nada).

Clic para modificar imagen de perfil

Si el usuario selecciona una imagen y acepta, entonces su imagen de perfil se actualiza inmediatamente (sin necesidad de recargar la página los cambios se mantienen).

Imagen de perfil modificada al instante

¿Cómo lo vamos a hacer?

Tenemos muchas formas de implementar esta característica.

En este caso lo haremos de la siguiente manera:

  • Vamos a añadir un formulario (pero con estilos haremos que permanezca oculto). Este formulario va a contener un campo de tipo file.
  • Vamos a usar Javascript para asociar un evento de clic sobre la imagen de perfil. Cuando se detecte este evento, vamos a provocar un clic sobre el input de tipo file.
  • Vamos a escuchar el evento change del input, para que cuando se haya escogido un archivo, realicemos una petición AJAX para modificar la imagen de perfil.
  • La petición AJAX devolverá un mensaje de éxito o fallo. Si la respuesta fue exitosa, entonces vamos a actualizar la imagen de perfil vía Javascript (para no actualizar la página).

Elaborandolo

El formulario oculto estará escondido gracias a una propiedad de CSS llamada display (con el valor none).

En este caso, lo estoy ubicando justo antes de la imagen.

<form action="{{ url('perfil/foto') }}" method="post" style="display: none" id="avatarForm">
    {{ csrf_field() }}
    <input type="file" id="avatarInput" name="photo">
</form>
<img src="{{ auth()->user()->getAvatarUrl() }}" id="avatarImage">

Nótese que:

  • El formulario, al igual que la imagen, y el input, tienen un id asignado (esto es importante para acceder a estos elementos vía Javascript).
  • El input de tipo file tiene un atributo name con el valor photo (este valor debe coincidir con lo que tengamos en nuestro controlador, para recuperar el archivo subido correctamente desde backend).
  • He simplificado el código quitando el atributo classalt y title a mi imagen (ustedes pueden usar estos atributos según les convenga en sus proyectos).

¿Qué hacemos con Javascript?

Como ya lo he comentado antes, necesitamos registrar 2 eventos. Y así mismo obtener una referencia de los elementos.

$(function () {
    var $avatarImage, $avatarInput, $avatarForm;

    $avatarImage = $('#avatarImage');
    $avatarInput = $('#avatarInput');
    $avatarForm = $('#avatarForm');

    $avatarImage.on('clic', function () {
        $avatarInput.clic();
    });

    $avatarInput.on('change', function () {
        alert('change');
    });
});

Como habrás notado, en el evento change del input sólo he puesto un alerta.

Hasta este punto debes asegurarte de obtener ese alerta, luego de hacer clic en la imagen y seleccionar un archivo.

Si todo está conforme, entonces ya puedes reemplazar el alert para realizar la petición Ajax.

Asynchronous JavaScript and XML: What Is AJAX? - Practic WEB

Así tendríamos lo siguiente:

$avatarInput.on('change', function () {
    var formData = new FormData();
    formData.append('photo', $avatarInput[0].files[0]);

    $.ajax({
        url: $avatarForm.attr('action') + '?' + $avatarForm.serialize(),
        method: $avatarForm.attr('method'),
        data: formData,
        processData: false,
        contentType: false
    }).done(function (data) {
        if (data.success)
            $avatarImage.attr('src', data.path);
    }).fail(function () {
        alert('La imagen subida no tiene un formato correcto');
    });
});

Aquí debemos tener en cuenta que:

  • Estamos obteniendo la url a la que se hará la petición, y el method a partir de los valores definidos en el formulario.
  • Usamos un objeto FormData para subir la imagen vía Ajax (ya que el método serialize en este caso sólo captura el csrf token).
  • La respuesta que obtenemos de la petición Ajax se compone de un objeto al que llamamos data y que tiene 2 atributos (success para indicar si la operación tuvo éxito, y path con la ruta hacia la imagen de perfil).

Por último, sólo nos hace falta tener registrada la ruta perfil/foto (que fue la que usamos en el action del formulario). Si lo prefieres, puedes usar una ruta distinta.

Esta ruta debe declararse en el archivo de rutas de Laravel.

Route::post('/perfil/foto', 'ProfileController@updatePhoto');

En este caso, la ruta se resuelve a través de un controlador llamado ProfileController. Específicamente a través de su método updatePhoto.

Es así que tendríamos lo siguiente en dicho controlador:

public function updatePhoto(Request $request)
{
    $this->validate($request, [
        'photo' => 'required|image'
    ]);

    $file = $request->file('photo');
    $extension = $file->getClientOriginalExtension();
    $fileName = auth()->id() . '.' . $extension;
    $path = public_path('images/users/'.$fileName);

    Image::make($file)->fit(144, 144)->save($path);

    $user = auth()->user();
    $user->photo_extension = $extension;
    $saved = $user->save();

    $data['success'] = $saved;
    $data['path'] = $user->getAvatarUrl() . '?' . uniqid();

    return $data;
}

Este método:

  • Valida que el campo photo enviado en la petición sea una imagen (y es un además un campo obligatorio).
  • Obtiene la extensión de la imagen, de tal forma que el id del usuario, seguido de la extensión sea el nombre del archivo a guardar.
  • Hace uso del facade Image para que si la imagen es mayor a 144×144 píxeles, entonces se ajuste su tamaño en función a este límite.
  • Guarda en la tabla de usuarios, en la columna photo_extension, la extensión de la imagen subida.
  • Finalmente devuelve una respuesta en JSON indicando si la operación fue exitosa, y además envía de vuelta la URL de la imagen.

Para que el código anterior funcione correctamente, es necesario que en la tabla de usuarios tengas una columna photo_extension.

$table->string('photo_extension')->nullable();

También es necesario que definas el método getAvatarUrl en el modelo User.

public function getAvatarUrl()
{
    if ($this->photo_extension)
        return asset('images/users/'.$this->id.'.'.$this->photo_extension);

    return asset('images/users/default.jpg');
}

Este método devuelve una URL absoluta hacia la imagen de perfil del usuario. Y en caso de no existir, la URL absoluta de una imagen por defecto.

Si no has instalado aún el paquete Intervention/Image (necesario para que funcione la redimensión de imágenes), lo puedes instalar simplemente ejecutando:

composer require intervention/image

Por cierto. Si eres observador habrás notado el uso de uniqid() en el método updatePhoto. Éste método se usa para añadir un número único al final del nombre del archivo, y asegurar de esta forma, que el usuario vea siempre su nueva imagen de perfil (ya que es posible que anteriormente haya subido una imagen con la misma extensión, y que esta imagen se haya guardado en la memoria caché del navegador).

Te esperamos en los siguientes artículos en donde hablaremos mas acerca de estos temas, los cuales hoy en día son de vital importancia en el mundo de la tecnología.

¿Te gustaría aprender Diseño Web?
Tenemos los cursos que necesitas. ¡Haz clic aquí!

About Author

NGuerrero

0 0 votos
Article Rating
Suscribir
Notificar de
guest
0 Comments
Comentarios.
Ver todos los comentarios
0
¿Te gusta este articulo? por favor comentax
()
x
Abrir chat
¿Quieres aprender a programar?
Hola 👋,
¿Te interesa información de nuestros cursos?