document.ready called in infinite loop with angular.js - javascript

I am building an app using Angular.js, Node.js, and Socket.io (among other things). My issue is that when I try to follow a link routing me to a login page, I end up in an infinite loop. The jquery document.ready function is called over and over, and each time this happens another socket connects to the user. The page won't even load because this keeps getting called. I am really stuck, so any help would be greatly appreciated.
Here is the configuration for the client side routing:
window.app = angular.module('MEAN', ['ngCookies', 'ngResource']);
window.app.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/', { templateUrl: 'views/index.html' }).
when('/signin', {
templateUrl: '/partials/signin',
controller: SigninCtrl
}).
when('/signup', {
templateUrl: '/partials/signup',
controller: SignupCtrl
}).
otherwise({redirectTo: '/'});
}]);
//Removing tomcat unspported headers
window.app.config(['$httpProvider', function($httpProvider, Configuration) {
//delete $httpProvider.defaults.headers.common["X-Requested-With"];
}]);
//Setting HTML5 Location Mode
window.app.config(['$locationProvider', function($locationProvider) {
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix("!");
}]);
Here is the controller:
function SigninCtrl($scope, $http, $location) {
$scope.form = {};
$scope.title = "Sign In";
$scope.SignIn = function() {
$http.post('/users/session', $scope.form).
success(function(data){
console.log("Successful sign in", data);
$location.path('/');
})
.error(function (data) {
console.log("There was an error");
$scope.errors = data.errors;
});
};
}
And here are the jade templates I am using for the partials:
extends ../layouts/default
block content
.row
.offset1.span5
a(href="/auth/facebook")
img(src="/img/icons/facebook.png")
a(href="/auth/github")
img(src="/img/icons/github.png")
a(href="/auth/twitter")
img(src="/img/icons/twitter.png")
a(href="/auth/google")
img(src="/img/icons/google.png")
.span6
if (typeof errors !== 'undefined')
.fade.in.alert.alert-block.alert-error
a.close(data-dismiss="alert", href="javascript:void(0)") x
ul
each error in errors
li= error.type
block auth
extends auth
block auth
form.signin.form-horizontal(class="simple-form")
.control-group
label.control-label(for='email') Email
.controls
input#email(type='text', name="email", placeholder='Email')
.control-group
label.control-label(for='password') Password
.controls
input#password(type='password', name="password", placeholder='Password')
.form-actions
button(ng-click='SignIn()') Sign in
| or
a.show-signup(href="/signup") Sign up
And here is the document ready function:
window.bootstrap = function () {
angular.bootstrap(document, ['MEAN']);
}
window.init = function () {
window.bootstrap();
}
window.connectSocket = function(){
var socket = io.connect();
socket.on('connect', function(message){
console.log("socket connected");
});
}
$(document).ready(function () {
//Fixing facebook bug with redirect
console.log("Document ready!");
if (window.location.hash == "#_=_") window.location.hash = "";
window.init();
window.connectSocket();
});

I feel dumb, but I figured out the issue. Similar to this issue: What web server configuration is required to make AngularJS routing function correctly?
I actually moved the routing from the server to the client earlier and in the partial template I had include auth and in the auth file I had an include for the header template, something that angular did already. In the end it was trying to include the same header in a loop... Hope this might help someone with the same issue later. Just make sure you don't have the header included multiple times...

Related

Difficulty defining new custom provider in angularJS script?

