Reputation: 685
I have the following directive for showing a popup to confirm an execution of a function on click.
Now i would like to use it within my controller to show a popup if the properties of an object has been changed and the user wants to change the location without saving the object before. Is that possible?
angular.module('app.confirm', [
'ui.bootstrap',
'template/modal/confirm.html',
])
.controller('ConfirmModalController', ['$scope', '$modalInstance', function($scope, $modalInstance) {
$scope.confirm = function() {
$modalInstance.close();
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
}])
.directive('confirm', ['$modal', function($modal) {
return {
restrict: 'A',
scope: {
confirm: '&',
title: '@confirmTitle',
message: '@confirmMessage',
confirmButtonText: '@confirmButtonText',
cancelButtonText: '@cancelButtonText'
},
link: function(scope, element, attributes) {
element.bind('click', function() {
var modal= $modal.open({
controller: 'ConfirmModalController',
templateUrl: 'template/modal/confirm.html',
size: 'sm',
scope: scope
});
modal.result.then(function() {
scope.confirm();
}, function() {
// Modal dismissed
});
});
}
};
}]);
angular.module('template/modal/confirm.html', []).run(['$templateCache', function($templateCache) {
$templateCache.put(
'template/modal/confirm.html',
'<div class="modal-header" ng-show="title">' +
'<strong class="modal-title">{{title}}</strong>' +
'</div>' +
'<div class="modal-body">' +
'{{message}}' +
'</div>' +
'<div class="modal-footer">' +
'<a href="javascript:;" class="btn btn-link pull-left" ng-click="cancel()">{{cancelButtonText}}</a>' +
'<button class="btn btn-danger" ng-click="confirm()">{{confirmButtonText}}</button>' +
'</div>'
);
}]);
You can use it like that:
<button
confirm="delete(id)"
confirm-title="Really?"
confirm-message="Really delete?"
confirm-button-text="Delete"
cancel-button-text="Cancel"
class="btn btn-danger"
>
Delete
</button>
Upvotes: 6
Views: 25329
Reputation: 3497
N0-$watch solutions:
1.
Provide your controller with a callback which receives an exposed interface from your directive. Your controller grabs the interface and uses it in script however it desires. Simple and can be implemented on any existing directive.
app.directive("simpleDialog",function(simpleDialog){
return{
template:"<button ng-click='open()'>open from directive</button>",
scope:{
onInit : "&onInit"
},
link: function(scope){
scope.open = simpleDialog.open;
scope.onInit({interface:{open:scope.open}});
}
}
});
Much more complicated but an excellent pattern...
2.
If you wish to make a directive which also has a programmable interface then I suggest implementing the core of the directive as a provider. You could then implement your directive based on the provider and in cases where you wanted to access the same functionality completely through script, you could operate directly on the provider by injecting it into your controller.
This is the implementation strategy followed by ngDialog
Also, as you are are creating a confirmation dialog you would find this pattern useful as your open method could return a promise which can be resolved or rejected by your dialog, allowing your controller to respond based on the promise.
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js@*" data-semver="1.3.0" src="//code.angularjs.org/1.3.0/angular.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body ng-controller="myCtrl">
<h1>Exposing a Directive interface to a controller</h1>
<div simple-dialog on-init="initCallback(interface)"></div>
<p><button ng-click="open()">Open from controller</button></p>
<p><button ng-click="open2()">Open from Exposed interface</button></p>
<script>
var app = angular.module("app",[]);
app.controller("myCtrl",function(simpleDialog,$scope){
$scope.open = simpleDialog.open;
$scope.open2 = function(){
this.interface.open();
}
$scope.initCallback = function(interface){
this.interface = interface;
}
});
app.provider("simpleDialog",function(){
this.$get = function(){
var publicMethods={
open:function(){
alert("Impelment Dialog Here");
}
}
return publicMethods;
}
});
app.directive("simpleDialog",function(simpleDialog){
return{
template:"<button ng-click='open()'>open from directive</button>",
scope:{
onInit : "&onInit"
},
link: function(scope){
scope.open = simpleDialog.open;
scope.onInit({interface:{open:scope.open}});
}
}
});
angular.bootstrap(document,["app"]);
</script>
</body>
</html>
Upvotes: 7
Reputation: 4302
You can watch for a change in a scope property in your directive.
For example, add a confirm-show-when
<button
confirm="delete(id)"
...
confirm-show-when="state.showConfirmDialog"
...
>
Delete
</button>
Add it to your directive definition
.directive('confirm', ['$modal', function($modal) {
return {
restrict: 'A',
scope: {
confirm: '&',
title: '@confirmTitle',
message: '@confirmMessage',
confirmButtonText: '@confirmButtonText',
cancelButtonText: '@cancelButtonText',
showWhen: '=confirmShowWhen'
},
link: function(scope, element, attributes) {
var showModal = function() {
var modal= $modal.open({
controller: 'ConfirmModalController',
templateUrl: 'template/modal/confirm.html',
size: 'sm',
scope: scope
});
modal.result.then(function() {
scope.confirm();
}, function() {
// Modal dismissed
// set showWhen back to false
scope.showWhen = false;
});
};
element.bind('click', showModal);
scope.$watch('showWhen', function(newVal) {
if (newVal) {showModal()};
});
}
};
}]);
And in your controller just set the showConfirmDialog
to true when you want to show it.
// controller code
// set up the state object so we use the 'dot' notation
$scope.state = { showConfirmDialog: false };
// other controller code
if (userWantsToDelete) {
$scope.state.showConfirmDialog = true;
}
Upvotes: 0