2

Is it possible to login the user in frontend through Ajax request and without all this redirecting? I am using the joomla login module and trying to extend it but so far no results.

I have this js

<script>
jQuery(document).ready(function() {
    jQuery("#login-form-162").submit(function(e) {
        e.preventDefault();
        var form = jQuery(this);
        var url = form.attr('action');
        var data = form.serialize();
        console.log(data);
        jQuery.ajax({
            type: "POST",
            url: url,
            data: data,
            success: function(response) {
                // Handle successful login
                console.log('login success');
                console.log(response);
            },
            error: function() {
                console.log('login error');
                console.log(response);
                // Handle login failure
            }
        });
    });
});

but apparently doesn't work since the url that comes from form.action is like below and always returns "success"

<?php echo Route::_('index.php', true); ?>

Is there another way to call on the user controller(? maybe login controller) through ajax?

1 Answer 1

1

Now, if I wanted to give a lazy answer on this question I'd just write: you should target the correct URL with your AJAX call for getting the controller method and you are done:

const url = "/index.php?option=com_users&task=user.login"

THE CODE PART

I just wrote a login/logout Ajax call using vanilla Javascript since I like plain javascript more nowadays (jQuery is getting more and more redundant unfortunatelly in web-development). So here it is (tested on Joomla4 Cassiopeia template):

window.addEventListener('load', () => {
  const form = document.getElementById("login-form-16");
  if (form) {
    form.addEventListener("submit",  async e => { formSubmit(e, form) 
   .then(console.log)
   .catch(console.log)    
    });
  }
});

async function formSubmit(e, form) {

  e.preventDefault();
  const formData = new FormData(form);

  const response = await fetch("/index.php?option=" + formData.get('option') + "&task=" + formData.get('task'),
  {
    method: "POST",
    body: formData,
  });

  if (response.ok) {
    return Promise.resolve("the user is logged in or out").then(location.reload());
  } else {
    return Promise.reject("User login/logout controller was not found");
  }
};

The user is logged in and out and stays on the same page with this script but nothing more. This script is not even using any sanitization or response on bad or not existing user credentials, a lot of other work... However it log in and out existing users. This is just a starter code for this task. I do not see the real purpose and value of using this particularly in Joomla Login/logout, other than "no default redirection after login", since Joomla is still an almost fully server side rendered content management system with quite great user management. But that is only me, maybe others want to use these things on their Joomla4 later.

WHAT OTHER OPTIONS WE HAVE ON THIS?

As we can see, calling the com_user component user controller login method via Ajax is almost totally useless, this method is not for Ajax call at all. You will not get any valuable response from doing that.

The other and better approach is creating your own custom plugin or module and you can get further with those. Terry Carter created an Ajax login plugin in his answer here in 2020. It was for Joomla 3 but easily can be followed and recreated for Joomla 4: External Login Script and getDbo()

I also created my own version of Login/Logout few days ago with using the advantage of com_ajax placing my own Ajax login method in my Cassiopeia child template in Joomla 4. com_ajax in Joomla handles Ajax calls not only for plugins and modules but for template helper files too. Much less people used this feature, however it is more confortable than creating plugins in my opinion. So I share this, maybe some people would like to use this approach.

I just created a helper.php file in the root directory of my child template and that way I can call that class and method via com_ajax.

My template is called Cassiopeia_cassiozoll, so I had to create this class:

<?php

