how to marry laravel and angularjs routing - angularjs

I have an angularjs (version 5) app running in laravels public folder.
angularjs should serve the ui aka frontend. whilst laravels "only" route is the backend api access to the data(base) serving the ui at /api/....
How do I marry both?
My angularjs app resides in /public/ui/ so I currently just have a route in laravel like so:
Route::get('/', function () {
return Redirect::to('/ui/dist');
});
This works partially. It works from within the angularjs app like expected. But when I call anuglarjs routes they will fail to display because of course they don't exist in laravel.
Going with the angularjs tutorial for example:
If I call /ui/dist/heroes it will display a 404 instead of the angularjs app.
Note: Laravels public folder is symlinked to the webroot.
Edit:
I redirect to /ui/dist as I use angulars builder to build its files and these are reflected in the index.html also generated with the builder.
The index.html looks like this:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Ui</title>
<base href="./">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="styles.5cf98968a6d57e778c48.bundle.css" rel="stylesheet"/><!-- the hash changes on every build -->
</head>
<body>
<app-root></app-root>
<script type="text/javascript" src="inline.2379cc013d19d70a8003.bundle.js"></script> <!-- the hash changes on every build -->
<script type="text/javascript" src="polyfills.ad37cd45a71cb38eee76.bundle.js"></script> <!-- the hash changes on every build -->
<script type="text/javascript" src="main.99c0191843a51dda5d54.bundle.js"></script> <!-- the hash changes on every build -->
</body>
</html>

Here is how you can integrate laravel and angular routing.
In your routes.php, add this route:
Route::get('{slug}', function () {
return view('index');
}); // this will ensure all routes will serve index.php file
Route::get('/', function () {
return view('index');
});
In your index.php of laravel view, add all necessary angular js config and controller like normally you do with angular projects.
Note: for each $http request urls you have to define it in laravel route file.
Now when any url is hit, it will serve the index.php file and then angular routing will serve the correct file.

In order to make the routing systems of Angular (7+) and Laravel (5.8) coexist effectively is to redirect all the 404 errors that Laravel intercepts to the Angular router.
You have to edit the render function in app/Exceptions/Handler.php:
public function render($request, Exception $e)
{
// handle Angular routes
if ($e instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
$url = parse_url($request->url());
$angular_url = $url['scheme'] . '://' . $url['host'] . '/#' . $url['path'];
return response()->redirectTo($angular_url);
}
return parent::render($request, $e);
}
Now, let's say that you want to show a login form using angular navigating to /auth/login. When you hit that route Laravel will kick in, and not recognising the route will then pass the control to the Exception Handler. What we're doing in the render() function is simply tell Laravel to rewrite the URL and redirect to /#/auth/login.
Then, you have to enable to enable HashLocationStrategy in your Angular application, like so:
RouterModule.forRoot(routes, {useHash: true})

Related

Angular js application is not loading after using ['ui.grid']

I am new to angular js. I have downloaded ui-grid.js and ui-grid.css to implement ng-grid in our existing angular js project. I have added the path for both of them in Index.html like below.
<link href="app/assets/vendor/css/ui-grid.css" rel="stylesheet" />
<script src="app/assets/vendor/scripts/ui-grid.js"></script>
But when I try to use it in tPController.js file like below, the page is not loading, application itself is not coming up.
(function () {
angular.module('dApp',['ui.grid']).controller('tPController', function ($timeout, $scope) {
var vm = this,
---------------
});
})();
Note that i have not yet started using ng-grid in the html file. Only using ['ui.grid'] in .js file is causing the issue.

Use HTTPD's mod_rewrite and AngularJS RouteProvider with Parameters

