I am using ui-router for routing in my angularjs app and ui-bootstrap for UI.In my app on entering a state i am opening a uibmodal which basically returns a uibmodalinstance but when i change a state using
$state.go('dashboard')
Inside my controller it is changing the state but didn't closing modal.
So i want modal to be closed on exiting the state.
i Have written following code but some part of code doesn't work.
please see coding and the comments for not working part
$stateProvider.state('makeabid',{
parent: 'dashboard',
url: '/makeabid/{id}',
data: {
authorities: ['ROLE_USER'],
pageTitle: 'global.menu.makeabid'
},
onEnter: ['$stateParams', '$state', '$uibModal', function($stateParams, $state, $uibModal) {
$uibModal.open({
templateUrl: 'app/dashboard/makeabid/makeabid.html',
controller: 'MakeabidController',
controllerAs: 'vm',
backdrop: true,
size: 'lg'
}).result.then(function () {
$state.go('dashboard');
});
}]
//this part doesnt work
,onExit:['$uibModalInstance','$stateParams', '$state',function ($uibModalInstance,$stateParams, $state) {
$uibModalInstance.close();
}]
});
My Controller Coding is as follows : -
MakeabidController.$inject = ['$stateParams','$state','$uibModalInstance','MakeabidService'];
function MakeabidController( $stateParams, $state, $uibModalInstance, MakeabidService) {
var vm = this;
loadAll();
vm.clear = clear;
vm.save = save;
function clear () {
$uibModalInstance.close();
}
function save() {
// console.log(vm.comparableData);
}
function loadAll() {
vm.comparableData = MakeabidService.getobject();
if(angular.isUndefined(vm.comparableData)){
//$uibModalInstance.close(); //It doesn't work
$state.go('dashboard'); //This is working
}
}
}
AnyOne Please Tell me solution for closing the uibmodal on changing state
I solved it by adding $uibModalStack.close() in my app.run
function run($uibModalStack) {
$rootScope.$on('$stateChangeStart', function() {
$uibModalStack.dismissAll();
});
}
You can tap into to some of the $stateProvider events.
I do something similar in one of my apps (in coffeescript, but you get the idea)
#$scope.$on '$stateChangeStart', (e, to, top, from, fromp) =>
#$uibModalInstance.close()
Basically, in your controller that handles the modal, you will watch for the $stateChangeStart event, and when you catch it, you can close the modal.
See https://github.com/angular-ui/ui-router/wiki, specifically the section on State Change Events
---EDIT---
I just noticed that these calls are deprecated. If you are using UI-Router > 1.0, there is some documentation here on how to migrate: https://ui-router.github.io/guide/ng1/migrate-to-1_0#state-change-events
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 a angular webapp that is using a pre-produced theme/framework called fuse (http://withinpixels.com/themes/fuse), this theme already has an app structure and code written to make the creation of apps easier.
We added some pages (which include sidemenu items), the problem is, when you tap one of the links in the sidebar, the whole page seems to be reloaded or at least a animate-slide-up is played 2 times on the index main div, I traced down one part of the problem to the configuration module of the page:
$stateProvider.state('app.pages_dashboard', {
url : '/dashboard',
views : {
'main#' : {
templateUrl: 'app/core/layouts/vertical-navigation.html',
controller : 'MainController as vm'
},
'content#app.pages_dashboard': {
templateUrl: 'app/main/dashboard/dashboard.html',
controller : 'DashboardController as vm'
},
'navigation#app.pages_dashboard': {
templateUrl: 'app/navigation/layouts/vertical-navigation/navigation.html',
controller : 'NavigationController as vm'
},
'toolbar#app.pages_dashboard': {
templateUrl: 'app/toolbar/layouts/vertical-navigation/toolbar.html',
controller : 'ToolbarController as vm'
},
},
bodyClass: 'login',
needAuth: true,
onStateChangeStart: function(event, state, auth, api) {
console.log('onStateChangeStart on DASHBOARD');
api.getUserCard.save({}, {}, function (response){
if (!response.result) {
state.go('app.pages_claimcard');
}
});
}
});
and the configuration module of the app
angular
.module('fuse')
.run(runBlock);
/** #ngInject */
function runBlock($rootScope, $timeout, $state, $auth, api)
{
console.log('INDEX.RUN loaded');
// Activate loading indicator
var stateChangeStartEvent = $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams)
{
// console.log('started change event');
// console.log(toState);
// console.log(fromState);
// check if authentication needed
if (toState.needAuth) {
// redirect to login page
if (!$auth.isAuthenticated()) {
event.preventDefault();
$state.go('app.pages_auth_login');
}
}
if (toState.onStateChangeStart) {
// THIS CAUSES ONE OF THE RELOADS
// toState.onStateChangeStart(event, $state, $auth, api);
}
$rootScope.loadingProgress = true;
});
// De-activate loading indicator
var stateChangeSuccessEvent = $rootScope.$on('$stateChangeSuccess', function ()
{
$timeout(function ()
{
$rootScope.loadingProgress = false;
});
});
// Store state in the root scope for easy access
$rootScope.state = $state;
// Cleanup
$rootScope.$on('$destroy', function ()
{
stateChangeStartEvent();
stateChangeSuccessEvent();
});
}
as you can see I commented the the toState OnStateChangeStart function, and that got rid of one the 'reloads' of the application, so basically have 2 questions:
Why does the onStateChangeStart function on the toState state causes the page to reload?
I have no idea what might be causing the other page reload, any ideas?
I found the problem, my states were defined as:
'app.pages.dashboard'
however there was never a declaration for
'app.pages'
so UI-router was trying its best to sort this mess, anyways, always remember to properly declare your states and everything should be fine.
This is a common issue, I see it a ton but nothing seems to work. Here is what I'm doing. I want to have some dynamic animations with my states, so basically login will do some cool animations and move into the actual interface. Now, I started nesting the views like this:
$stateProvider
.state('login', {
url: '/login',
title: "Login",
views: {
"master": {
controller: "LoginController",
templateUrl: "/components/login/login.html",
authentication: false
}
}
})
.state("logout", {
url: "/logout",
title: "Logout",
authentication: false
})
.state('foo', {
url: '/',
controller: "HomeController",
views: {
"master": {
templateUrl: '/components/ui-views/masterView.html'
},
"sidebar#foo": {
templateUrl: '/components/sidebar/_sidebar.html'
},
"header#foo": {
templateUrl: '/components/header/_header.html'
}
}
})
.state('foo.inventory', {
url: '/inventory',
title: "Inventory",
views: {
"content#foo": {
controller: "InventoryController",
templateUrl: "/components/inventory/inventory.html"
}
}
});
So while running this I need to redirect to logout, but it gets stuck. It won't move from this logout state at all.
Here is how I'm handling that:
function run($http, $rootScope, $cookieStore, $state, $templateCache, $timeout, AuthService, modalService, const) {
var timeout;
FastClick.attach(document.body);
$rootScope.globals = $cookieStore.get('globals') || {};
if ($state.current.name !== 'login' && !$rootScope.globals.guid) {
$state.go('login');
} else if ($state.current.name == 'login' && $rootScope.globals.guid) {
$state.go('foo');
}
$rootScope.$on('$stateChangeStart', function (event, next, current, fromState, fromParams) {
var authorizedRoles = next.level != undefined ? next.level : 1,
needAuth = next.authentication != undefined ? next.authentication : true;
if (needAuth) {
if (!AuthService.isAuthorized(authorizedRoles, true)) {
event.preventDefault();
if (AuthService.isAuthenticated()) {
$rootScope.$broadcast(const.auth.notAuthorized);
$state.go('foo', {});
} else {
$rootScope.$broadcast(const.auth.notAuthenticated);
}
}
}
if (next.name == 'logout') AuthService.logout($rootScope.globals);
});
}
So why would this not work? It seems like this work fine. But the $state.go('login') returns a bad value.
If anyone could guide me in the right direction, or tell me what is wrong exactly.
The issue is that the $state.go is in the run, there doesn't seem to be many docs on this topic. $state.go is not initialized yet and will not run.
I had the same problem. After debugging into how the attribute ui-sref works for hyperlinks, i found out that the $state.go is wrapped around $timeout in the core angular-ui library.
var transition = $timeout(function() {
debugger;
$state.go("app.authentication");
});
Perhaps you could try the same. (Although the ui-sref event handler in the core library clearly mentions that this is a hack.)
只需要升级ui-router版本就可以了,见连接地址:https://github.com/mattlewis92/angular-bluebird-promises/issues/11
If you upgrade to ui-router 0.3.2 it's actually been fixed there: angular-ui/ui-router#66ab048
I have faced this issues in my app, you are trying to call $state.go on an internal ui-view that is not relative to the state ,thats you are facing this problem, so try this :
app.run(['$state', '$rootScope', '$timeout',
function ($state, $rootScope, $timeout) {
$timeout(function() {
$state.go('login');
});
}]);
I'm using UI-Router and angular bootstrap-ui. I have a state setup to create modal 'onEnter'. I'm having problems now when I'm trying to close the modal 'onExit'. Here is the basic state. It will open a modal when 'items.detail' is entered and it will transitions to 'items' when that modal is closed or dismissed.
.state('items.detail', {
url: '/{id}',
onEnter: function ($stateParams, $state, $modal, $resource) {
var modalInstance = $modal.open({
templateUrl: 'views/modal/item-detail.html',
controller: 'itemDetailCtrl'
})
.result.then(function () {
$state.transitionTo('items');
}, function () {
$state.transitionTo('items');
});
}
})
I've tried using the onExit handler like so. But haven't been able to access the modalInstance or the scope that the modal is in from that handler. Everything I try to inject comes up undefined.
.state('items.detail', {
url: '/{id}',
onEnter: function ($stateParams, $state, $modal, $resource) {
var modalInstance = $modal.open({
templateUrl: 'views/modal/item-detail.html',
controller: 'itemDetailCtrl'
})
.result.then(function () {
$state.transitionTo('items');
}, function () {
$state.transitionTo('items');
});
},
onExit: function ($scope) {
controller: function ($scope, $modalInstance, $modal) {
$modalInstance.dismiss();
};
}
})
from within my modal's controller I've tried listening for state changes.
$scope.$on('$stateChangeStart', function() {
$modalInstance.dismiss();
});
I've tried this with both $scope.$on and $rootScope.$on and both of these work but they end up being called every time I transition between any states. This only happens however after I've opened the modal.
In case this last bit is unclear... When I refresh my angular app I can transition between all my other states with out this listener event being called but after I open that modal all of my state changes get called by that listener, even after the modal is closed.
Although the question is quite old, as I ran into the same situation recently, and came up with a better solution, I decided to share my results here anyway. The key point is to move the $modal.open service into resolve part of the state, which is kind of pre-load data and $promise services, and then inject the resolved modelInstance into onEnter, onExit etc. Codes might looks like as follow:
.state('items.detail', {
url: '/{id}',
resolve: {
modalInstance: function(){
return $modal.open({
templateUrl: 'views/modal/item-detail.html',
controller: 'itemDetailCtrl'
})
},
},
onEnter: function ($stateParams, $state, modalInstance, $resource) {
modalInstance
.result.then(function () {
$state.transitionTo('items');
}, function () {
$state.transitionTo('items');
});
},
onExit: function (modalInstance) {
if (modalInstance) {
modalInstance.close();
}
}
})
I think you can better organize your modal opening behavior.I would not use onEnter and onExit handlers inside state definition. Instead it's better to define controller which should handle modal:
.state('items.detail', {
url: '/{id}',
controller:'ItemDetailsController',
template: '<div ui-view></div>'
})
Then define your controller:
.controller('ItemDetailsController', [
function($stateParams, $state, $modal, $resource){
var modalInstance = $modal.open({
templateUrl: 'views/modal/item-detail.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
itemId: function () {
return $stateParams.id;
}
modalInstance.result.then(function () {
$state.go('items');
}, function () {
$state.go('items');
});
}
});
}
])
Then define your ModalInstanceCtrl:
.controller('ModalInstanceCtrl', [
function ($scope, $modalInstance, itemId) {
//Find your item from somewhere using itemId and some services
//and do your stuff
$scope.ok = function () {
$modalInstance.close('ok');
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
]);
In this way you modal will be closed within ModalInstanceCtrl, and you will not worry about state's onExit handler.
About listener added on $scope. Seems when you add the listener and never remove it whenever your state changes, this is causing memory leak and handler function gets executed every time your app changes his state! So it's better to get rid of that event listener as you don't need it actually.
You can prevent leaving the state by listening for the $stateChangeStart event
$scope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){
console.log('$stateChangeStart', toState);
if (toState.name != 'items.list'){
event.preventDefault();
var top = $modalStack.getTop();
if (top) {
$modalStack.dismiss(top.key);
}
$state.go('items.list');
}
});
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.