/**
 * @package     Joomla.Site
 * @subpackage  Templates.Cassiopeia_cassiozoll
 *
 * @copyright   -- no copyright --
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Response\JsonResponse;


class TplCassiopeiacassiozollHelper
{
    public static function loginUserAjax() {       

        $app = Factory::getApplication();

        // checking Token
        if ($app->isClient('site')) {
            Session::checkToken('post') or die(Text::_('JINVALID_TOKEN'));            
        } 

        $input = $app->getInput();        

        if ($input->getMethod() === 'POST') {

            $credentials = $input->getArray();

            $loggedIn = $input->cookie->get('joomla_user_state');        

            $options = array();

            $userID = null;
            $name = null;

            if ($loggedIn === "logged_in")
            {
                $userID = $app->getIdentity()->id;
                $result = $app->logout($userID, $options);
                $loggedIn = $result ? null : 'logged_in';
                $userID = $result ? null : $userID;
            }
            else
            {
                $result = $app->login($credentials, $options);
                $userID = $app->getIdentity()->id;
                $name = $app->getIdentity()->name;
                $loggedIn = $result ? 'logged_in' : null;
            }

            $allResult = array('result'=> $result, 'userID' => $userID, 'name' => $name, 'loggedin' => $loggedIn);
            
            return new JsonResponse($allResult);        
        
        }
        else 
        {
            return;
        }
    }
}

And via this file we can reach the Application login/logout method and we have all the responses we need during login/logout. And we just call this method with our Javascript file similarly as above:

window.addEventListener('load', () => {
  const form = document.getElementById("login-form-16");
  if (form) {
    form.addEventListener("submit",  async e => { formSubmit(e, form) 
      .then( (response) => {
          console.log(response);

          if (response.data.messages) {
              warningMessage(form, response.data.messages)
          } else {
              location.reload()
          }
          
      })
      .catch(console.log)    
    });
  }
});

async function formSubmit(e, form) {

  e.preventDefault();

  const formData = new FormData(form);
  formData.delete('option');
  formData.delete('task');

  const myRequest = new Request( 
      "/index.php?option=com_ajax&template=cassiopeia_cassiozoll&method=loginUser&format=json",
      {
        method: "POST",
        body: formData,
      }
    );

  const response = await fetch(myRequest)
  
  if (response.ok) {
      return response.json();
  } else {
      return Promise.reject("User login/logout was not found");
  }
};

// let's do some warning if password is empty or login problems
function warningMessage(element, message) {

  const loginElement = element.parentElement;
  
  for (let i = 0; i < message.warning.length; i++) {
      const errorMessage = document.createElement('span');
      errorMessage.style.cssText += 'color: red; background-color: WhiteSmoke; padding: 1rem; font-weight: bolder;';
      errorMessage.textContent = message.warning[i];
      loginElement.parentNode.insertBefore(errorMessage, loginElement);
  }

}

And it is actually done. This method is using the core login/logout of Joomla and we get warning messages from these core methods. But on successful login we still have to reload the page if we do not want to waste our time to create a lot of other things just to refresh everything asynchronously.

TL;DR - OTHER CONSIDERATIONS OF THIS APPROACH AND PROBLEM OF LOGIN WITH USING AJAX IN JOOMLA

This could be a much more interesting question for some developers on Joomla. I am also a big fan of the usage of Javascript on front-end of websites. However this question is interesting from few other angles. I like the Javascript controlled client requests when submitting Forms. It is just much more elegant nowadays. The only exception would be logging in and out users in Joomla. Why?

Unless you have a client side rendered (I mean with client side state management like Vue and React for example), custom made Template/theme on your Joomla, you will have to reload the page (from the server) after you logged in/out the user by using an Ajax request. Thus there is not much advantage of using Ajax for login compared to the normal Joomla http request on submit. Because it is one thing to log in users in Joomla but after that you should be able to get the Joomla User object with Javascript to identify your user and get his display name (not username), you should get the user roles and access permissions and update asynchronously the contents corresponding with the access level and so on... it might get a bit complicated by using Javascript.

So it is just much easier in Joomla to reload the current location/page right away when the user is logged in by an Ajax call and then it becomes visible in the standard Joomla login module with the user's display name. So you do not have to waste your time to refresh/replace the login module HTML by using Javascript. And then you also might want to logout the users with using the same script. So it can get just a little more complex.

But as I see it in your question, your problem with using the standard Joomla login method is actually the redirection and not the login method in the first place. You just want to let your users staying on the same page after logging in and out. You do not want them to be redirected to profile page or to home page or anywhere when they are signing in (if I understood this well in your question).

1
  • 1
    Thank you so much for your detailed answer. It looks like you said with updating the layout when user is logged in(it's kinda "hard" with only js). So I guess should be ok to override the login and use ajax when the login is failed and when the user is successfully logged in I can still reload/redirect the page. I wanted only to avoid the redirect/reload on fail login.
    – anthi_p
    Mar 22 at 15:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.