cbil360's avatar

Routing between Angular and Laravel

Hello We have started working on a angulara project(Not sure if the term is right or in use). We are facing challenges in routing back and forth using laravel and angular. Use Case: We have a multiple page application where we are using Angular(Not using angular cannot be a solution). Our one app view has the inception section i.e user login and registration. The 2nd app has the users dashboard and the 3rd app view has a website editor.

Now while loading app our laravel route is invoked at first. routes.php

Route::get('/','WelcomeController@index');
Route::get('dashboard','DashboardController@dashWelCome');

WelcomeController@index

public function index()
    {
        return view('start.index');
    }

Now, when we have to switch from the login app to the dashboard app(after user logs in), we need to load the app first in order to send request as till the app does not load not request would be served.

$scope.doLogin = function(customer) {
        Data.post('login', {
            customer: customer
            // customer: sanitizeCredentials(customer)
        }).then(function(results) {
            Data.toast(results);
            if (results == "success") {
                // $location.path('dashboard');
                window.location.href = path+"dashboard";
            }
        });
    };

This is what we do: return success from laravel auth method and in the callback hit the dashboard URL(above) ,this loads the dashboard and then we can further perform operations. But instead we want the user to go to dashboard/profile after login,but as the app view is not loaded we do not get the result and the url just redirects to root like localhost/dashboard/profile.

Same issue in the reverse manner,when user logs out we want to send him to login app view but as we are in the dashboard app we are redirected to laravel route which does not have any view attached, as the views are attched to angular.Not sure if I can return a parameter with logout route and catch in angular to load the login app again and then go ahead. Please suggest.

0 likes
15 replies
rodrigo.pedra's avatar

For the logout I think you can do this in Laravel:

public function logout() {
    // logout logic here, probably:
    Auth::logout();
    app( 'session' )->flush();

    // redirect to login page
    return redirect('/');
}

For the login redirection, I didn't understand the whole flow:

  1. Laravel sends to / (Route::get('/','WelcomeController@index');)
  2. Angular loads proper login form
  3. User submits the form and angular triggers the $scope.doLogin = function(customer) {...}; Which send login data with an angular promise (probably the Data.post(...) is a service/factory wrapper around $http.post(...))
  4. Upon success you want to redirect it to a different URL but wants Laravel to handle it (is this correct?)

I don't understand this:

But instead we want the user to go to dashboard/profile after login,but as the app view is not loaded we do not get the result and the url just redirects to root like localhost/dashboard/profile.

Is angular trying to handle the redirect instead of the browser sending the request to the server (Laravel)? What do you mean by the "app view is not loaded", who loads this view: angular or laravel?

cbil360's avatar

@rodrigo.pedra Currently I have the same setup for logout as you mentioned,but in case I have to go to /login ,there lies a problem. routes.php

Route::get('/','WelcomeController@index');
Route::post('login','LoginController@auth');
Route::get('dashboard','DashboardController@index');
Route::post('userstats','UserstatController@stat');
Route::get('logout','LoginController@logoutUser');

Actually logincontroller is different which call auth method

public function auth()
    {
        $input = \Request::only('customer.email', 'customer.password');
        if(Auth::attempt(array_get($input, 'customer')))
        {
            //return Response::json(Auth::user());
              return "success";
        }else {
            return Response::json(array('flash' => 'Invalid username or password'), 500);
        }
    }

and smartboard.index is the view under my resources/views/smartboard/index.

Yes,actually as our dashboard view is a entirely different page (a different view),till I load the base route which is /dashboard I cannot call the further routes that are after dashboard. Example : I want the user to see his profile directly after login at a route like dashboard/profile ,this is where we are failing. What is the right way to do this?

rodrigo.pedra's avatar

I imagine you have a more lengthy routes.php file, but in the snippet you posted the /login is s post route, so you won't be able to redirect there. Isn't redirecting to the / route after logging the user out working? The auth middleware should redirect the user to the login page.

For the second part, why this does not work? (in your javascript)

.then(function(results) {
    Data.toast(results);
    if (results == "success") {
        // $location.path('dashboard');
        window.location = "./dashboard/profile"; // no need for the href here, the dot means "from this folder on"
    }
});

As far as I understood your profile view is generated by laravel so it should work

1 like
cbil360's avatar

@rodrigo.pedra Ok,the /login is called via angular route and not laravel.Hence not in the routes.php.The file has the complete list.

public function index()
{
        if(Auth::check()){
            //return view('smartboard.index');
            return 'success';
        }else{
            return redirect('logout');
        }
    }

Here is the dashboardController index method.I return success on Auth::check(); Catch that in the do.post callback.

$scope.doLogin = function(customer) {
        Data.post('login', {
            customer: customer
            // customer: sanitizeCredentials(customer)
        }).then(function(results) {
            Data.toast(results);
            if (results == "success") {
      
                window.location = "./dashboard/profile";   //Here aded the profile URL
            }
        });
    };

I just see success printed on the screen and the view is not loaded on the browser. I believe I have to load the initial view which is index(as my app is multiple page this is a diff view) via laravel and rest using angular. Please correct!

rodrigo.pedra's avatar

