Passing data between controllers in uib modal - angularjs

I have written a common uib modal in my angularjs app. The content of its body and footer needs to change according to some situations so I have given it like this as of now. The code is actually working fine but need to make sure if Im doing things the right way...
Here on clicking a button of modal I'm actually comparing the text of the button clicked. I'm not expecting the modal as a directive.
HTML code
<div class="modal-header" >
<button type="button" class="close" ng-click="close()" data-dismiss="modal">×</button>
<h4><b>{{customModal.title}}</b></h4>
</div>
<div class="modal-body">
{{customModal.body}}
</div>
<div class="modal-footer" >
<span ng-repeat="item in customModal.buttons">
<button type="button" class="{{item.btnClass}}" ng-click="modalBtnClick(customModal,item);close()" data-dismiss="modal" ng-if="item.show">{{item.text}}</button>
<span>
</div>
Calls the modal on clicking.....
$scope.areYouSureModalInstance = $uibModal.open({
animation: true,
templateUrl: 'views/Modal.html',
controller: 'ModalCtrl',
windowClass: 'nested-modal',
scope: $scope,
resolve: {
items: function() {
let btns=[
{id:1,text:"Yes",show:true, btnClass:"btn btn-success"},
{id:2,text:"No",show:true, btnClass:"btn btn-danger"},
{id:3,text:"Cancel",show:true, btnClass:"btn btn-default"}
];
$scope.customModal = {
id:'confirm',
title:'Confirm',
body:'Do you want to save the changes you have made?',
buttons:btns
};
//passes the modal properties
return $scope.customModal;
}
}
});
//Inside the uib modal controller
function ModalCtrl($scope) {
$scope.close = function() {
$scope.areYouSureModalInstance.close();
};
$scope.modalBtnClick=function(data,btn){
if(data.id==="confirm"){
$scope.$emit('close.confirm',btn);
}
};
}
//If the user clicks on any button
$scope.$on('close.confirm', function(event, data) {
if (data.text.toLowerCase()==="no") {
//do some stuff here
}
if (data.text.toLowerCase()==="yes") {
//do some stuff here
}
$scope.modalInstance.close();
});
Is there a better way to do this with two controller and not using the modal a directive...

$uibModal.open().result returns a Promise.
So therefore subscribe to the promise result by doing
$scope.areYouSureModalInstance.then(function(args) {
//arguments that the modal returned
console.log(args);
},function () {
//errors
});
for closing and confirmation method
function ModalCtrl($scope, $uibModalInstance) {
$scope.close = function() {
$uibModalInstance.dismiss();
};
$scope.modalBtnClick=function(data,btn){
if(data.id==="confirm"){
// btn -> arguments that you pass
$uibModalInstance.close(btn);
}
};
}

Related

ui-bootstrap: accessing modal instance from within directive controller

