I need to open the menu automatically when navigate to a specific page.
but the event is ignored.
I created the menu controller:
.controller('MenuController', function ($scope, $ionicSideMenuDelegate) {
$scope.toggleLeft = function() {
$ionicSideMenuDelegate.toggleLeft();
}; })
and the specific page controller:
.controller('Sem_ConsultasCtrl', function ($scope) {
$scope.toggleLeft();
$scope.btn = function () { $scope.toggleLeft(); }
})
in my specific page i have a directive ng-click="btn()" wich works (toggles side-menu when click on button).
but if I call ' $scope.toggleLeft(); ' outside of btn() to automatically open the side menu when navigate to specific page nothing happens.
I found the problem:
when I call '$scope.toggleLeft();' outside of btn() the page/template still has not loaded/rendered the DOM. and when I click on button (btn()) works because DOM is already rendered.
to automatically open the side-menu I need to only call '$scope.toggleLeft();' when DOM is already and for achieve that I need to define a Watcher wich do something when occurs some modification to my template:
$timeout(function () {
$scope.toggleLeft();
});
$timeout(function () { //runs after DOM is render} );
This way, is working :)
EDIT:
I was going through my answers and I noticed that this answer was not correct.
calling $timeout triggers a digest cycle that captures differences in the DOM and updates it.
other events like clicking a button or writing in a input text triggers a digest cycle, thats why the changes only happened when clicked the button
Related
I'm trying to pass back a value from a uibModal. I can define the return if the user clicks the modal's close button
$scope.close = function () {
$modalInstance.close($scope.editMade);
};
But this doesn't work if the user click the backdrop area.
How can I define a return value for that particular event?
When you click on the backdrop outside, it does a dismiss internally.
Try using this inside modal:
$modalInstance.dismiss($scope.editMade);
And use this to handle data:
instance.result.then(function(){
//Get triggers when modal is closed
}, function(){
//gets triggers when modal is dismissed. You can basically handle data here
});
Check this working plunker. It uses the dismiss as I mentioned
http://embed.plnkr.co/YdWmqYPFBNbv4vhQZc4t/
Passing custom data to your parent controller when modal is dismissed:
Code in Modal Controller:
$scope.$on("modal.closing",function(){
$rootScope.$broadcast("modalClosing",$scope.editMade);
});
Then in your parent controller:
$scope.$on("modalClosing",function(event,value){
console.log(value); //value should be $scope.editMade
});
You can learn more by reading the documentation here:
https://github.com/angular-ui/bootstrap/tree/master/src/modal/docs
In the modal controller, you can do something like this:
$scope.$on('modal.closing', function(event, reason, closed) {
if (reason == "backdrop click" || reason == "escape key press")
{
event.preventDefault();
$uibModalInstance.close({"data": somedata});
}
});
This way you'll always get the success callback on modalInstance
modalInstance.result.then(function(response) {
// here response will be whatever is passed. in this sample {"data": somedata}
});
I'm using a module from the UI Boostrap extensions (http://angular-ui.github.io/bootstrap). The module actually serves as a loading dialog and is automatically closed when a set of web service data is returned to my Angular code. As the data on this page is loaded automatically the dialog comes up immediately.
All this works great when I hit the page in question for the the first time or simply refresh it. The problem occurs when I go to a deeper page and then try and navigate back to the original page (with the dialog) via the browser's back button. The dialog never goes away despite all the fact that all the data is returned and the module's dismiss() call has been made.
I've traced this down to the promise to open the dialog appears to be happening after the dismiss call but, again, only when the page is loaded via the back button. The dismiss call never closes anything because it hasn't been added yet (I've confirmed this in the debugger).
The question I have is how could I handle this? Is there a solid way to catch the completion of the page loading via Angular and double check that the dialog closed? Is there a better way via UI Bootstrap's api?
I know this is rather unusual case but any thoughts on it would be great.
Thanks!
#HankScorpio's solution is good, but I think there may be a simplified option now.
There is no need to store the current modal anymore, if you register either a $locationChangeStart or $routeChangeStart listener with $uibModalStack injected and call $uibModalStack.dismissAll(). $locationChangeStart has the benefit of working for both ngRoute and uiRoute.
i.e. If only for the one page, then in your controller you'd have:
angular.module('app')
.controller('ctrl', ['$scope', '$uibModalStack', ctrl]);
function ctrl($scope, $uibModalStack) {
$scope.$on('$locationChangeStart', handleLocationChange);
function handleLocationChange() {
$uibModalStack.dismissAll();
}
}
If you want to do this for all pages then define this in a factory that is always loaded or just an app.run code segment:
angular.module('app')
.run(['$rootScope', '$uibModalStack', setupUibModal]);
setupUibModal($rootScope, $uibModalStack) {
$rootScope.$on('$locationChangeStart', handleLocationChange);
function handleLocationChange() {
$uibModalStack.dismissAll();
}
}
Here is the simple solution when using ui-router for state change
Closing modal popup on the back button click in angularjs
App.run(['$rootScope', '$modalStack', function ($rootScope, $modalStack) {
$rootScope.$on('$stateChangeStart', function (event) {
var top = $modalStack.getTop();
if (top) {
$modalStack.dismiss(top.key);
}
})
}]);
hope this will save lot of time for people who are breaking heads
I've run into this same problem. Here's how I fixed it.
1) Create a service to abstract the opening and closing of a modal and track which one is open (necessary for step 2). Instead of calling $modal.open() directly, call ModalService.open().
Here you go, you can have the one I wrote:
(function () {
'use strict';
var theModule = angular.module('services.modalService', ['ui.bootstrap']);
theModule.factory('ModalService', function ($modal) {
var service = {};
var currentModal;
var clearModal = function () {
currentModal = undefined;
};
service.getCurrentModal = function () {
return currentModal;
};
service.open = function (options) {
currentModal = $modal.open(options);
currentModal.result['finally'](clearModal);
return currentModal;
};
return service;
});
}());
2) In a controller, add an event listener to $routeChangeStart, this event will fire whenever someone hits the back button.
$scope.$on('$routeChangeStart', function(){
var currentModal = ModalService.getCurrentModal();
if(angular.isDefined(currentModal)){
currentModal.dismiss('cancel');
}
});
3) Your modals should now close when a user hits back.
4) Enjoy.
IMPROVEMENT:
I found the answer from HankScorpio to be the best out there. I wanted to include this snippet for those using ui-router and their recommendation for stateful modals.
1) I wanted the result.finally(...) to jump to a parent state;
2) I wanted to control the closing of the modal from $stateProvider config, NOT through rigging a controller and adding a listener to $routeChangeStart
Here is an example of a state that opens (and closes) it's modal:
.state('product.detail', {
url: '/detail/{productId}',
onEnter: /*open-modal logic*/,
onExit: ['ModalService', function (ModalService) { ModalService.close()} ]
})
I made ModalService aware of $state so that the result of closing a modal could jump to a parent view:
a. Add an isStateful flag to modalService.open(...):
service.open = function (options, isStateful) {
currentModal = $uibModal.open(options);
currentModal.result.finally(function () {
clearModal(isStateful);
});
return currentModal;
};
so that clearModal will return to previous state:
var clearModal = function (isStateful) {
currentModal = undefined;
if (isStateful)
$state.go('^');
};
Finally, add the closeModal() function called above (not a "stateful" close, simply a dismissal):
service.close = function() {
if (currentModal) {
currentModal.dismiss().then(function () {
clearModal();
})
}
}
The benefits of this are that back button functionality is controlled at state config level, not through a listener.
I open a UI-Bootstrap Modal using the $modal.open({...}) method. I need this to close when the user presses the back button.
The result promise returned by the open() method is not useful in this case as it cannot detect the state change due to the back button. Right now when the back button is pressed, the state changes but the modal stays open.
Basically I am having the exact problem as this question but even though it has a selected answer, the problem is not solved as evidenced from the comments. This other question is similar but also doesn't solve the back button issue.
I need some way to detect that the current state has changed from within the controller and call $modalInstance.close() or the $scope.$close() methods.
I could listen for $stateChangeStart event and check the fromState argument to conditionally close the modal. But then this event would unnecessarily keep firing for all subsequent state changes too.
UPDATE: So I tried listening for the event and deregistered it as soon as it is fired for the first time. This way I get to listen for the back button state change and then stop it when I want. The final code for the modal state is as follows:
$stateProvider.state('itemList.itemNew', {
url: '/new',
onEnter: function($state, $modal) {
$modal.open({
templateUrl: "/static/partials/item/form.html",
controller: function($http, $scope, $modalInstance) {
$scope.editableItem = {};
$scope.saveItem = function(item) {
$http.post('/api/item', item)
.success(function(data) {
$modalInstance.close(data);
alert("Saved Successfully");
}).error(function(data) {
alert("There was an error.");
});
};
//Register listener specifically for the back button :(
deRegister = $scope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams) {
if (toState.name === 'itemList' &&
fromState.name === 'itemList.itemNew') {
$modalInstance.close();//Close the modal
deRegister();//deRegister listener on first call
}
}
);
}
}).result.then(function() {
//Promise Resolved, Modal Closed.. So reload
$state.go("^", null, {
"reload": true
});
}, function() {
//Promise Rejected, Modal Dismissed.. no reload
$state.go("^");
});
},
});
I still think there should be a better way to do it. Constellates apparently decided to dump modal.js from ui-bootstrap altogether. Should I do the same and simply render the modal using plain Bootstrap CSS out of a <ui-view/>?
I needed to address the same issue. Maybe a slightly different setup, but I believe it may work for you.
I am using the angular-modal-service, which I believe is running on bootstrap anyway.
Inside the controller for the modal I used the following:
$scope.$on('$stateChangeStart', function() {
$scope.stateChange();
});
$scope.stateChange = function() {
// Manually hide the modal using bootstrap.
$element.modal('hide');
// Now close as normal, but give 500ms for bootstrap to animate
close('', 500);
};
The second function is just the manual way of exiting the modal as per the docs under "I don't want to use the 'data-dismiss' attribute on a button, how can I close a modal manually?". It says:
All you need to do is grab the modal element in your controller, then call the bootstrap modal function to manually close the modal. Then call the close function as normal
So now, if a state change happens (including a user-initiated back-button click), the modal gracefully zips away.
In order to prevent view changes on a form with edits, I am using $locationChangeStart to intercept the view change. In that function, I am attempting to use a bootstrap dialog to prompt the user.
It all works ok, until the part where I call $location.path(current) to change the view. Instead of navigation to the appropriate route, it goes to the default route (the home page). Why is this?
Here is the code I am using in my controller:
function onNavigate() {
var turnOff = $scope.$on('$locationChangeStart',
function (event, current, previous) {
if (vm.canSave) {
dialogService.confirmationDialog('Are you sure?', 'You have unsaved changes, are you sure you want to abandon your form?')
.then(function() {
turnOff();
$location.path(current);
});
event.preventDefault();
});
}
In the debugger, the value of current is something like
http://localhost:3091/#/participant/24
at the $location.path line, however my application ends up at
http://localhost:3091/#/
I have a navbar ul in a view that shouldn't be shown if the user isn't logged in. For this I'm using ng-show="session.exists()". When the logout button (also in the nav bar view) is clicked it calls:
$scope.logout = function () {
var success = function () {
$state.transitionTo('login');
};
console.log($scope.session.exists());
Auth.logout().then(success);
console.log($scope.session.exists());
};
from my log statements i can see that the see that the session is being destroyed ok but the el elements I want hidden are still visible. The elements are hidden if I refresh the page. Am I missing a trick here? I'm new to angular and have inherited a project so any suggestions greatly appreciated!
C
Probably your function are not being fired on ngShow when you destroy the user session.
I suggest you to use a variable to control that, and set it on on your logout function.
In your controller:
$scope.loggedIn = true;
$scope.logout = function() {
$scope.loggedIn = false;
}
And in your view:
<div data-ng-show="loggedIn">Show Me!</div>
I think you are trying to update the scope variable responsible for your showing your navbar url from an asynchronous function call probably in side the success of a http call.
So try adding $scope.$digest(); just after updating the scope variable