AngularUI Bootstrap Modal. Scope issue - angularjs

Im fairly new to AngularJS and I'm trying to create an List with "Draggables" that you can drop in 4 lists which are sortable. I got this to work with AngularUI-Sortable.
Now for the next part I'm trying to edit the content (more options and settings in the feature). With a modal from AngularUI-Bootstrap.
I got this to work with opening the content from the selected item I want to edit.
As you can see in the Plunker I almost got it working. Only thing I cant figure out is how I can get the {{ item }} to be {{ widgetOption }} AFTER I pressed the save button.
http://embed.plnkr.co/TTNccRuToObZuSmwYlTG/preview

My approach would be to keep a reference to the original object in your modal controller. So, assume the object passed in is the original, and it should only be modified if the form is saved. Making the fewest changes possible to your code to make it work, I came up with this plunker.
http://plnkr.co/edit/eSbEZZajsNfmVv3vmTdc?p=info
var modalInstance = $modal.open({
templateUrl: 'modal.html',
controller: ModalInstanceCtrl,
resolve: {
widgetOptionsLocal: function () {
return widgetOptions;
}
}
});
var ModalInstanceCtrl = function ($scope, $modalInstance, widgetOptionsLocal) {
var widgetOptionsOriginal = widgetOptionsLocal;
$scope.widgetOptions = angular.copy(widgetOptionsLocal);
$scope.ok = function () {
widgetOptionsOriginal.content = $scope.widgetOptions.content;
$modalInstance.close($scope.widgetOptions);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};

Related

Angularjs dialog call another controller function on close

AngularJs:
I'm trying to show an edit modal box. For this, I'm using $mdDialog of AngularJs material design. Here is my code:
angular.module('main').controller('UserCtrl', ['$scope', function($scope){
function loadUser(){
...
}
function showEditSale(index) {
var sale = $scope.user.Sales[index];
$mdDialog.show({
clickOutsideToClose: true,
fullscreen: $scope.customFullscreen,
parent: angular.element(document.body),
locals: {
sale: sale
},
templateUrl: 'front/templates/user/edit-sale.html',
controller: 'UserSaleCtrl'
})
.then(function(answer) {
}, function() {
});
}
}]);
angular.module('main').controller('UserSaleCtrl', ['$scope', function($scope, sale){
$scope.sale = sale;
function updateSale(){
...
**Need to reload user**
}
}]);
When I click on edit button against user sale, I call showEditSale passing the index of user.Sales item. A popup is displayed and I can edit values there. When clicking on update, an api is called which is updating sale information.
The problem is I want to reload this user. To reload, I have to call function from another controller.
My questions are:
Is there another way to pass data to dialog box instead of injecting in controller?
Do I have to use emit event of AngularJs to call function of another controller?
Can I somehow use same controller? I tried but I think $scope is being reset for modal dialog.
I added following code in UserSaleCtrl:
function updateSale(){
...
$rootScope.$broadcast('reloadUser');
}
Added following code in UserCtrl:
$scope.$on('reloadUser', function(event) {
loadUser();
});

Create several similar Angular dialogs without lots of code and templates

I'm using the modal module of Angular UI to create dialog boxes. I need to create several boxes which are quite similar. They look roughly like this:
Question?
yes (yellow button), no, cancel
Question?
yes (yellow button), no
cancel
option 1
option 2 (yellow button)
option 3
Is there a smart way I can avoid having to create HTML templates for each individual dialog? In other languages I would do something like "result = showDialog("Question?", ["ok", "cancel"])". I want to avoid putting things in my controller that should really not be there though (e.g. presentation stuff).
By the way, I don't get to decide how the UI looks by the way so they must look like this...
You can create a single template and a single controller that would handle all of these scenarios, just applying different options through the controller:
var ModalInstanceCtrl = function ($scope, $modalInstance, options) {
$scope.options = options;
$scope.yes = function () {
$modalInstance.close('yes');
};
$scope.no = function () {
$modalInstance.dismiss('no');
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
Then wrap the call to that modal inside a function that can be called wherever you need it:
$scope.showDialog = function (question, title, showCancel) {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
options: function () {
return {
question: question,
title: title,
showCancel: showCancel
};
}
}
});
modalInstance.result.then(function (action) {
$scope.selected = action;
}, function (action) {
$scope.selected = action;
});
};
Then a modal template that displays whatever you need based on the options passed in. Here's a plunker demonstrating the idea: http://plnkr.co/edit/y3c5bz5AXjrNo1XFzMBP
Of course, if you are going to use such a thing throughout your application, it would be wise to abstract it out into a service, rather than have it be a function attached to $scope.
Within a single html template, create multiple buttons, and in conjunction with custom options, show or hide the buttons using ng-show. Example: ng-show="showButton3".

