I am using AngularJS v1.6.5
When I navigate through the pages of the website normally the URL is in the form of:
https://mySite.co.uk/#!/login
However when I type in an address without the hash-bang the site goes into an infinite loop and the following message is displayed in the browser console output:
WARNING: Tried to load angular more than once.
My route provider looks like this
.config(['$routeProvider', '$httpProvider', function ($routeProvider, $httpProvider) {
$routeProvider
.when('/', {
templateUrl: 'partials/home.html',
controller: 'HomeCtrl'
}).
//ALL ELSE
.otherwise({
redirectTo: '/'
});
I have an interceptor listening for HTTP error messages however this doesn't seem to be being called:
$httpProvider.interceptors.push(['$q', '$location', '$localStorage', function ($q, $location, $localStorage) {
return {
'responseError': function (response) {
if (response.status === 404) {
window.location = "/";
}
return $q.reject(response);
}
};
}]);
One solution I found is to use html5 mode to remove the #! from the URL, then the '.otherwise' route in the route provider catches any other requests.
$locationProvider.html5Mode(true);
Then in the index.html add:
<head>
<meta charset="utf-8">
<base href="/">
</head>
Related
I've my backend developed in Silex with various routes that I can fully access using localhost:8080/project/api/index.php/user (example of a route).
if i'm coding an http get(I use only one controller called mainController ) in a js file using angular 1.5.8, how can I make that when I enter angularsite/user
(user is the route, "/user"), it returns me the data and then show it on my index.html ?
You need to set your app routes to your mainController controller.
angular.module('YourProject', [
'YourModule',
'ngRoute'
])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/user', {
templateUrl: 'view.html',
controllerAs: 'mainCtrl',
controller: 'mainController '
});
$routeProvider.otherwise({
redirectTo: '/user'
});
}])
Now when the page loads the RouteProvider will finds the default path to user (default route) route and that will load your view and controller. The templateUrl contains the route to the template.
If you only want to load a partial page you can do something like this..
'use strict';
var App = angular.module('YourProject', ['ngRoute', 'AppControllers']);
App.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/user', {
templateUrl: 'user.html'
}).
otherwise({
redirectTo: '/path'
});
}]);
Since your question mentions an API if you are trying to just load some data you can call user API with something like this from your controller (provided that the url given is an API which accepts GET request and responds with JSON data)..
var AppControllers = angular.module('AppControllers', []);
AppControllers.controller('MainController', ['$scope','$http', function ($scope, $http) {
//Declare a function to fetch user data from API
$scope.user= function () {
var req = {
method: 'GET',
url: baseURL + 'project/api/index.php/user'
}
return $http(req).success(function (data,status,header) {
$scope.user = data;
}).error(function () {
console.log(error);
});
}
//Call the get user function
$scope.user();
}]);
And finally in your partial file (user.html) add the controller to bind it with the controller declared above.
<!doctype html>
<html ng-app="YourProject">
<body>
<head>
<title>User Page</title>
</head>
<div class="user-content" ng-controller="MainController">
</body>
</html>
Here is my app.js route file in AngularJS
var app = angular.module('myApp', ['ngRoute', 'ngAnimate', 'toaster']);
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.
when('/login', {
title: 'Login',
templateUrl: 'resources/views/layouts/loginUser.php',
controller: 'authCtrl'
})
.when('/', {
title: 'Login',
templateUrl: 'resources/views/layout/login.php',
controller: 'logoutCtrl'
})
.when('/reset', {
title: 'Reset Password',
templateUrl: 'resources/views/layouts/forgetPassword.php',
controller: 'authCtrl'
})
.when('/invalidtoken', {
title: 'Login',
templateUrl: 'resources/views/layout/invalidtoken.php',
controller: 'authCtrl',
role: '0'
})
//$locationProvider.html5Mode(true);
}])
.run(function ($rootScope, $location, Data, $http) {
$rootScope.$on("$routeChangeStart", function (event, next, current) {
var nextUrl = next.$$route.originalPath;
if (nextUrl == '/signin' || nextUrl == '/login' || nextUrl == '/verify' || nextUrl == '/register' || nextUrl == '/registered' || nextUrl == '/reset' || nextUrl == '/resetdone' || nextUrl == '/registersuccess')
{
$location.path(nextUrl);
}
else
{
$location.path('/');
}
});
});
Here i use .run to handle few requests.
I want to remove the # from the url to make the url pretty,
So i did like this to remove the # as suggested here
app.config(['$routeProvider', '$locationProvider'
function ($routeProvider, $locationProvider) {
and in the last line
$locationProvider.html5Mode(true);
But nothing is happening to the application, it stills haves # in the url.
Even i tried this way
How can i achieve this ?
Update :
If i do
.run(function ($rootScope, $location, Data, $http, $locationProvider) {
and in the last line
$locationProvider.html5Mode(true);
I am getting this error
Error: $injector:unpr
Unknown Provider
I have tried in many ways, but none of them working.
Update 2 :
Or can anyone suggest a link of angularjs example which provides example without # in url ?
Why do you want to use the .run() ? Add <base href="/" /> to you <head> or start of your body (first line) and then to match the logic of your .run() try this ( .otherwise("/path") to your $routeProvider):
var app = angular.module('myApp', ['ngRoute', 'ngAnimate', 'toaster']);
app.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider.
when('/login', {
title: 'Login',
templateUrl: 'resources/views/layouts/loginUser.php',
controller: 'authCtrl'
})
.when('/', {
title: 'Login',
templateUrl: 'resources/views/layout/login.php',
controller: 'logoutCtrl'
})
.when('/reset', {
title: 'Reset Password',
templateUrl: 'resources/views/layouts/forgetPassword.php',
controller: 'authCtrl'
})
.when('/invalidtoken', {
title: 'Login',
templateUrl: 'resources/views/layout/invalidtoken.php',
controller: 'authCtrl',
role: '0'
})
. otherwise("/");
$locationProvider.html5Mode(true);
}]);
If you still face issues, I recommend https://github.com/angular-ui/ui-router
Update:
your index.html
<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Green Hopping</title>
<link rel="shotcut icon" type="favicon.ico" href="public/images/favicon.ico" />
<link rel="icon" type="favicon.ico" href="public/images/favicon.ico" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.min.js"></script>
<script src="https://code.angularjs.org/1.4.2/angular-route.min.js"></script>
</head>
<body ng-cloak="">
<base href="/">
<div data-ng-view="" id="ng-view" class="slide-animation"></div>
</body>
<script>
var app = angular.module('myApp', ['ngRoute']);
app.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider.
when('/', {
title: 'Home',
templateUrl: 'home.html',
controller: 'homeCtrl'
})
.when('/login', {
title: 'Login',
templateUrl: 'login.html',
controller: 'authCtrl'
})
.when('/logout', {
title: 'Logout',
templateUrl: 'logout.html',
controller: 'logoutCtrl'
})
.when('/dashboard', {
title: 'Dashboard',
templateUrl: 'dashboard.html',
controller: 'dashboardCtrl'
})
.otherwise('/');
$locationProvider.html5Mode(true);
}])
.run(function ($rootScope, $location, $http, loginSrv) {
$rootScope.$on("$routeChangeStart", function (event, next, current) {
var nextUrl = next.$$route.originalPath;
var orUseUrl = $location.path();
console.log(nextUrl);
if (nextUrl === '/logout'){loginSrv.logout();}
if (nextUrl === '/login'){loginSrv.login();}
if (loginSrv.loggedin === false) { $location.path('/'); }
else { $location.path(nextUrl); }
});
});
app.service("loginSrv",function(){
var ls= this;
ls.loggedin = false;
ls.logout = function(){
ls.loggedin = false;
}
ls.login = function(){
ls.loggedin = true;
}
});
app.controller("homeCtrl",function($scope, loginSrv){
$scope.loggedin = loginSrv.loggedin;
})
app.controller("dashboardCtrl",function($scope, loginSrv){
$scope.loggedin = loginSrv.loggedin;
})
app.controller("authCtrl",function($scope, loginSrv){
$scope.loggedin = loginSrv.loggedin;
})
app.controller("logoutCtrl",function($scope, loginSrv){
$scope.loggedin = loginSrv.loggedin;
})
</script>
</html>
All other templates are same like this. Copy paste the following for home.html , login.html , dashboard.html , logout.html . Plunker will not be able to show issues with routes for client side. Try this. This is completely functional code.
<div>
<div>Home |
Login |
Dashboard |
Logout</div>
<div> This is from the home/login/dashboard/logout Controller. Loggedin: {{loggedin}}</div>
</div>
try changing:
$locationProvider.html5Mode(true);
To:
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
You can't inject $locationProvider in .run because providers are only available for .config
This answer should help but you will have 2 issues:
You will need a check before $locationProvider.html5Mode(true) as it will not work on IE 10 or older.
if(window.history && window.history.pushState){
$locationProvider.html5Mode(true);
}
This will work only by removing the # when the user enters it in the url, i.e. if the users types app/#/login it will change to app/login. However, if the user bookmarks or copies the changed url app/login and enters that in the browser he will get an error as the server does not know about angular routing since it is client side only. In the thread I linked above you may find some comments on how to fix this.
Are you by chance receiving a 404 error ? Whenever you change the mode to html5history you have to tell the web server to always return the index page no matter what URL is requested. As the angular docs says here:
https://docs.angularjs.org/guide/$location
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links to entry point of your application (e.g. index.html). Requiring a <base> tag is also important for this case, as it allows Angular to differentiate between the part of the url that is the application base and the path that should be handled by the application.
Have you tried doing that and configured it properly ?
Also remember that you have to specify the base entry href of your application for it to work like
<base href="/" />
The problem is it is missing the service injection.
So in config section try injecting $locationProvider like below.
app.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider.
when('/login', {
title: 'Login',
templateUrl: 'resources/views/layouts/loginUser.php',
controller: 'authCtrl'
})
.when('/', {
title: 'Login',
templateUrl: 'resources/views/layout/login.php',
controller: 'logoutCtrl'
})
.when('/reset', {
title: 'Reset Password',
templateUrl: 'resources/views/layouts/forgetPassword.php',
controller: 'authCtrl'
})
.when('/invalidtoken', {
title: 'Login',
templateUrl: 'resources/views/layout/invalidtoken.php',
controller: 'authCtrl',
role: '0'
})
$locationProvider.html5Mode(true);
}])
With html5mode you have to specify the base url in your html file for eg,
<base href="/">
Some links you can go through for your reference,
https://docs.angularjs.org/error/$location/nobase
https://scotch.io/quick-tips/pretty-urls-in-angularjs-removing-the-hashtag
it does not look possible you to remove hash-bang (#) from href manually, and it causes malfunction your router because hashbangs are enables browser not treat your state in order to send get request to an end point. browser does not fire a get request over #/route while it does over /route, unless you use your url provider in html5 mode. For html5 mode, add the line below into your app.config function:
$locationProvider.html5Mode(true).hashPrefix('!')
I have an Angular app which makes some calls (POST and GET for now) to a backend service (powered by node.js with a REST interface). While developing the app itself I noticed it makes two requests to the backend each time a button is pressed or a page is loaded. Curiously everything works but each time I press some button the backend gets two requests. I am not using any fancy package only ngRoute, ngResource and routeStyles for css partials. Anybody has an idea of what could be the reason why the app behaves like that?
I actually found another question similar to this one but the OP there was using express aside of Angular and there is no answer...
EDIT added some code.
in app.js:
'use strict';
var cacheBustSuffix = Date.now();
angular.module('myApp', ['myApp.controllers', 'myApp.services', 'myApp.filters', 'ngRoute', 'ngResource', 'routeStyles'])
.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$locationProvider
.html5Mode({enabled: true,
requireBase: false})
.hashPrefix('!');
$routeProvider
.when('/', {redirectTo: '/myApp'})
.when('/myApp', {
templateUrl: '/partials/home.html?cache-bust=' + cacheBustSuffix,
controller: 'ctrlHome'
})
.when('/myApp/search', {
templateUrl: '/partials/search.html?cache-bust=' + cacheBustSuffix,
controller: 'ctrlSearch'
})
.when('/myApp/list/', {
templateUrl: '/partials/list.html?cache-bust=' + cacheBustSuffix,
controller: 'ctrlList'
})
// a bunch of other redirections
.otherwise({
templateUrl: '/partials/404.html?cache-bust=' + cacheBustSuffix,
controller: 'ctrl404'});
}]);
from services.js:
'use strict';
var app = angular.module('myApp.services', ['ngResource']).
factory('List', function ($resource) {
return $resource(WSROOT + '/search', {}, {get: {method: 'GET', isArray: false}});
});
from controllers.js, one controller that makes multiple requests
var controllers = angular.module('myApp.controllers', []);
var ctrlList = controllers.controller('ctrlList', function ($scope, $window, List) {
$window.document.title = 'myApp - List';
List.get({}, function (data) {
// $scope.res is an array of objects
$scope.res = data.response;
$scope.nitems = data.response.length;
});
});
ctrlList.$inject = ['$scope', 'List'];
And the network call when loading the index+home and navigating to some other page. As you can see, it first loads the index page, the scripts and styles listed there (not shown), then the home where I have a controller similar to the one above and suddenly two wild request to my web server:
Can we see your HTML files? I had this problem a while back. My solution was that by declaring a controller in the routing, and in the pages, a double post was created as each controller was loaded twice.
//Home
.state('tab.home', {
url: '/home',
views: {
'tab-home': {
templateUrl: 'templates/tab-home.html',
controller: 'HomeCtrl' // <-- This goes away
}
}
})
I have an rest api, in which the api is sending instruction to redirect (301 is being sent).
And I have my angular js code like this:
var request = $http.post(LOGIN_URL,{username:'tes',password:'test'})
request.success(function(html)
{
if(html.failure) {
console.log("failure")
$scope.errorMessage = html.failure.message
}
else {
console.log("Success here....")
$location.path("route")
}
})
I can see in the browser log that it is coming in the else part ( Success here..... is being printed). But the url is not changed. $location.path doesnt do anything; I have also tried $location.url which also results the same thing.
And also I'm injecting the $location to my controller.
Where I'm making mistake?
Thanks in advance.
Try something like this
$location.path("/myroute")
You have to have a ng-view on the page as well.
Also make sure you have a corresponding view with that name
and when you're registering your controller you have the '$location' var being injected in the declaration of your controller like this example:
controllers.controller('MyCtrl', ['$scope', '$route', '$location', 'MYService',
function($scope, $route, $location, MyService) {
// ... controller code
}])
also you might want to debug your route changing to see what is happening with a location change listener, like in this example:
return app.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/home', {
templateUrl: 'partials/home.html',
controller: 'MyCtrl'
}).
when('/login', {
templateUrl: 'partials/login.html',
controller: 'LoginCtrl'
}).
when('/error/:errorId', {
templateUrl: 'partials/error.html',
controller: 'ErrorCtrl'
}).
otherwise({
redirectTo: '/home'
});
}]).run(function($rootScope, $injector, $location) {
$rootScope.$on("$locationChangeStart", function(event, next, current) {
console.log('current=' + current.toString());
console.log('next=' + next.toString());
});
});
});
I'm trying to implement basic authentication routing in AngularJS. I have a model that has a authorize method that returns a promise. I want the routing to wait until that authorize function has returned true or false to continue, once that has completed it should resume the path or redirect the user to the login page.
I think essentially i need to stop routing, call that method and then resume or redirect to login. Below is the code I have so far but i'm not sure how to accomplish the pause/resume. Any ideas?
return angular.module('d', ['ngCookies', 'ngRoute'])
.config(['$routeProvider', '$locationProvider', '$httpProvider',
function ($routeProvider, $locationProvider, $httpProvider) {
$routeProvider.when('/',
{
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
});
$routeProvider.when('/login',
{
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
});
$routeProvider.otherwise({ redirectTo: '/404' });
$locationProvider.html5Mode(true);
// handle unauthorized requests by redirecting to login page
$httpProvider.responseInterceptors.push(
['$location', '$q', function ($location, $q) {
function success(response) {
return response;
}
function error(response) {
if (response.status === 401) {
$location.path('/login');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
return function (promise) {
return promise.then(success, error);
}
}]);
}])
.run(['$rootScope', '$location', 'Auth', function ($rootScope, $location, Auth) {
$rootScope.$on("$routeChangeStart", function (event, next, current) {
$rootScope.error = null;
Auth.authorize().then(function(){
$location.path('/');
},function(){
$location.path('/login');
});
});
}]);
Your solution is very similar to a prototype I wrote a while back.
The idea is that whenever you "touch" the server and get an authentication error, a modal window pops-up asking for a login without changing the URL (you let it change to the new URL and stay there).
My implementation was also based on an interceptor checking for 401. It has a dependency on $rootScope and sets a property "needsLogin" to true. The page template has the login modal window visible when needsLogin === true and hides the ng-view (this is important since the new route has been loaded but it misses its data). Finally, upon successfull login, the login controller does the $route.reload() and then sets $rootScope.needsLogin = false.
Small snippets:
<div id="main" role="main" ng-show="needsLogin === false">
<div ng-view></div>
</div>
<div ng-show="needsLogin === true" ng-include="'views/login.html'" class="overlay"></div>
The login controller can be something like:
function LoginCtrl($scope, $rootScope, $route, $http) {
$scope.login = function () {
$http.post( /* Somehow do your login */ )
.success(function () {
var deregister = $rootScope.$on('$routeChangeSuccess', function () {
// hide login / show ng-view after route has been reloaded
$rootScope.needsLogin = false;
deregister();
});
$route.reload();
})
.error(function () {
/* handle errors */
});
};
}
$route.reload() is not a full page refresh, it merely re-initializes the route (controller/view etc). Hopefully, the call that was rejected before will run again and the page will be fine.