In this plunk I have an Angular UI Modal wrapped in a directive. From the controller, I call a method to open the modal, but to do so I need to use $timeout
, otherwise, the DOM hasn't finished rendering the directive.
This seems to work, however, what would happen if whatever needs to be completed hasn't finished after the $timeout
expires? The $timeout
may work in a development environment but may fail in production. Is it a bad practice to use $timeout
? How to avoid using it in this example?
HTML
<div modal control="modalCtl"></div>
Javascript
var app = angular.module('app', ['ui.bootstrap']);
app.controller('myCtl', function($scope,$timeout) {
$scope.modalCtl = {};
$timeout(function(){
$scope.modalCtl.openModal();
},100);
})
.directive('modal', function ($uibModal) {
var directive = {};
directive.restrict = 'EA';
directive.scope = {
control: '='
};
directive.link = function (scope, element, attrs) {
scope.control = scope.control || {};
scope.control.openModal = function() {
scope.modalInstance = $uibModal.open({
template: '<button ng-click="close()">Close</button>',
scope: scope
})
};
scope.close = function () {
scope.modalInstance.close();
};
};
return directive;
});
To avoid using $timeout
the directive can notify controller when everything is ready. Take a look:
.directive('modal', function ($uibModal) {
var directive = {};
directive.restrict = 'EA';
directive.scope = {
control: '=',
onReady: '&' // <-- bind `onReady` with `onModalReady`
};
directive.link = function (scope, element, attrs) {
scope.control = scope.control || {};
scope.control.openModal = function() {
scope.modalInstance = $uibModal.open({
template: '<button ng-click="close()">Close</button>',
scope: scope
})
};
scope.close = function () {
scope.modalInstance.close();
};
scope.onReady(); // <-- notify controller
};
return directive;
});
Out HTML:
<div modal on-ready="onModalReady()" control="modalCtl"></div>
Our controller:
$scope.onModalReady = function(){
$scope.modalCtl.openModal();
}
Changed Plunker
About comment @Eduardo La Hoz Miranda
you should be ok with the timeout. I would decrease the time to 0, tho, since timeout will send your call to the bottom of the event loop.
Generally when we initialize $timeout
with 0 milliseconds or with no argument as:
$timeout(function(){
$scope.modalCtl.openModal();
});
We delay $scope.modalCtl.openModal()
to run before next digest cycle a.e. last in queue. So in this case directive link will run 1st from beginning to to the end and only after you will enter to $timeout
.
The $timeout may work in a development environment but may fail in production.
On Production you have the same code. It should work. I believe the problem is in something else. If you are not confident with $timeout
use above mentioned way I posted.
Your Logged Plunker
When link function of directive is finished, it can emit a message that it's ready.
And controller listens to this message and displays modal when received.
Code:
var app = angular.module('app', ['ui.bootstrap']);
app.controller('myCtl', function($scope,$timeout) {
$scope.modalCtl = {};
$scope.$on("hey", function() {
$scope.modalCtl.openModal();
});
})
.directive('modal', function ($uibModal) {
var directive = {};
directive.restrict = 'EA';
directive.scope = {
control: '='
};
directive.link = function (scope, element, attrs) {
scope.control = scope.control || {};
scope.control.openModal = function() {
scope.modalInstance = $uibModal.open({
template: '<button ng-click="close()">Close</button>',
scope: scope
})
};
scope.close = function () {
scope.modalInstance.close();
};
scope.$emit("hey");
};
return directive;
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With