How to pass a function to angular ui bootstrap modal

What is the best way to pass a function to an angular ui bootstrap modal dialog? I created a method in my modal controller that calls $scope.$parent.myMethod() like so:
$scope.isChosen = function(concept) {
return $scope.$parent.isChosen(concept);
};
This works, but I would rather pass the function to the modal in a similar way to how functions are passed to directives. I've tried to use the modal "resolve" parameter to do this but without success. Is it possible to resolve a function for a modal, and if so, what is the syntax? If not possible, is there any other way to do this other than accessing the parent scope?
Edit: Here is a plunk attempting to pass a method to a modal, it is a little simplified but represents what I'm trying to do: http://plnkr.co/edit/eCjbZP
When you are defining your modal , you must resolve like this :
// here is the controller where you want to trigger the modal to open
$scope.openModal = function () {
// somewhere in your html , you may click on a button to envoke openModal()
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
size: size,
resolve: {
isChosen: function () {
return $scope.isChosen;
}
}
});
};
And later on , in your modalCtr you can inject isChosen like this :
app.controller('modalCtrl',function($scope,isChosen){
// now you can use your isChosen function however you want
});

Can I change a directive's controller for different instances of the directive?

I have created a directive called modalDialog, which is basically that, a modal dialog. It uses transclude, so I can later do this in my code:
<div modal-dialog id="dialog" dialog-title="This is my Dialog">
...
here goes the content of the dialog
</div>
I want to use this directive in different places of my application, and for different purposes. The content of the dialogs will vary, naturally, so it would be very nice to have a way to pass the Controller to the directive, in the same way that I pass the dialog-title or any other parameter.
I thought about wrapping the modal-dialog in a div, with a controller set on it. Like this:
<div ng-controller="ThisInstanceController">
<div modal-dialog id="dialog" dialog-title="This is my Dialog">
...
here goes the content of the dialog
</div>
</div>
But I don't quite like it. Is there a more elegant way to do it?
Take a look at Angular-UI modals. They have a pretty elegant way of using modals. In short you can pass which controller you'd like to initialize when the modal opens.
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log('Modal dismissed at: ' + new Date());
});
};
The nice part as well is you can pass data with the resolve between controllers.

AngularJS modal window directive

