Create several similar Angular dialogs without lots of code and templates - angularjs

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".

Related

Angular directive to disable child elements

Is it feasible to create a directive (canUpdate) which will enable me to enable/disable elements of my angular/web api application depending on results from a service that will test users permissions for a given user group or comma separated list of groups maybe.
My thinking is:
<div><button can-update="customerMgmt">Edit customer detail</button></div>
and my directive can perform the call to check this user is part of customerMgmt group and enable/disable appropriately.
However I am struggling to visualize/understand what my directives' template would look like.
If you was to write a directive that would perform this type of operation what would your directives' html template look like, as I'd want this to be applicable to any element, text input, button, anchor, label etc... i'd basically be saying if the user isn't in the group(s) specified on the attribute then disable/don't allow text entry/clicking etc...
So I've wrote the following that "appears" to be performing as I expect for now, need to test more next week but it seems to be enabling/disabling dependent on what my permissionsService.permissionsForObject method returns(which goes off to webApi controller).... Does this make sense to you?
(function () {
'use strict';
angular.module('blocks.permissions').directive('canWrite', canWriteDirective);
function canWriteDirective() {
return {
//scope: {},
restict: "A",
controller: CanWriteController,
controllerAs: "vm",
bindToController: false,
link: function (scope, iElement, iAttrs, controller) {
controller.canWrite(iAttrs.canWrite);
}
}
};
CanWriteController.$inject = ["permissionsService"];
function CanWriteController(permissionService) {
var vm = this;
vm.canWrite = canWrite;
vm.canUpdate = false;
function canWrite(group) {
permissionService.permissionsForObject(group).then(function (result) {
vm.canUpdate = true;
}).catch(function (result) {
vm.canUpdate = false;
});
}
}
})();

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
});

AngularUI Bootstrap Modal. Scope issue

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');
};
};

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