I have a webapp that I would like to have some consistent themes across pages. To do so, I have attempted to modify the routeProvider provider to allow for utilization of such a template. As far as I can tell, the ways to do this are to define the provider, configure it, include it in the controller scope, and then inject it into the controller. I believe I have done this. Here are the relevant code blocks:
var webApp = angular.module('myApp.module', ['ngRoute','ngCookies','angular-jwt']);
webApp.provider('extendedRouteProvider', function($routeProvider) {
this.$get = function() {
return {
when: function(path,route) {
if (route.layOutTemplateUrl) {
route.resolve = route.resolve || {};
route.resolve.layOutTemplateUrl = function (){
return route.layOutTemplateUrl;
};
}
$routeProvider.when(path,route);
}
}
}
});
webApp.config(function(extendedRouteProvider) {
extendedRouteProvider
.when('/', {
templateUrl: 'webapp_loginScreen_UIDesign.html',
controller: 'loginScreenController'
})
//Could add the following to after the .html bellow in the file if we want screen to load at anchor point specified in the HTML file: #!/mainPage...
//This would also require adding it into the $location.path within the screenController function if so
.when('/mainPage', {
templateUrl: '/UI Design/Main Page/HTML/webapp_mainPageScreen_UIDesign.html',
controller: 'mainPageScreenController',
layOutTemplateUrl: '/UI Design/webapp_mainLayoutTemplate.html'
})
.otherwise({
redirectTo: '/'
});
});
function loginScreenController($http, $cookies, $location, $route,extendedRouteProvider) {
var vm = this;
var layOutTemplateUrl = $route.current.layOutTemplateUrl;
// do stuff
const serverURL = "URL";
$location.path('/mainPage');
}
webApp.controller('loginScreenController',loginScreenController);
loginScreenController.$inject = ['$http','$cookies','$location','$route','extendedRouteProvider'];
However, this code, when running via VSCode's live server, gives me an $injector:unpr error. I don't understand why this is happening. Note that I 'enter' the webapp through an HTML page which does not itself utilize the layout template, but $location.path('/main') should be sending the user to the mainpage, which does utilize the layout template.

Detect route change in an Angular service

I'm trying to create a service to check if a certain route needs a user to be logged in to access the page. I have a working code but I want to place the $scope.$on('routeChangeStart) function inside the service. I want to place it in a service because I want to use it in multiple controllers. How do I go about this?
Current code:
profileInfoCtrl.js
angular.module('lmsApp', ['ngRoute'])
.controller('profileInfoCtrl', ['$scope', '$location', ' 'pageAuth', function($scope, $location, pageAuth){
//I want to include this in canAccess function
$scope.$on('$routeChangeStart', function(event, next) {
pageAuth.canAccess(event, next);
});
}]);
pageAuth.js
angular.module('lmsApp')
.service('pageAuth', ['$location', function ($location) {
this.canAccess = function(event, next) {
var user = firebase.auth().currentUser;
//requireAuth is a custom route property
if (next.$$route.requireAuth && user == null ) {
event.preventDefault(); //prevents route change
alert("You must be logged in to access page!");
}
else {
console.log("allowed");
}
}
}]);
routes.js
angular.module('lmsApp')
.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider){
$routeProvider
.when('/admin', {
templateUrl: 'view/admin.html',
css: 'style/admin.css',
controller: 'adminCtrl',
requireAuth: true //custom property to prevent unauthenticated users
})
.otherwise({
redirectTo: '/'
});
}]);
By using $routeChangeStart, you are listening to a broadcast sent by $routeProvider on every change of the route. I don't think you need to call it in multiple places ( controllers ), just to check this.
In your service:
angular.module('lmsApp')
.service('pageAuth', ['$location', function ($location) {
var canAccess = function(event,next,current){
var user = firebase.auth().currentUser;
//requireAuth is a custom route property
if (next.$$route.requireAuth && user == null ) {
event.preventDefault(); //prevents route change
alert("You must be logged in to access page!");
}
else {
console.log("allowed");
}
}
$rootScope.$on('$routeChangeStart',canAccess);
}]);
And then inject your service in the .run() part of your application. This will ensure the check will be done automatically ( by the broadcast as mentioned earlier ).
In you config part :
angular.module('lmsApp')
.run(function runApp(pageAuth){
//rest of your stuff
});
You would add an event handler in the Service to $rootScope instead of $scope.
Also it would be much better if you would add the $routeChangeSuccess in the ".run" section so all the pages can be monitored from one point rather than adding it in every controller
You need to listen $rootScope instead of $scope.
And I think it's better to call that listener on the init of wrapped service, for instance (Services are singletons, so it will be run once you will inject it to any controller).
angular.module('lmsApp')
.service('stateService', ['$rootScope', function ($rootScope) {
$rootScope.$on('$locationChangeStart', (event, next, current) => {
// do your magic
});
}]);

How to get my angular controller to reload data