I'm trying to make a directive angularJS directive for Twitter Bootstrap Modal.
var demoApp = angular.module('demoApp', []);
demoApp.controller('DialogDemoCtrl', function AutocompleteDemoCtrl($scope) {
$scope.Langs = [
{Id:"1", Name:"ActionScript"},
{Id:"2", Name:"AppleScript"},
{Id:"3", Name:"Asp"},
{Id:"4", Name:"BASIC"},
{Id:"5", Name:"C"},
{Id:"6", Name:"C++"}
];
$scope.confirm = function (id) {
console.log(id);
var item = $scope.Langs.filter(function (item) { return item.Id == id })[0];
var index = $scope.Langs.indexOf(item);
$scope.Langs.splice(index, 1);
};
});
demoApp.directive('modal', function ($compile, $timeout) {
var modalTemplate = angular.element("<div id='{{modalId}}' class='modal' style='display:none' tabindex='-1' role='dialog' aria-labelledby='myModalLabel' aria-hidden='true'><div class='modal-header'><h3 id='myModalLabel'>{{modalHeaderText}}</h3></div><div class='modal-body'><p>{{modalBodyText}}</p></div><div class='modal-footer'><a class='{{cancelButtonClass}}' data-dismiss='modal' aria-hidden='true'>{{cancelButtonText}}</a><a ng-click='handler()' class='{{confirmButtonClas}}'>{{confirmButtonText}}</a></div></div>");
var linkTemplate = "<a href='#{{modalId}}' id= role='button' data-toggle='modal' class='btn small_link_button'>{{linkTitle}}</a>"
var linker = function (scope, element, attrs) {
scope.confirmButtonText = attrs.confirmButtonText;
scope.cancelButtonText = attrs.cancelButtonText;
scope.modalHeaderText = attrs.modalHeaderText;
scope.modalBodyText = attrs.modalBodyText;
scope.confirmButtonClass = attrs.confirmButtonClass;
scope.cancelButtonClass = attrs.cancelButtonClass;
scope.modalId = attrs.modalId;
scope.linkTitle = attrs.linkTitle;
$compile(element.contents())(scope);
var newTemplate = $compile(modalTemplate)(scope);
$(newTemplate).appendTo('body');
$("#" + scope.modalId).modal({
backdrop: false,
show: false
});
}
var controller = function ($scope) {
$scope.handler = function () {
$timeout(function () {
$("#"+ $scope.modalId).modal('hide');
$scope.confirm();
});
}
}
return {
restrict: "E",
rep1ace: true,
link: linker,
controller: controller,
template: linkTemplate
scope: {
confirm: '&'
}
};
});​
Here is JsFiddle example http://jsfiddle.net/okolobaxa/unyh4/15/
But handler() function runs as many times as directives on page. Why? What is the right way?
I've found that just using twitter bootstrap modals the way the twitter bootstrap docs say to is enough to get them working.
I am using a modal to house a user edit form on my admin page. The button I use to launch it has an ng-click attribute that passes the user ID to a function of that scope, which in turn passes that off to a service. The contents of the modal is tied to its own controller that listens for changes from the service and updates values to display on the form.
So.. the ng-click attribute is actually only passing data off, the modal is still triggered with the data-toggle and href tags. As for the content of the modal itself, that's a partial. So, I have multiple buttons on the page that all trigger the single instance of the modal that's in the markup, and depending on the button clicked, the values on the form in that modal are different.
I'll take a look at my code and see if I can pull any of it out to build a plnkr demo.
EDIT:
I've thrown together a quick plunker demo illustrating essentially what I'm using in my app: http://embed.plnkr.co/iqVl0Wb57rmKymza7AlI/preview
Bonus, it's got some tests to ensure two password fields match (or highlights them as errored), and disables the submit button if the passwords don't match, or for new users username and password fields are empty. Of course, save doesn't do anything, since it's just a demo.
Enjoy.
There is a working native implementation in AngularStrap for Bootstrap3 that leverages ngAnimate from AngularJS v1.2+
Demo : http://mgcrea.github.io/angular-strap/##modals
You may also want to checkout:
Source : https://github.com/mgcrea/angular-strap/blob/master/src/modal/modal.js
Plunkr : http://plnkr.co/edit/vFslNmBAoKPVXtdmBXgv?p=preview
Well, unless you want to reinvent this, otherwise I think there is already a solution.
Check out this from AngularUI. It runs without twitter bootstrap.
I know it might be late but i started trying to figure out why the handler got called several times as an exercise and I couldn't stop until done :P
The reason was simply that each div you created for each modal had no unique id, once I fixed that everything started working. Don't ask me as to what the exact reason for this is though, probably has something to do with the $('#' + scope.modalId).modal() call.
Just though I should post my finding if someone else is trying to figure this out :)

Resources