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.
Related
I want to create a modal (dialog). I have followed examples on official bootstrap documentation but I stuck. When I am trying to create modal I receive an error
angular.min.js:122 Possibly unhandled rejection: {}
mainController:
angular
.module('app')
.controller('tlmController', function($scope, $http, $timeout, $uibModal, DTOptionsBuilder, DataLoader, TestLines) {
$scope.openTestLineDetails = function(id) {
var modalInstance = $uibModal.open({
size: 'lg',
controller: 'testlineDetailsController',
templateUrl: 'app/client/layout/testlinedetails.tpl.html',
resolve: {
testLineId: function() {
return id;
}
}
});
};
})
and TestlineDetailsController:
angular
.module('app')
.controller('testlineDetailsController', function($scope, $modalInstance, testLineId) {
});
What is wrong with this code? I am using $uibModal ($modal service does not exist) in main controller. When I replace $modalInstance by $uibModalInstance I receive an error too (service $uibModalInstance does not exist), so I have to use $uibModal with $modalInstance. Strage but true.
you can write below code in app.config
app.config(['$qProvider', function ($qProvider) {
$qProvider.errorOnUnhandledRejections(false);
}]);
First of all, check your modal controller script is appended to the main HTML file and if its appended(appears) in the browser (In Chrome, open web developer tools with F12 keyboard button then open the "Elements" tab button) (This is in case you are using some scaffolding tool like generator-angular from Yeoman's team, remember to clear cache in order to get the latest update from your code), because I had the same problem :( and I was reviewing constantly what was wrong with my code then I found out that the browser was not appending the latest script I made (Modal controller), so my code was like yours, but taking your code example:
<!-- In your index.html file, check for this script being appended in your browser -->
<script src="testlineDetailsController.js"></script>
//In your parent controller
angular
.module('app')
.controller('tlmController', function($scope, $http, $timeout, $uibModal, DTOptionsBuilder, DataLoader, TestLines) {
$scope.openTestLineDetails = function(id) {
var modalInstance = $uibModal.open({
size: 'lg',
controller: 'testlineDetailsController',
templateUrl: 'app/client/layout/testlinedetails.tpl.html',
resolve: {
testLineId: function() {
return id;
}
}
});
};
})
Secondly, make sure you are implementing at least one method from the modal instance service in the modal controller: EDIT: (This is optional, you can hide the modal using the backdrop property from the modal option object)
//In your modal controller
angular.module('app').
controller('testlineDetailsController', function ($scope, $uibModalInstance, testLineId) {
//In case you have the ok and cancel buttons in your modal template
$scope.id = testLineId;
$scope.ok = function () {
$uibModalInstance.close();
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
});
After this, your app should be working.
Now, there is another alternative to get this issue solved, you can directly write the controller function in the property of the modal options object:
//In your parent controller
angular
.module('app')
.controller('tlmController', function($scope, $http, $timeout, $uibModal, DTOptionsBuilder, DataLoader, TestLines) {
$scope.openTestLineDetails = function(id) {
var modalInstance = $uibModal.open({
size: 'lg',
//write an anonymous function instead of naming the controller name.
controller: function ($scope, $uibModalInstance, testLineId) {
$scope.id = testLineId;
$scope.ok = function () {
$uibModalInstance.close();
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
},
templateUrl: 'app/client/layout/testlinedetails.tpl.html',
resolve: {
testLineId: function() {
return id;
}
}
});
};
})
This alternative should work also in your app. So I hope this explanation helps you to solve the issue you have.
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.
So i've got my code:
(function (ng) {
ng.module('myModuleName')
.provider('myProviderName', ['importedThing', function (importedThing) {
//lots of cool stuff happens in here
}];
}])
.config(['$stateProvider', '$urlRouterProvider', 'importedThing', 'myProviderNameProvider', function ($stateProvider, $urlRouterProvider, importedThing, myProvider) {
window.stateProvider = $stateProvider;
$urlRouterProvider.otherwise(function ($injector, $location) {
$location.path("/pages");
});
}]);
}(angular));
I need to unit test this line for code coverage:
$location.path("/" + myProvider.coolString);
But I can't figure out how. I've seen several people do it with controllers, but not with providers.
I've got the unit test set up like:
beforeEach(function () {
angular.module('dsLocale', function () { }).config(function ($urlRouterProvider) {
$urlRouterProvider.deferIntercept();
$urp = $urlRouterProvider;
$urp.deferIntercept();
});
});
//Lines are commented to represent random stuff I've tried already.
it('should call otherwise', function () {
//$urp.otherwise('/otherwise');
//$location.path("/lastrule");
console.log($location.path());
//local.$emit("$locationChangeSuccess");
expect(true).toBe(true);
});
I feel like it should be as easy as just changing $location.Path to something that doesn't exist, so UIRouter can do it's thing but it doesn't appear to be that easy. Any help is appreciated, and thanks in advance!
just had the same problem. I wanted to test the otherwise functionality, but did not how, i figured out a way and this worked for me.
I am setting the path with location.path, firing the $locationChangeSuccess event and then it works
it('redirects to otherwise page after locationChangeSuccess', function() {
location.path('/nonExistentPath');
rootScope.$emit("$locationChangeSuccess");
expect(location.path()).toBe("/");
});
without the event, the path is the of course /nonExistentPath
--
this is my full test file. Be aware, i am using browserify and the file maybe looks a little different than yours
'use strict';
require('../../../app');
var objectToTest = 'CoreRouterConfig';
describe(objectToTest, function () {
var rootScope,
state,
location;
beforeEach(angular.mock.module('ui.router'));
beforeEach(angular.mock.module('core'));
beforeEach(inject(function ($rootScope, $state, $location) {
rootScope = $rootScope;
state = $state;
location = $location;
}));
it('should respond home state', function() {
expect(state.href('home')).toEqual('#/');
});
it('init path is correct', function() {
rootScope.$emit("$locationChangeSuccess");
expect(location.path()).toBe("/");
});
it('redirects to otherwise page after locationChangeSuccess', function() {
location.path('/nonExistentPath');
rootScope.$emit("$locationChangeSuccess");
expect(location.path()).toBe("/");
});
it('does not redirect to otherwise page without locationChangeSuccess', function() {
location.path('/nonExistentPath');
expect(location.path()).toBe("/nonExistentPath");
});
});
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';
}
});
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...