Does anyone know how to prevent the event from firing twice? I've tried using a controller scope level boolean variable to see if the event has already fired, but it did not work. It is like the event is firing on 2 separate threads and the variable was always false.
In the code below the $ionicPlatform.ready event is firing twice, but I can't figure out why.I'm using the current version of the Ionic Framework ionic-v1.0.0-beta.13.
angular.module('rsgApp.controllers', [])
.controller('MapCtrl', ['$ionicPlatform',
function ($ionicPlatform) {
var vm = this;
$ionicPlatform.ready(function () {
alert('device is ready');
});
}]);
Thanks TechMa9iac I was able to resolve this problem. In my tab template I had added an 'ng-controller' attribute to my ion-content tag. This is what was causing the $ionicPlatform.ready event to fire twice.
Related
I have a requirement where closing a child window should trigger a method in the parent window. I'm using angular for development and I'd really like to do it the angular way.
Window 1 controller:
var abc = $window.open(url);
$scope.$watch('abc.closed', function() {
// Do something when abc.close is true
};
This works, but there is a delay in the process. When the child window closes, the listener is being triggered rather late, like 20-40 seconds later.
What do you think is the issue, and also what's the best way to make this work?
I've seen other posts where people are using setInterval and watching for .closed but that would be a hack-ey way of doing it.
You can achieve this by using onbeforeunload event of the window
app.controller('yourCtrl', ['$window', function ($window) {
$window.onbeforeunload = function (evt) {
//Your Service/Logic
}
]);
I use bootstrap and angularjs in my project.
I use Bootstrap 'Modals'. I'm trying to customize some of the default features.
How do I fire event handler in controller when I close the modal window by clicking on the background not the close button.
The hidden.bs.modal event fires whenever the modal is closed, no matter how you close it. See the docs.
EDIT
In the code snippet you showed me, the event was firing, but you were using $interval instead of setInterval.
$(document).ready(function () {
$("#closeModal").click(function () {
$("#modalWindow").modal("hide");
});
$("#modalWindow").on('hide.bs.modal', function () {
console.log("I fired");
var t = "333";
self.timer = setInterval(function () {
checkUpdate();
}, self.delay, false);
});
});
Whenever I'm having problems with events, I always put a console.log at the top of the callback function to make sure it is definitely being fired. 9/10 times it is and its the code in the callback that is crashing.
I am using the $broadcast pattern outlined here to controllers to listen to their service's state.
for example, here I listen to the service's busy/unbusy state to set the mouse cursor.
//Our service
function startRunning(){
$rootScope.$broadcast("busy");
}
function stopRunning(){
$rootScope.$broadcast("unbusy");
}
//Our controller
$scope.$on("busy", function(){
$scope.state = "busy";
});
$scope.$on("unbusy", function(){
$scope.state = "ready"
});
HTML
<div ng-controller = "myctrl" ng-state = "state"/>
CSS:
.busy{
cursor: wait;
}
.ready {
cursor:auto;
}
The problem with this is that the cursor doesn't change immediately. It usually requires me to move the mouse, I imagine to trigger the $digest cycle, before the cursor changes.
I can fix that with
$scope.$apply($scope.state = "ready");
But this will throw up:
Error: [$rootScope:inprog] $digest already in progress
errors.
What is the best way to deal with this?
Edit: here's a working JSFiddle: http://jsfiddle.net/HB7LU/23512/
The issue appears to be something to do with using non-angular timeout/asynchronous methods. (ie. issue doesn't appear if using a $timeout, but does appear if using a setTimeout;
Imagine I have a controller which handles, for example, view changes:
function Controller($scope){
var viewModel = this;
viewModel.goBack= function(){
viewModel.visible = visibleLinks.pop(); //get last visible link
viewModel.swipeDirection = 'left';// for view change animation
}
}
But I want to handle it not only for example with HTML buttons inside <body>, but also with Back button on device. So I have to add Event Listener for deviceready event, and also explicit call $scope.$apply() in order to fact, that it is called outside of AngularJS context, like this:
document.addEventListener("deviceready", function(){
document.addEventListener("backbutton", function(){
viewModel.goBack();
$scope.$apply();
}, false);
}, false);
}
But I also want to follow (relatively :) ) new controllerAssyntax, cause this is recommended now e.g. by Todd Motto: Opinionated AngularJS styleguide for teams and it allows to remove $scope from controllers when things like $emit or $on are not used. But I can't do it, case I have to call $apply() cause my context is not Angular context when user clicks on device back button. I thought about creating a Service which can be wrapper facade for cordova and inject $scope to this service but as I read here: Injecting $scope into an angular service function() it is not possible. I saw this: Angular JS & Phonegap back button event and accepted solution also contains $apply() which makes $scope unremovable. Anybody knows a solution to remove Cordova specific events outside Angular controller, in order to remove $scope from controllers when not explicity needed? Thank you in advance.
I don't see a reason why to remove the $scope from the controller. It is fine to follow the best practice and to remove it if not needed, but as you said you still need it for $emit, $on, $watch.. and you can add it $apply() in the list for sure.
What I can suggest here as an alternative solution is to implement a helper function that will handle that. We can place it in a service and use $rootScope service which is injectable.
app.factory('utilService', function ($rootScope) {
return {
justApply: function () {
$rootScope.$apply();
},
createNgAware: function (fnCallback) {
return function () {
fnCallback.apply(this, arguments);
$rootScope.$apply();
};
}
};
});
// use it
app.controller('SampleCtrl', function(utilService) {
var backBtnHandler1 = function () {
viewModel.goBack();
utilService.justApply(); // instead of $scope.$apply();
}
// or
var backBtnHandler2 = utilService.createNgAware(function(){
viewModel.goBack();
});
document.addEventListener("backbutton", backBtnHandler2, false);
});
In my case I was simply forwarding Cordova events with the help of Angular $broadcast firing it on the $rootScope. Basically any application controller would then receive this custom event. Listeners are attached on the configuration phase - in the run block, before any controller gets initialized. Here is an example:
angular
.module('app', [])
.run(function ($rootScope, $document) {
$document.on('backbutton', function (e) {
// block original system back button behavior for the entire application
e.preventDefault();
e.stopPropagation();
// forward the event
$rootScope.$broadcast('SYSTEM_BACKBUTTON', e);
});
})
.controller('AppCtrl', function ($scope) {
$scope.$on('SYSTEM_BACKBUTTON', function () {
// do stuff
viewModel.goBack();
});
});
Obviously in the $scope.$on handler you do not have to call $scope.$apply().
Pros of this solution are:
you'll be able to modify an event or do something else for the entire application before the event will be broadcasted to all the controllers;
when you use $document.on() every time controller is instantiated, the event handler stays in the memory unless you manually unsibscribe from this event; using $scope.$on cares about it automatically;
if the way a system dispatches Cordova event changes, you'll have to change it in one place
Cons:
you'll have to be careful when inheriting controllers which already have an event handler attached on initialization phase, and if you want your own handler in a child.
Where to place the listeners and the forwarder is up to you and it highly depends on your application structure. If your app allows you could even keep all the logic for the backbutton event in the run block and get rid of it in controllers. Another way to organize it is to specify a single global callback attached to $rootScope for example, which can be overriden inside controllers, if they have different behavior for the back button, not to mess with events.
I am not sure about deviceready event though, it fires once in the very beginning. In my case I was first waiting for the deviceready event to fire and then was manually bootstrapping AngularJS application to provide a sequential load of the app and prevent any conflicts:
document.addEventListener('deviceready', function onDeviceReady() {
angular.element(document).ready(function () {
angular.bootstrap(document.body, ['app']);
});
}, false);
From my point of view the logic of the app and how you bootstrap it should be separated from each other. That's why I've moved listener for backbutton to a run block.
Please see the fiddle:
http://jsfiddle.net/HB7LU/4089/
On window resize if you check the console, $scope.showName keeps getting toggled between true and false as expected. However the view does not update. It remains with the initialized value of true.
From my understanding, the {{}} or ng-bind provides 1 way binding from controller to the view, so the value in the view should update when it changes in the controller.
What am I missing?
The $scope only binds to the view on $digest cycles - your event doesn't trigger a digest cycle since there was no action taken. You have to call $scope.$apply() to trigger a view update.
Be warned tho, $scope.$apply() can throw an error if a $digest cycle is already in progress.
You are missing a call to $scope.$apply() since the event is being handled outside of an angular context you need to call $scope.$apply() to trigger a digest which will updated any watchers http://jsfiddle.net/HB7LU/4091/
function MyCtrl($scope, $window) {
$scope.name = 'Timothy';
$scope.showName = true
$scope.nickname = 'Tim'
angular.element($window).bind('resize', function() {
if ($scope.showName){
$scope.showName = false;
}
else
{
$scope.showName = true;
}
console.log($scope.showName);
$scope.$apply();
});
}
You need to add $scope.$apply(); after any scope manipulation outside Angular events. Add it as the last statement of the bind function.
missing a call to $scope.$apply()
Look at http://jsfiddle.net/HB7LU/4093/
http://jsfiddle.net/HB7LU/4094/
$scope.$digest();
Try with this code..
I have add $digest of scope alter resize