I am trying to use HTTPD's mod_rewrite and AngularJS's routeProvider to allow users to access a webpage with a clean URL that will change its content based on some parameters in the URL.
For example, let's say there are profile pages for JohnSmith and JaneDoe. I want them to be able to access their pages via URL, like so:
http://my.example.com/JohnSmith
I want the AngularJS code to be able to grab the name "JohnSmith" from the URL, and use that for the REST call thusly:
$http.get('http://my.example.com/profile/name/' + $routeParams.profile)
So far, I have managed to get AngularJS to grab the correct profile IF the URL is formatted like one of the following:
http://my.example.com/#/JohnSmith
http://my.example.com/index.html#/JohnSmith
Here is the relevant code thus far:
index.html:
<html ng-app="profile">
<head>
<title>Profile</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js">
</script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-route.js"></script>
<link rel='stylesheet' href='style/style-main.css' type='text/css' />
<script src="js/app.js"></script>
</head>
<body>
<ng-view></ng-view>
</body>
</html>
app.js:
(function() {
var app = angular.module('profile', ['ngRoute']);
app.config(function($routeProvider) {
$routeProvider.when("/:profile",
{
templateUrl: "profile.html",
controller:"ProfileController",
controllerAs: "profile"
});
});
app.controller("ProfileController", [ '$http', '$routeParams', function($http, $routeParams) {
var SELF = this;
SELF.profile = $routeParams.profile;
console.log(SELF.profile);
}) ();
Using a clean URL like this:
http://my.example.com/JohnSmith
...will return a 404.
So I'm trying to use Apache's mod_rewrite to change the URL into a form that AngularJS can understand and extract the 'JohnSmith' parameter from.
I've got the following RewriteRule in place on my Apache node:
RewriteRule "(^[^\/#\.]+$)" "#/$1"
I've checked it out, and I know that this RewriteRule should trigger on any URL that doesn't contain any extra slashes, pound signs, or periods. Therefore, it should rewrite a URL like this:
http://my.example.com/JohnSmith
to this:
http://my.example.com/#/JohnSmith
That's a form that I know AngularJS can serve content for correctly, so after Apache rewrites the URL into that form, it should work, right?
Well, with that RewriteRule in place, trying to access the clean URL no longer results in a 404. In fact, if I right-click and select 'View Source', I can see that my browser has definitely attempted to load the HTML page. It looks like the routeProvider is no longer picking up the parameter from the URL, however.
How can I resolve this issue so the HTTPD RewriteRule and AngularJS routeProvider work together to provide the correct content using the clean URL?

AngularJS routing doesn't work with templateUrl?

I tried so hard to get this work but all my tries failed. I'm trying to learn Angular routing, I started very simple: created 4 files in the same folder: index.html, page1.html, page2.html and page3.html.
this is the index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>routing</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<script src="D:\Developer Library\MyAngular\angular.min.js"></script>
<script src="D:\Developer Library\MyAngular\Scripts\angular-route.js"></script>
<body ng-app="myApp">
one
two
three
<div ng-view></div>
<script>
var app = angular.module('myApp', ['ngRoute']);
app.config(function ($routeProvider) {
$routeProvider
.when('/page1', { templateUrl: 'D:\Developer Library\dom\AngularRouting\page1.html' }).
when('/page2', { template: '<h1>page2.html</h1>' })//the template is working fine unlike templateUrl
.when('/page3', { template: '<h1>page3.html</h1>' });
//page1.html, page2.html, page3.html are just files holding headers with some text.-->
})
</script>
</body>
</html>
I got these errors in the console window:
XMLHttpRequest cannot load
file:///D:/Developer%20LibrarydomAngularRoutingpage1.html. Cross
origin requests are only supported for protocol schemes: http, data,
chrome, chrome-extension, https, chrome-extension-resource.
and:
Error: [$compile:tpload]
http://errors.angularjs.org/1.5.8/$compile/tpload?p0=D%3ADeveloper%20LibrarydomAngularRoutingpage1.html&p1=-1&p2=
at angular.min.js:6
at angular.min.js:156
at angular.min.js:131
at m.$eval (angular.min.js:145)
at m.$digest (angular.min.js:142)
at m.$apply (angular.min.js:146)
at HTMLBodyElement. (angular.min.js:115)
at Sf (angular.min.js:37)
at HTMLBodyElement.d (angular.min.js:37)
I made every possible change to get it work: I changed the href value of the anchor elements to /#/page1, #/page1, /page, page , I also changed the templateUrl value to similar values (my last try was the full path of the file!)
I'm actually confused between the href value and the first parameter of when method and the templateUrl, so I have some questions that I think will help me understand how routing work in angular:
What each of them refers to: are the href and the first argument of when method the same?
Can I assign the href attribute any value, and refer to it in the when argument?
Is templareUrl value related to the location of the current file(index.html),
What does the hash symbol # mean and why it's important?
Angular is loading templates via AJAX, and AJAX can not access local file system.
You must run you app on a server (you can use local server) for templateUrl to work.

Grails asset images in AngularJS templates

I'm using Grails 2.4.4 and AngularJS 1.3.15 to create a simple application. To make templates work with Grails I use the AngularJS Template Asset-Pipeline Plugin 2.0.7.
My first prototype was not using any routing logic from ngRoute. Now I want to extract parts of the growing application and put them in separate templates. This almost worked but I have a problem with images. Previously I used constructs like this to load images as assets:
<img src="${assetPath(src: 'image.png')}">
That did work because the whole app was a .gsp page. Extracting the code into templates this processing does not happen anymore and the images cannot be loaded anymore.
My current workaround is:
<img src="../assets/image.png">
Obviously this technique has some drawbacks. Hardcoding the asset location with a relative path can be very fragile. Did I miss some nice feature or command that could help me here merging AngularJS with Asset Pipeline?
I 'got around' this issue by adding an 'AssetService' to my Angularjs project.
angular.module('myApp.assets')
.service('AssetService', [ function () {
this.getAssetDir = function() {
return window.grailsSupport.assetsRoot;
};
this.getRootDir = function() {
return window.grailsSupport.root;
};
}]);
This requires some code in your Grails Layout File....
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title><g:layoutTitle default="MyApp"/></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="${assetPath(src: 'favicon.ico')}" type="image/x-icon">
<link rel="apple-touch-icon" href="${assetPath(src: 'apple-touch-icon.png')}">
<link rel="apple-touch-icon" sizes="114x114" href="${assetPath(src: 'apple-touch-icon-retina.png')}">
<!-- add this to your layout -->
<g:javascript>
window.grailsSupport = {
assetsRoot : '${ raw(asset.assetPath(src: '')) }',
root : '${createLink(uri: '/')}'
};
</g:javascript>
<asset:stylesheet src="application.css"/>
<asset:javascript src="application.js"/>
<g:layoutHead/>
</head>
I got the window.grailsSupport code in an answer to another stack overflow question.
You can now use the AssetService to prefix your URLs in your controllers...
angular.module('MyApp.profile')
.controller('MyCtrl', [ 'AssetService', '$http', function(AssetService, $http) {
var self = this;
self.pathToAnonProfile = AssetService.getAssetDir() + "ppt_Anonymous.jpg";
$http.get(AssetService.getRootDir() + 'api/v1/users').then(function(response) {
....
}, function(errResponse) {
....
});
}]);
It could most likely be improved and you still need to mix gsp and pure Angular templates but it keeps the Asset path in a single service which can easily be modified.
It looks like the tag and the corresponding assetPath() are broken in this respect. They do not appear to respect the serverURL setting in config.groovy or calculate the base path properly when rendering in a template.
I was able to work around the issue in a less fragile way using the createLink() tag to generate my path. It fixes the name of the asset controller, but I think it unlikely that they will change that.
In my case I was resolving some flag images within an AJAX callback.
<img src='${createLink(controller: "assets", action: "flags/16/US.png")}' />
Hopefully this works for you as well.

Routing single page application using angular js

I am building a web Single Page Application using AngularJS. I need that clicking on link change URI in client browser without http request.
http://example.com/ ---> it shows my single page application and clicking on a specific link I need the URL is http://example.com/about but without send http request and show hidden div.
I don't know what you precisely want to do but if you only want do one http request you can perhaps use angular ui router with something like
$stateProvider
.state('main', {
url: "/",
templateUrl: "main.html"
})
.state('about', {
url: "/about",
templateUrl: "main.html",
controller: function($scope) {
$scope.showDiv = "true";
}
})
That way you can switch state and because everything you need is already loaded, nothing gets loaded anymore. Or perhaps you can also use parameters.
But why is it so bad to have one additional request? That would be something interesting to know! :)
EDIT: The easy approach with $location
(https://docs.angularjs.org/guide/$location)
index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example</title>
<base href="/">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="html5-mode">
<div ng-controller="LocationController">
<button ng-click="changeUrl()">Change url</button>
</div>
</body>
</html>
app.js:
angular.module('html5-mode', [])
.controller("LocationController", function ($scope, $location) {
$scope.$location = {};
$scope.changeUrl = function () {
// https://docs.angularjs.org/guide/$location
console.log("The current path: " + $location.path());
console.log("Changing url...");
$location.path('/newValue')
};
})
.config(function ($locationProvider) {
$locationProvider.html5Mode(true).hashPrefix('!');
})
Be sure to set the basePath correct.
Take a look at html2js. This is a grunt task to convert your html templates into a pre-cached js file.
Ideally you would run this as part of your build process. As well you can run a watch task to compile your HTML templates into the pre-cache whenever you save a template -- this is nice for development.
If you are already using gulp, there is a package for you. There are many alternatives to html2js that do essentially the same thing. So if it doesn't suit your needs, try another.
So with this in place, when you navigate to another page -- the HTML template will just be pulled out of angular's cache, and not grabbed from the server.

Resources