I have an angular app with three views. When it loads it runs some code to populate the $scope variables. When I change views and then go back to the controller I want the initial code to run again but it doesn't. It seems it is cached and the $scope variables are not updated based on what happened.
How can I force the controller to run the initialisation code every time the view is loaded?
My routes:
app.config(function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'HomeController',
templateUrl: 'home.html'
})
.when('/teach', {
controller: 'TeachController',
templateUrl: 'teach.html'
})
.otherwise({
redirectTo: '/'
});
});
The code I want to run every time the '/' route is clicked:
getSubPools.success(function(data) {
$scope.userPools = data;
});
Controller in full:
app.controller('HomeController', ['$scope', '$filter', 'stream', 'removeDroplet', 'qrecords', 'helps', 'get_user', 'updateRecords', 'getSubPools', function($scope, $filter, stream, removeDroplet, qrecords, helps, get_user, updateRecords, getSubPools) {
get_user.success(function(data) { //get current user
$scope.user = data;
});
getSubPools.success(function(data) {
$scope.userPools = data;
});
stream.success(function(data) {
$scope.stream = data;
if ($scope.stream.length === 0) { //determine if user has stream
$scope.noStream = true;
} else {
$scope.noStream = false;
}
$scope.getNumberReady(); //determine if any droplets are ready
if ($scope.numberReady === 0){
$scope.noneReady = true;
} else {
$scope.noneReady = false;
$scope.stream = $filter('orderBy')($scope.stream, 'next_ready'); //orders droplets by next ready
}
});
$scope.showEditStream = true;
$scope.showStream = false;
$scope.rightAnswer = false;
$scope.wrongAnswer = true;
$scope.noneReady = false;
$scope.subbedDroplets = [];
$scope.focusInput = false;
}]);
You can use the $routeChangeStart and $routeChangeSuccess events to reload the data into the controller:
https://docs.angularjs.org/api/ngRoute/service/$route
Edit:
As mohan said as this will work for every route change, you can make a service to catch these events and for each route broadcast a special event.
And in the relevant controller/service listen to this event and reload data
If you want to force reload, then add an click function like follows,
Note: This will work only if you use $stateProvider
Home
and in controller ,
$scope.goToHome = function(){
$state.transitionTo('home', {}, {reload:true});
}
The issue here was that on clicking the link to '/' not all of the initialisation code was rerunning. Rather than making calls to the database to get fresh data, angular was just returning old data. The way I fixed this was to rewrite my factories. The factories that were failing were written:
app.factory('stream', ['$http', function($http) {
return $http.get('/stream/')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}]);
The factory that worked every time was written:
app.factory('stream', ['$http', function($http) {
return {
fetch: function () {
return $http.get('/stream/');
}
}
}]);
Now it runs every time. I am not sure why though.

How to 'restart' angularjs app