Noob alert!
The question here is exactly the one that I have: angularjs - Accessing ui-bootstrap modal dismiss and close function from another controller
However, I don't understand the answer that was chosen as correct! I understand the words but don't understand how to share the controller.
I have an app controller that opens a modal, and within the modal's template, I have a directive. I would like to be able manipulate the modal instance from within the directive's controller.
Here's my markup:
<script type="text/ng-template" id="settingsModal">
<div class="modal-header">
<h3 class="modal-title">Confirm update!</h3>
</div>
<div class="modal-body">
<calendar-settings cid="calendarId"/>
</div>
<div class="modal-footer">
<!-- I want these buttons inside the directive instead -->
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</script>
In the body of the modal, I am calling the calendarSettings directive, and I'd like to be able to use the .dismiss and .close methods of the modal instance from within my directive.
Here's what the open method looks like:
var modalInstance = $modal.open({
templateUrl: 'settingsModal',
controller: 'ModalInstanceController',
resolve: {
item: function() {
return $scope.sEntry;
},
cid: function() {
return id;
}
},
reject: {
item: function() {
return $scope.sEntry;
},
cid: function() { return null; }
}
});
And here's the ModalInstanceController:
calendarsApp.controller('ModalInstanceController', function($scope, $modalInstance, item, cid){
$scope.item = item;
$scope.calendarId = cid;
console.log(item);
$scope.ok = function () {
$modalInstance.close($scope.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
I think what I want to do is easy, but I'm not sure how to expose the modal instance controller to the directive, or if there is a different approach I should be thinking about.
NOTE: I didn't paste the code from the directive in here. But I'm trying to access the close and dismiss methods of the modal instance from within my directive's controller.
Thanks for any help!
So
var modalInstance = $modal.open({
templateUrl: 'settingsModal',
controller: 'ModalInstanceController',
resolve: {
item: function() {
return $scope.sEntry;
},
cid: function() {
return id;
}
},
reject: {
item: function() {
return $scope.sEntry;
},
cid: function() { return null; }
}
//This comes from ModalInstanceController result is
//equivalent to whatever u send back
}).result.then(function(result){
console.log(result); //Press f12 on chrome and go to console.
});
calendarsApp.controller('ModalInstanceController', function($scope, $modalInstance, item, cid){
$scope.item = item;
$scope.calendarId = cid;
console.log(item);
$scope.ok = function () {
//You are sending back $scope.item but if you want everything
//send back the $scope which is equivalent to sending back //ModalInstanceController
// $modalInstance.close($scope.item);
$modalInstance.close($scope);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});

UI Bootstrap Modal within Directive

I'm using Angular 1.4.1 and UI Bootstrap 0.13.
I have a directive from which I'm opening a modal. The modal opens fine, but the buttons seemingly don't get bound to their handlers - they don't do anything. I've used this same code in another project just fine, except not from within a directive.
My directive:
(function () {
var app = angular.module('myApp');
app.directive('someDirective', function () {
return {
restrict: 'E',
templateUrl: 'SomeDirective.html',
scope: {
list1: '=list1',
list2: '=list2',
save: '&'
},
controller: ['$scope','$modal','myService', function ($scope,$modal,myService) {
$scope.openModal = function () {
var modalInstance = $modal.open({
templateUrl: 'ModalTemplate.html',
controller: 'modalController',
backdrop: 'static',
size: 'sm',
resolve: {
saveData: function () {
//do save action
}
}
});
modalInstance.result.then(
function (itemToSave) {
//save item
},
function () {
//Cancel
}
);
};
}]
}
});
}());
The modal's controller:
(function() {
var app = angular.module('myApp');
app.controller('modalController', [
'$scope', '$modalInstance', 'saveData',
function($scope, $modalInstance, saveData) {
$scope.saveData = saveData;
$scope.save = function() {
$modalInstance.close($scope.saveData);
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
}
]);
}());
And the template for the modal content:
<div class="modal-header bg-info">
<h3 class="modal-title">Add New Record</h3>
</div>
<div class="modal-body">
<form class="form-horizontal" role="form"></form>
</div>
<div class="modal-footer bg-info">
<button class="btn btn-primary" ng-click="save()">Save</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
My thought is that I'm having issues with scope, but I can't track it down. I have put break points on modalController. The app.controller() call happens when the app loads, I've seen that. But breakpoints within save() and cancel() never get hit.
Can someone help me figure out why the modal's buttons don't do anything?
This turned out to be a stupid mistake. At some point I apparently overwrote the name of one of the other controllers in my project with the same name of the controller I was using for the modal. The other controller did not have save() or cancel() methods so nothing was happening. As soon as I fixed my error and all controllers once again had their proper names this started working again.

Closing A Modal in AngularJS UI

Quite new with AngularJS (and AngularJS UI) and I am unable to close a modal window.
The HTML code as follows:
<div>
<div data-ng-show="agencies || agencies.length > 0">
<div>
<h3>
Agencies
</h3>
</div>
<div>
<span class="glyphicon glyphicon-cloud-upload"></span> Add
</div>
</div>
</div>
Controller:
'use strict';
app.controller('agencyController', function ($scope, $modal, agenciesDataService) {
$scope.agencies = [];
$scope.agency = null;
init();
function init() {
getAgencies();
};
function getAgencies() {
var onResponse = function (results) {
$scope.agencies = results.data;
};
var onError = function (error) {
alert(error.message);
};
agenciesDataService.getAgencies()
.then(onResponse, onError);
};
$scope.showModalAddAgency = function () {
var modalInstance = $modal.open({
templateUrl: 'app/views/agencydetail.html',
controller: 'agencyController',
backdrop: 'static'
});
};
$scope.addAgency = function addAgency() {
var currentAgency = this.agency;
var onResponse = function (results) {
currentAgency.Id = results.data.Id;
currentAgency.Name = results.data.Name;
currentAgency.AgencyPortalAccountId = results.data.AgencyPortalAccountId;
$scope.agencies.push(currentAgency);
currentAgency = {};
// HOW TO CLOSE THE MODAL FROM HERE? WHAT FUNCTION DO I CALL?
};
var onError = function (error) {
//alert(error.message);
}
agenciesDataService.addAgency(currentAgency)
.then(onResponse, onError);
};
});
Pretty much, after making the POST request, I want to close the modal window, but I don't have any idea how. Not sure how I can reference the modal window which I opened.
Any insight appreciated.
Update:
My modal's html includes an Save and Cancel button.
<div class="modal-footer">
<button class="btn btn-primary normal-button"
ng-click="addAgency()">Save</button>
<button class="btn btn-warning" ng-click="$dismiss()" style="width:100px;">Cancel</button>
</div>
The modal does close when I hit the Cancel button. What I want to achieve is being able to close the modal when the addAgency function is completed.
You need to save your modal instance in the $scope so that you have a reference to it later.
So you'd init your $scope.modalInstance = null; at the top.
Then you'd open your modal like this:
$scope.modalInstance = $modal.open({
templateUrl: 'app/views/agencydetail.html',
controller: 'agencyController',
backdrop: 'static'
});
To close, you would then call $scope.modalInstance.close(); (which would go where you have your // HOW TO CLOSE THE MODAL FROM HERE? WHAT FUNCTION DO I CALL? comment.
Here's a plunkr that shows how to do it: EXAMPLE
To close your modal you have 2 functions : "close" and "dismiss".
Let say that the end of your modal html file looks like that :
<div class="modal-footer">
<input type="button" class="submit" value="Save" ng-click="ok()" />
<input type="button" class="cancel" value="Cancel" ng-click="cancel()" />
</div>
All you have to write in your modal controller is that :
$scope.cancel = function(){
$modalInstance.dismiss('cancel');
};
$scope.ok = function(){
// you can pass anything you want value object or reference object
$modalInstance.close("clicked ok");
};
And if you want to know if the user clicked on "cancel" or "ok" you have to change your function "showModalAddAgency" like that :
$scope.showModalAddAgency = function () {
var modalInstance = $modal.open({
templateUrl: 'app/views/agencydetail.html',
controller: 'agencyController',
backdrop: 'static'
});
modalInstance.result.then(function (resultOk) {
// OK
}, function (resultCancel) {
// CANCEL
});
};
I hope my answer fit you.
Have a nice day !

Angular UI Bootstrap modal inside ngRepeat

I am making an app, where I have a lot of input fields. Those input fields are generated from JSON object array field with AngularJS ngRepeat directive and have a button next to them which open an Angular UI Bootstrap modal to edit this value in a bigger textarea. I cannot figure out how to reference this model property to Angular UI Bootstrap so that I can save the changes made in modal. Since this functionality is needed in multiple views, I turned it into a service.
I have made a plunker to illustrate my problem.
http://plnkr.co/edit/ZrydC5UExqEPvVg7PXSq?p=preview
Currently in plunker example modal contains textarea, but I will actually need to use Text-Angular directive, because those fields contain some HTML markup and I would be easier for users to edit values with this nice addon.
TextAngular
EDIT: Please, if you are taking time to answer, you might aswell take a little more time to edit my plunker example to show exactly how your solution would look like, because seems that everyone who tries to help me, think they know the solution, but in reality it doesn't work :( Thanks!
I personally like to decorate my $scope with the services (i.e. $scope.modalService = ModalService;), so I understand the source of the logic. In the ng-repeat you then pass the value item into the method call:
<div class="input-group">
<input class="form-control" ng-model="value.value">
<span class="input-group-addon" ng-click="modalService.openTextEditModal(value)">
<span class="glyphicon glyphicon-align-justify"></span>
</span>
</div>
The modal service and modal template would then reference the object, in this case a clone of the object to help with state management, not the text:
app.factory('ModalService', function($modal) {
return {
openTextEditModal: function(item) {
var modalInstance = $modal.open({
templateUrl: 'modal.html',
backdrop: 'static',
controller: function($scope, $modalInstance, $sce, item) {
var clone = {};
angular.copy(item, clone);
$scope.clone = clone;
$scope.close = function() {
$modalInstance.dismiss('cancel');
};
$scope.save = function() {
angular.extend(item, clone);
$modalInstance.close();
};
},
size: 'lg',
resolve: {
item: function() {
return item;
}
}
});
}
};
});
With the corresponding modal template changes:
<div class="modal-header">
<h3 class="modal-title">Edit text</h3>
</div>
<div class="modal-body">
<textarea class="form-control" ng-model="clone.value"></textarea>
</div>
<div class="modal-body">
<button class="btn btn-warning" ng-click="close()">Close</button>
<button class="btn btn-success pull-right" ng-click="save()">Save</button>
</div>
It might be easier to make a controller for your modal and pass in the objects that you need from your scope. Those will be passed by reference so changes to them will update the scope of your parent scope. Something like this in your MainCtrl :
var modalInstance = ModalService.open({
templateUrl: 'modal.html',
controller: 'YourModalController',
size: 'lg',
resolve: {
text: function () {
return $scope.editText;
}
}
});
modalInstance.result.then(function () {
});
And then in your modal controller:
app.controller('YourModalController', ['$scope', '$modalInstance', 'text', function YourModalController($scope, $modalInstance, text) {
$scope.text = text;
$scope.close = function() {
$modalInstance.dismiss('cancel');
};
$scope.save = function() {
$modalInstance.close($scope.text);
};
}]);
And if you want it to be reusable so you do not have to duplicate the modal instance code in the parent controller you could make that a directive.
You can return the promise and then handle the success callback in the controller.
In the openTextEditModal function, return modalInstance.result;.
Then, in the controller you can do this:
$scope.editText = function(text){
ModalService.openTextEditModal(text).then(function(newText){
console.log(newText);
$scope.text = newText; // do something with the new text
});
};

angularjs custom confirm box

So I'm trying to implement a custom confirm box using Angular. Ideally, I would simply like to add an attribute to enable the functionality. Example:
<button type="button" ng-click="delete(foo)">Delete</button> -> <button type="button" ng-click="delete(foo)" ng-confirm="Are you sure you want to delete this foo?">Delete</button>
(foo is inside an ng-repeat... foo in fooList..)
So all of the problems I am having revolve around tying the click event that would normally happen to a different button. I have a seperate directive "confirmBox" that will create my modal (not using bootstrap) and handle all of the showing/hiding/etc.
What I am currently using requires me to alter my ng-click functionality, which I really want to get away from:
Current Implementation:
<button ... ng-click="confirm('Are you sure you want to delete this foo?, 'delete', foo)">Delete</button>
var confirmModule = angular.module('confirm', []);
confirmModule.run(function($rootScope) {
$rootScope.confirm = function(text, func, obj) {
$rootScope.$broadcast('confirm', func, obj, text);
};
});
confirmModule.directive('confirmBox', function($parse) {
return {
restrict: 'A',
template: myModalTemplate,
link: function(scope, element, attrs){
element.hide();
var noBtn = element.find("[name='no']");
noBtn.bind("click", function() {
element.hide();
});
scope.$on("confirm", function(event, func, obj, text) {
var yesBtn = element.find("[name='yes']");
element.show();
yesBtn.unbind("click").bind("click", function() {
scope[func](obj);
});
});
}
}
});
Anyone have any ideas? I started by adding the directive for the button and then unbinding the click event so ng-click doesn't fire. Then I am left with the string 'delete(foo)' from the ng-click attribute that I can execute with $parse(attrs.ngClick)(scope), but I don't know how to tie that to the separate directives button click.
Edit: Here is a fiddle with my current attempt at implementation. The problem is the variable being passed in to the function is always undefined.
http://jsfiddle.net/UCtbj/2/
Edit2: Updated implementation, however I don't particularly like how it links the two directives together by targetting the other directives elements.
http://jsfiddle.net/UCtbj/3/
It seems to me that you're trying to do things the jQuery way from within the directive. However, what you want is as simple as pulling in the UI-Bootstrap directive for confirming actions. http://plnkr.co/edit/JhfAF1?p=preview
First simple service for modal windows:
app.service('ConfirmService', function($modal) {
var service = {};
service.open = function (text, onOk) {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalConfirmCtrl',
resolve: {
text: function () {
return text;
}
}
});
modalInstance.result.then(function (selectedItem) {
onOk();
}, function () {
});
};
return service;
})
app.controller('ModalConfirmCtrl', function ($scope, $modalInstance, text) {
$scope.text = text;
$scope.ok = function () {
$modalInstance.close(true);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
Then simple directive that uses it:
app.directive('confirm', function(ConfirmService) {
return {
restrict: 'A',
scope: {
eventHandler: '&ngClick'
},
link: function(scope, element, attrs){
element.unbind("click");
element.bind("click", function(e) {
ConfirmService.open(attrs.confirm, scope.eventHandler);
});
}
}
});
And here u go:
<button ng-click="test(12)" confirm='Are you sure?'>Button</button>
http://plnkr.co/edit/LOZOnsVyx3JU5XoKYn74?p=preview
To allow a button to be marked up like
<button type="button" ng-click="deleteItem(drink)" ng-confirm="Are you sure you want to delete '{{drink.name}}'">Delete</button>
You can write a directive that
Intercepts the click event before ngClick's click handler can run
Opens a dialog (using $modal and not the removed $dialog)
On close of the dialog (which is treated as a success) run the function specified by the ngClick attribute on the element.
Basing the code on the previous answer, you can do this as follows:
app.directive('ngConfirm', function($modal, $parse) {
return {
// So the link function is run before ngClick's, which has priority 0
priority: -1,
link: function(scope, element, attrs) {
element.on('click', function(e) {
// Don't run ngClick's handler
e.stopImmediatePropagation();
$modal.open({
templateUrl: 'ng-confirm-template',
controller: 'ngConfirmController',
resolve: {
message: function() {
return attrs.ngConfirm;
}
}
}).result.then(function() {
// Pass original click as '$event', just like ngClick
$parse(attrs.ngClick)(scope, {$event: e});
});
});
}
};
});
which needs a simple controller:
app.controller('ngConfirmController', function($scope, $modalInstance, message) {
$scope.message = message;
$scope.yes = function() {
$modalInstance.close();
};
$scope.no = function() {
$modalInstance.dismiss();
};
});
and template for the dialog:
<script type="text/ng-template" id="ng-confirm-template">
<div class="modal-body">
<p>{{message}}</p>
</div>
<div class="modal-footer">
<button class="btn btn-link pull-left" ng-click="no()">No</button>
<button class="btn btn-primary pull-right" ng-click="yes()">Yes</button>
</div>
</script>
You can see this running at http://plnkr.co/edit/Gm9lFsGb099w6kCMQoVY?p=preview
Edit: changed plunker link to example without scrollbar appearing/disappearing on display of the dialog
Here is a nice directive for that.That is ngBootbox. Check it out.
<button class="btn btn-lg btn-primary"
ng-bootbox-title="A cool title!"
ng-bootbox-custom-dialog="Some custom text"
ng-bootbox-buttons="customDialogButtons"
ng-bootbox-class-name="some-class">
Custom dialog
</button>
<script>
$scope.customDialogButtons = {
warning: {
label: "Warning!",
className: "btn-warning",
callback: function() { $scope.addAction('Warning', false); }
},
success: {
label: "Success!",
className: "btn-success",
callback: function() { $scope.addAction('Success!', true) }
},
danger: {
label: "Danger!",
className: "btn-danger",
callback: function() { $scope.addAction('Danger!', false) }
},
main: {
label: "Click ME!",
className: "btn-primary",
callback: function() { $scope.addAction('Main...!', true) }
}
};
</script>
Demo
ngBootbox
I created a repo for this functionality. It wraps the ui-bootstrap modal to produce a confirmation box. It is customizable and easily integrated into any application.
Here is the link to the GitHub: https://github.com/Schlogen/angular-confirm
Example Usages:
As a directive:
<button type="button" ng-click="delete()" confirm-if="checked" confirm="Are you sure, {{name}}?">Delete</button>
As a service:
$confirm({text: 'Are you sure you want to delete?'})
.then(function() {
$scope.deletedConfirm = 'Deleted';
});
Ok, here is the one I ended up going with
1) Create a service for the dialog
app.service('dialogModal', [
'$modal', function($modal) {
return function(message, title, okButton, cancelButton) {
okButton = okButton === false ? false : (okButton || 'Yes');
cancelButton = cancelButton === false ? false : (cancelButton || 'No');
var modalInstance = $modal.open({
templateUrl: '/templates/deletePrompt.html',
controller: ModalInstanceCtrl,
resolve: {
settings: function() {
return {
modalTitle: title,
modalBody: message,
okButton: okButton,
cancelButton: cancelButton
};
}
}
});
// return the modal instance
return modalInstance;
}
}
]);
2) Create a controller and pass the model instance in it
var ModalInstanceCtrl = function ($scope, $modalInstance, settings) {
angular.extend($scope, settings);
$scope.ok = function () {
$modalInstance.close(true);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
3) included the link into the header to take on default styling
<link data-require="bootstrap-css#3.x" data-semver="3.1.1" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
4) overwrote the styling in my own css
5) Here is my delete prompt template
<div id="overlayClearMainDiv" class="dialog-modal">
<div id="overlayClearText">
<span>{{modalBody}}</span>
</div>
<div id="overlayClearButton">
<button id="overlayClearYesButton" class="confirmButton" type="button" ng-click="ok()" ng-show="okButton">{{okButton}}</button>
<button class="confirmButton-white" ng-click="cancel()" ng-show="cancelButton">{{cancelButton}}</button>
</div>
</div>
Here's a quick one for you - http://plnkr.co/edit/YklthDZcknmvMjU5A6pe?p=preview
So basically if you are interested in showing a modal dialog once a user clicks on let's say, a button there's no need to make it difficult.
All you need is a simple directive that encapsulate $modal service found in ui-bootstrap.
In my simple example I just pass in a string representing a message and then defining a on-confirm attribute that my directive invokes once the user confirms. Invoking the function itself leverages the awesomeness of $parse to resolve the expression and once resolved, invoke it with the scope.
Nice and clear and here's how it looks like.
View
<body ng-controller="AppController">
<input type="button" value="Delete"
confirm="'Are you sure you want to delete me?'" on-confirm="delete()" />
<script type="text/ng-template" id="modal.html">
<div class="modal-header">
<h3 class="modal-title">Confirm</h3>
</div>
<div class="modal-body">
<p>The world won't be a better place if you delete me.</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</script>
</body>
Controller / Directive
angular
.module('App', ['ui.bootstrap'])
.controller('AppController', ['$scope', function($scope){
$scope.delete = function(){
alert('Woho, Im deleted!');
};
}])
.directive('confirm', ['$modal', '$parse', function($modal, $parse){
return {
link: function(scope, el, attr){
el.bind('click', function(){
var instance = $modal.open({
templateUrl: 'modal.html',
controller: ['$scope', '$modalInstance', modalController]
});
instance.result.then(function(){
// close - action!
$parse(attr.onConfirm)(scope);
},function(){
// dimisss - do nothing
});
});
}
};
function modalController(modalScope, $modalInstance){
modalScope.ok = function(){
modalInstance.close();
};
modalScope.cancel = function(){
modalInstance.dismiss();
};
}
}]);

Resources