What does Data.toast(results); is it something like toastr.js [ https://github.com/CodeSeven/toastr ]? Isn't it replacing the page content with the results?

Also I forget the data returned by the server is in a data property inside the results argument, so try this:

        .then(function(results) {
            // Data.toast(results); // what does this do?
            if (results.data == "success") { // note the data attribute
                window.location = "./dashboard/profile";
            }
        });
cbil360's avatar

@rodrigo.pedra Actualy Data.toast(results) is a plugin for showing toast notifications and nothing to do with the actual view of the application,it is displayed on success or failure with the appropriate message. something similar here: https://github.com/jirikavi/AngularJS-Toaster.

Also results.data also results into same. I am confused a bit on how should the routing function. For example:

  1. My very first request goes to laravel i.e / which invokes the WelcomeController.(Url in browser: http://localhost/iq/public/#/home)
  2. My Angular route opens the login page for me which is /login (url in browser: http://localhost/iq/public/#/login)
  3. The above two views are loaded in the start.index page under resources/views dir.
  4. Now when I move to dashoard I want to load a completely new view(new page),which is under smartboard dir inside resources/view.
  5. So,as I have to load a new view to have it in DOM I will have to call the laravel route first.Hence if I call dashboard and redirect to http://localhost/iq/public/dashboard this works.
  6. If I directly try to return redirect(dashboard.profile) it just prints the success msg and no redirect happens as the HTML container in which the profile view is supposed to load is not present in the DOM.

Hope I am able to assess the situation right.

Thank you

rodrigo.pedra's avatar

How is the profile route defined in routes.php?

Try this on step 6:

return redirect('dashboard/profile');
cbil360's avatar

@rodrigo.pedra Route::get('dashboard/profile','DashboardController@display'); this is the route.Added this for testing purpose. smartboard is the dir in resources\views\ with dashboard views

public function display()
    {
        return view('smartboard.profile');

    }

What you say is right,I tried this and I only get the HTML but as per angular convention(SPA) I have a index.php for the entire dashboard where I would like to inject the views of profile,payment,deal etc modules.But to inject the profile HTML in the dashboard index file I will first need to have the index present in DOM which is what not happening and I get unformated HTML in return If I do a `return redirect('dashboard/profile');

rodrigo.pedra's avatar

Now I got what you mean, as you said it is not a SPA I wasn't considering that you would use ngRouter internally.

Right now I am in a bus traveling for a meeting I have tomorrow, I'll arrive in 4-5h and will take a look.

If anyone else want to help, be my guest.

rodrigo.pedra's avatar
Level 56

I'll assume the following, correct me if I am wrong:

  1. On Laravel's route /dashboard you return the HTML with proper angular bindings (e.g.: ng-app="...") for bootstraping an angular app.
  2. Inside this HTML you have an angular app that uses ngRoute module to load internal views.
  3. Upon loading the app you want to load the angular route/view dashboard/profile and let angular load the partial in a ng-viewdirective somewhere.

So try this:

  1. After logging the user in, redirect him/her to the dashboard route:
.then(function(results) {
    Data.toast(results);
    if (results.data == "success") {
        window.location = "./dashboard"; // this will make a request to laravel's router
    }
});
  1. In the app loaded in the dashboard add this run(...) block
var app = angular.module('dashboard', ['ngRoute']); // module definition

/* ... other relevant code ... */

// this will run when the angular app is loaded for the first time
app.run(['$location', function ($location) {
    $location.path( '/profile' ).replace(); // "replace()" will replace the page in the browser's history
}]);

By your description I think this will nail it.

1 like
cbil360's avatar

@rodrigo.pedra I will just mention what we tried and worked for us,I would like to know if its relevant and valid way to do it. In LoginController

public function logoutUser()
    {
        Auth::logout();
        return redirect('/');
    }

I have this scope in my angular controller.We sent a parameter because we may need to redirect based on some condition to different routes.

$scope.logout = function() {
        Data.get('logout').then(function(results) {
            // Data.toast(results);
            window.location.href = path+"#/home?login";
            // $location.path('login');
        });
    }

The run method in our angular app config

.run(function($rootScope, $location, path) {
    $rootScope.$on("$stateChangeStart", function(event, next, current) {
        var absUrl = $location.absUrl();
        if(absUrl.indexOf("login") != -1){
            console.log(absUrl.indexOf("login") != -1);
            $location.url("/login");
        }
    });

This redirects to the login view correctly.But is it the right way to do it?

Another concern is the route in laravel routes.php Route::get('logout','LoginController@logoutUser'); Now if some user accidnetly/purposely enters /logout in the url the user would be logged out(Ya though the chances are grim).How can restrict this thing to happen?

rodrigo.pedra's avatar

I don't see it as a problem, if a user accidentally types logout on the url bar, maybe he/she really wants to log out :)

Anyway, what you could do is:

  1. Detect if the call to the Logout route is from ajax
  2. If not, use laravel's to redirect the user back

I will start with point #2:

public function logoutUser( Request $request ) // use \Illuminate\Http\Request; or any subclass of it
{
    if ($request->ajax()) { // check if call is made from ann AJAX request
        Auth::logout();
        return ['result' => 'success']; // will return as json to $http call
    }
    return redirect()->back();
}

The reason I started with #2, is to show how easy it is in laravel side. But, unfortunately, angular doesn't play nice with laravel's $request->ajax() method, which require a X-Requested-With: XMLHttpRequest header to be sent with the request, which angular does not send by default [ https://github.com/angular/angular.js/commit/3a75b1124d062f64093a90b26630938558909e8d ].

You can make angular sending it with this code:

// app is your angular module 
app.config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
}]);
1 like
cbil360's avatar

@rodrigo.pedra Cool I will use the same approach in the dashboard/profile issue i mentioned. I believe this sets the header for every ajax request if I set it in the root angular app?Right?

 $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

Also I was stuck with the pivot tables and posted a question https://laracasts.com/discuss/channels/general-discussion/issues-with-seed-and-pivot-tables-while-working-with-api-lesson Any good resource to understand seeding?I am working on the API tutorial but could not move ahead.

rodrigo.pedra's avatar

Correct! It will set the headers for all ajax requests from angular.

I will take a look at the other thread. :)

1 like

Please or to participate in this conversation.