In my AngularJS app I have a service that is based on WebSocket. If the WebSocket connection breaks, I want to fully 'restart' the application (go to default page from $route, recreate the service etc.). Is that achievable? This is how I started, but from that point I have no idea how to proceed:
Module:
(function () {
angular.module('mainModule', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/home', {
templateUrl: 'partials/home.html',
controller: 'appController'
}).
when('/register', {
templateUrl: 'partials/register.html',
controller: 'appController'
}).
otherwise({
redirectTo: '/home'
});
}]);
}());
Service:
(function () {
var app = angular.module('mainModule');
app.service('$wsService', ['$q', '$window', function ($q, $window) {
$window.console.log("WebSocket SERVICE: started");
var self = this;
var ws = new WebSocket("ws://127.0.0.1:9090");
this.isConnected = function (m) {};
ws.onopen = function () {
$window.console.log("WebSocket SERVICE: connected");
self.isConnected(true);
};
ws.onmessage = function (m) {
//do whatever I want to do
};
ws.onerror = function () {
$window.console.log("WebSocket SERVICE: disconnected");
self.isConnected(false);
};
}]);
}());
Controller:
(function () {
var app = angular.module('mainModule');
app.controller('appController', ['$route', '$scope', '$wsService', function ($route, $scope, $wsService) {
$wsService.isConnected = function (m) {
//restart logic
};
}]);
}());
So as my 'restart logic' I tried "$route.reload();" but as you already know it doesn't do what I need. Eventually I will have a warning message pop up (bootstrap modal) informing the user that the connection has been lost, and on a button click in that modal it will reload the app and go to /home. I am not asking how to do that popup etc as this is already done. As for now however, I need to figure out just the logic for total reload of the app. Any ideas? Thanks.
To answer my own question, achieved with a trial and error:
$scope.$apply(function() {
$location.path('/home');
$window.location.reload();
});
This will go to /home (default) and reload everything, thus creating new service, module, controllers etc. If there is a better way of doing it (if I change default path to /blah in my module, this won't pick it up and thus I will have to edit this code too), let me know :)
I achieved the same thing doing:
$window.location.href = '/home';
A little tweak I did to your answer that helped a lot with the UI refreshing. Is to do the path change inside the reload success callback:
$window.location.reload().then(
function success(){
$location.path('/home');
},
function error(error) {}
);
Most of the time it gives a very smooth transition, presuming you are restarting while redirecting to a different page.

AngularJS: forward to login page for authentication, retain route as GET parameter?

I'm a bit of an Angular newbie. I'm trying to write an Angular service that on any page, will check if a user is logged in, and if not, forward them to a login page, passing their current path as a a GET parameter.
I'm almost there, but it's not quite working. The problem I'm having is as follows: if the user goes to #/articles/my-articles/, they get forwarded to #/login/?next=%2Farticles%2F:module%2F.
In other words, it looks as though Angular is passing the route pattern, not the actual URL.
This is my authentication code:
auth.run(['$rootScope', '$location', '$user', 'TOKEN_AUTH', 'PROJECT_SETTINGS', function ($rootScope, $location, $user, TOKEN_AUTH, PROJECT_SETTINGS) {
var MODULE_SETTINGS = angular.extend({}, TOKEN_AUTH, PROJECT_SETTINGS.TOKEN_AUTH);
$rootScope.$on('$routeChangeStart', function (e, next, current) {
if (next.$$route && !next.$$route.anonymous && !$user.authenticated) {
var nextParam = next.$$route.originalPath;
$location.url(MODULE_SETTINGS.LOGIN + '?next=' + nextParam);
}
});
}]);
I can get the original path in a hacky way using current.params.module - but that doesn't help me, because it seems that routeChangeStart is fired several times and the current object is undefined on all but the last fire.
This is my routes file:
articles.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/articles/:module/', {
templateUrl: 'views/articles/article_list.html',
controller: 'ArticlesListCtrl'
})
.when('/articles/:module/:id/', {
templateUrl: 'views/articles/article_detail.html',
controller: 'ArticlesDetailCtrl'
});
}]);
How can I fix this problem?
auth.run(['$rootScope', '$location', '$user', 'TOKEN_AUTH', 'PROJECT_SETTINGS', function ($rootScope, $location, $user, TOKEN_AUTH, PROJECT_SETTINGS) {
var MODULE_SETTINGS = angular.extend({}, TOKEN_AUTH, PROJECT_SETTINGS.TOKEN_AUTH);
$rootScope.$on('$routeChangeStart', function (e, next, current) {
if (!$user.authenticated) {
$location.url(MODULE_SETTINGS.LOGIN + '?next=' + $location.path());
$location.replace();
}
});
}]);
If logging in is not a AngularJS view, you may have to provide an otherwise route:
(depends on your $locationProvider config)
$routeProvider.otherwise({
template: 'Redirecting…',
controller : 'Redirect'
});
...
articles.controller('Redirect', ['$location', function($location) {
if (someConditionThatChecksIfUrlIsPartOfApp) {
location.href = $location.path();
return;
} else {
// Show 404
}
}]);
Side note: you shouldn't read $$-prefixed properties, they are private AngularJS variables.
Also note: don't use $ prefixes ($user) in your own code, these are public properties, reserved for AngularJS.
My solution works on Angular 1.2.13 :
preventDefault stops angular routing and $window.location sends me out to login page. This is working on a ASP.NET MVC + Angular app.
$rootScope.$on("$locationChangeStart", function (event, next, current) {
event.preventDefault();
$window.location = '/Login';
}
});

Categories

Resources