I have a controller that is attached to a route. The controller constantly polls the server using $timeout. When the route changes, I need to stop polling, and start it again when the route changes back.
Please help.
Here is my code:
(angular
.module('app.controllers', ['ng', 'ngResource'])
.controller('myContr', [
/******/ '$scope', '$resource', '$timeout',
function ($scope, $resource, $timeout) {
function update() {
$resource('my-service').get({}, function (d) {
// ...use data...
$timeout(update, UPDATE_INTERVAL);
});
};
update();
}
])
);
Save the return value (a promise) from $timeout (to a $scope property).
Register a $destroy event handler on your scope.
Call cancel() on that $timeout promise when the event handler triggers.
When the route changes back, the controller will get recreated, so your existing code should start up the polling again.
Related
I want to send an object returning from the DB to a child directive. When I am sending any kind of data in sync mode it works. However when I am doing the same thing but fetching the data from remote server - async method, the directive is getting fired before the results are coming.
This is the controller which fetched the data from the server:
app.config(function($routeProvider, $locationProvider) {
$routeProvider.when("/root/cardboards/employees/:_employee", {
templateUrl: "screens/root/cardboards/employees-detail/employees-detail.html"
});
// $locationProvider.html5Mode(true);
});
app.controller("employees-detail", ($rootScope, $scope, $location, $routeParams, Http, Cast) => {
Http.GET({
resource: `employees/${$scope._employee}`
}, (err, response) => {
if (err) return Cast.error(err);
$scope.employee = response.data; // employee's object
$scope.$apply();
});
});
This is the directive element in HTML:
<edit-employee employee="employee"></edit-employee>
And this is the edit-employee directive js file:
app.directive("editEmployee", ($rootScope, Http, Cast) => {
return {
templateUrl: "/screens/root/cardboards/employees-detail/components/edit-employee/edit-employee.html",
scope: {
employee: "="
},
link: function($scope, element, attrs) {
console.log($scope.employee); // undefined
}
}
});
I thought that when I am using the = operator it means that it's now two way binding and the directive will watch for changes and then having the functionality based on the data that will come after the request to the server.
However it doesn't work like that.
How should it work and what should I do, the standard way, to make thing working?
When <edit-employee being rendered it will try to get the employeeto do a console.log on this line
link: function($scope, element, attrs) {
console.log($scope.employee); // undefined
}
But unfortunately, at that time, the employee is still undefined since it waiting for the response from the server.
To understand more clearly about this you can initiate a $watch to watch the employee inside edit-employee directive, and whenever the HTTP is finished it will update the employee to newest value.
How should it work and what should I do, the standard way, to make thing working?
It really depends, I meet that problem once and I used an ng-if on <edit-employee ng-if='employee' which mean the edit-employee directive will be rendered after the employee is initial (!= undefine).
Another way is you watch the employee inside edit-employee directive and check if employee has value then continue the business logic
Was wondering what is the best way to refresh controller scope on route change?
Any help would be appreciated!
I would refactor to have all my initialisation of data properties on the scope in a single function, called something like initScope() that is called when the controller is first run, and also on the $routeChangeSuccess (and probably also $routeChangeUpdate if you want to handle changes to the URL that resolve to the same route) event(s).
e.g.
app.controller('MyCtrl', ['$scope', function ($scope) {
function initScope() {
$scope.foo = 1;
$scope.bar = { ram: 'ewe' };
}
initScope();
$scope.$on('$routeChangeUpdate', initScope);
$scope.$on('$routeChangeSuccess', initScope);
}
When a webpage loads for the first time, controller of my directive needs to know what is the currently selected route.
After first load I can detect change using
$scope.$on('$routeChangeSuccess', function(){});
EDIT:
Answers below are valid but it didn't work for me because:
- I am new to AngularJS technology i didn't realize that, on our project, we are using custom route provider
- You need to inject 'ngRoute' in your module before you can use $route
You can use the $routeChangeSuccess callback signature
function(event, currentRoute, previousRoute) {}
PLUNKER
e.g.
$rootScope.$on('$routeChangeSuccess', function(event, current, previous) {
console.log(current.$$route); // displays current $route object
});
The current documentation does not show the callback signatures(IDK why) but you can see them on their github, see this line.
you need to inject $route dependency in your controller and then get the current route using $route.current
Plunker Example
Controller code snippet:
.controller('MainController', function($scope, $route, $routeParams, $location) {
//Here setting scope with $route object
$scope.$route = $route;
$scope.$location = $location;
$scope.$routeParams = $routeParams;
})
In the .run section of the main module of my application, I have an event handler for the $locationChangeStart event. I want to use this in order to confirm discarding unsaved changes. The problem is that I need a reference to the $scope in order to perform these checks.
I tried adding that reference as I added the one for the $rootScope, but I get an error Uncaught Error: Unknown provider: $scopeProvider <- $scope.
How should I proceed to this? I am open for alternatives.
.run(['$rootScope', '$location', function ($rootScope, $location) {
$rootScope.$on("$locationChangeStart", function (event, next, current) {
if ($scope.unsavedChanges && !confirm('Unsaved changes') {
event.preventDefault();
}
});
}
You can only inject instances (not Providers) into the run blocks. This is from the doc of module.
angular.module('myModule', []).
run(function(injectables) { // instance-injector
// This is an example of a run block.
// You can have as many of these as you want.
// You can only inject instances (not Providers)
// into the run blocks
});
So you won't be able to inject $scopeProvider.
You could inject $scope to your function like;
.run(['$rootScope', '$location', '$scope', function ($rootScope, $location, $scope)
I have the following set up:
stApp.controller('AdminTableController', ['$rootScope', '$scope', 'gridService',
function ($rootScope, $scope, gridService) {
$scope.$watch('tableData.$pristine', function (newValue) {
$rootScope.broadcast("tableDataUpdated", {
state: page.$pristine
});
});
}])
stApp.controller('AdminGridController', ['$rootScope', '$scope', 'gridService',
function ($rootScope, $scope, gridService) {
$rootScope.on("tableDataUpdated", function (args) {
//args.state would have the state.
alert(args.state);
});
}])
When I run this code I am getting a message:
Object #<Object> has no method 'on'
Note that I tried this with both $rootScope.on and $scope.on
You must have meant $broadcast and $on (rather than broadcast and on), that is:
$rootScope.$broadcast("tableDataUpdated", { state: page.$pristine });
// ...
$rootScope.$on("tableDataUpdated", function (args) {
// ...
It's worth noting that $broadcast is used to delegate events to child or sibling scopes, whereas $emit will bubble events upwards to the scope's parents, hence;
When choosing $broadcast (and not $emit), one should either inject the root scope for tying the $on (as you nicely did) or call $on on the receiver's isolated scope, be it a child scope of the dispatcher.
See this post for elaboration on $emit and $broadcast.
If the event listener is in a child scope: $scope.$broadcast('MyEvent', args);
If the event listener is in a parent scope: $scope.$emit('MyEvent', args);
You could avoid any confusion by using $broadcast on $rootScope, since $rootScope is the parent of all scopes in an Angular application: $rootScope.$broadcast('MyEvent', args);
Whether you use $emit or $broadcast, the receiving controller listens with $scope.$on('MyEvent', function() {});
$rootScope.on("tableDataUpdated", function (args) {
//args.state would have the state.
alert(args.state);
});
should be
$rootScope.$on("tableDataUpdated", function (args) {
//args.state would have the state.
alert(args.state);
});
$scope.$emit('MyEvent', args);
from first controller, and receive in second controller:
$scope.$on('MyEvent', function() {});