AngularJS and Laravel 4 routing conflict in HTML5 mode
Asked Answered
C

6

9

I would like to remove the # hash from URLs using Angularjs' $locationProvider.html5Mode(true).

Example: The address bar displays http://localhost/shop instead of http://localhost/#/shop.

Everything works well untill I refresh a page. If i refresh, the following Laravel Route (defined in routes.php) is accesed

Route::resource('shop', 'ShoppingController')

not the AngularJS Route (defined in app.js)

$routeProvider.when('/shop', { 
    templateUrl: 'templates/shop.html', 
    controller: 'ShoppingController' 
});

My Code:

routes.php (Laravel Routes)

Route::get('/', function() {
    return View::make('index'); 
});
Route::resource('shop', 'ShoppingController'); 

app.js (AngularJS Routes)

var app = angular.module('shoppingApp',['ngRoute','SharedServices']); 

app.config(function($routeProvider, $locationProvider) { 
    $routeProvider.when('/shop', {
        templateUrl: 'templates/shop.html', 
        controller: 'ShoppingController'
    });
    $routeProvider.otherwise({ redirectTo: '/' }); 
    $locationProvider.html5Mode(true); 
});

My directory structure:

Project
    /app 
        /...
        /views
            -index.php (single page application file)
            -routes.php (Laravel routes)
    /public
        /...
        /js
            -angular.js
            -app.js
        -index.php (Laravel index file)

Tried Solutions:

Rewrite the htaccess file so that all requests are redirected to index.php (the single page application file, from where AngularJS would take over the routing). Problem: In this way the Laravel route (Route::resource('shop', 'ShoppingController'); - necessary for interaction with the database) becomes inaccessible to the AngularJS $http service:

app.js

app.controller("ShoppingController", function($scope, $http) {
    $http.get('/shop', { cache: true}).
        success(function(data, status) {
        $scope.items = data
    }).
    error(function(data, status) {
        console.log('Status: ' + status);
        });
});

Question: How can I solve the routing problem, so that the AngularJS route, not the Laravel Route gets accessed if I refresh localhost/shop?

Colossians answered 25/1, 2014 at 22:48 Comment(0)
R
7

From what I read, it seems like Laravel is reading the modified route when you refresh the page. In this case, you should make Laravel continue to make the original view even if it would otherwise be a 404 redirect.

Try adding the following somewhere on the Laravel side (Ex. routes.php)

App::missing(function($exception)
{
    return View::make('index');
});

Note: You might want to have AngularJS's routing use .otherwise to handle pages that are not found.

Renaterenato answered 2/3, 2014 at 8:40 Comment(2)
Thanks for your reply. I added such a catch all route and prefixed all my backend routes (Route::group(array('prefix' => 'api'), function(){}) as suggested in this excellent tutorial: scotch.io/tutorials/php/…Colossians
I think this will avoid 404 error pages. so our app wiil not have any 404 page!Hrutkay
L
5

A better solution is to redirect this way:

'Redirect::to('/#/' . Request::path())'

When you refresh or go to the URI directly:

  • 'Request::path()': returns the requested URI i.e. ('/shop/categories/electronics');
  • AngularJS in 'html5Mode' still responds to the '#/' prefix;
  • If angular detects the prefix when in HTML5 mode it will remove the prefix for you.

Final solution:

App::missing(function($exception) {
    return Redirect::to('/#/' . Request::path());
});
Lauritz answered 18/5, 2015 at 13:34 Comment(0)
B
3

If you are using Laravel 5 then go to app/Exception/Handler.php and place the code below:

public function render($request, Exception $e)
{
    if($e instanceof NotFoundHttpException)
    {
        return Redirect::to('/#/' . Request::path());
    }
    return parent::render($request, $e);
}
Beverlee answered 16/9, 2015 at 1:3 Comment(0)
A
0

If you wana have more than one single page application running in html5mode or just have another use for App::missing inside a Laravel app you migh use a rewrite rule like this:

#Redirect base url of AngularJS app in html5mode

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^/path/.+$
RewriteRule  ^(path)/(.*)  /path/#/$2           [R=301,L,NE]
Anabolite answered 17/4, 2014 at 17:3 Comment(0)
C
0

I have another solution which I found quite useful. Rather than just making home page view, I pass in the URI to the home page, which will get checked by a controller and redirect accordingly (the Angular way). This means that if you are on myapp.com/about and you refresh, instead of taking you home, it takes you back to the page you were currently on.

routes.php: Notice that I have a URI wildcard that I pass in as an argument to the callback function, then as a variable to the view.

// Note that this must be on the bottom of your routes file because
// if you have any registered route with a similar pattern
// it will get caught by this route and never reach any registered routes you might have
Route::get('{slug}', function($slug){
    return View::make('index', compact('slug'));
});


// These routes will never get hit, so move them above Route::get('{slug}')
Route::get('about', function(){...});
Route::get('contact', function(){...});

index.blade.php:

<html ng-app"myApp">
<head>
    ...
</head>
<body>
    <!--Check if there is a variable named $slug that is set-->
    <!--If it is, set hidden input with ng-model-->
    @if(isset($slug))
        <input type="hidden" value="{{slug}}" ng-model="slug" ng-controller="RedirectController">
    @endif

    <div ng-view></div>
</body>
</html>

app.js

angular.module('myApp', [])
    .controller('RedirectController', ['$location', '$scope', function ($location, $scope) {
        // If slug is set, redirect to that slug
        if ($scope.slug) {
            $location.path('/' + $scope.slug);
        }
    }]);
Commonplace answered 24/10, 2014 at 23:34 Comment(0)
H
0

For Laravel 4.x or 5.x i use this simple and nice trick and there is no need to change .htaccess file. this trick is very simple and works for me. it doesn't redirect URL and user will stay on same URL when refresh page and page will refresh:

Route::get('dashboard/{all?}', function(){
    return view('main.index');
});

here my dashboard app is my SPA. this way we can resolve 404 errors pages too.

Hrutkay answered 27/5, 2016 at 13:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.