Angular directive check element? - angularjs

I'm hooking up a $modal service for confirmation boxes in my app and made a directive that only works for ng-click. Well I also need it to work for ng-change so I did it like the following:
.directive('ngConfirmClick', ['$modal',
function($modal) {
var ModalInstanceCtrl = function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close();
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
};
return {
restrict: 'A',
scope:{
ngConfirmClick:"&",
item:"="
},
link: function(scope, element, attrs) {
element.bind('click', function() {
var message = attrs.ngConfirmMessage || "Are you sure ?";
if(element == 'select'){
var modalHtml = '<div class="modal-body">' + message + '</div>';
modalHtml += '<div class="modal-footer"><button class="btn btn-success" ng-model="" ng-change="ok()">OK</button><button class="btn btn-warning" ng-change="cancel()">Cancel</button></div>';
} else {
var modalHtml = '<div class="modal-body">' + message + '</div>';
modalHtml += '<div class="modal-footer"><button class="btn btn-success" ng-click="ok()">OK</button><button class="btn btn-warning" ng-click="cancel()">Cancel</button></div>';
}
var modalInstance = $modal.open({
template: modalHtml,
controller: ModalInstanceCtrl
});
modalInstance.result.then(function() {
scope.ngConfirmClick({item:scope.item});
}, function() {
});
});
}
}
}
]);
You can see I'm trying to check if the element is a 'select' element but I'm not sure how angular's link method/function reads the element. Can I check it with a string like how I did it? (It doesn't work when I try this btw).
How can I check if the element I'm attaching my directive to is a select?

Angular's jqLite is a subset of jQuery and that is the element parameter passed into the link function (unless you load the full jQuery library, then it will be a jQuery object). As described in this post using element.prop('tagName') will return the element type which is a method included in the jqLite library.

So I got confused and the if statement should of been at the element.bind not at the var modalHtml...
Here's the updated code for me to get this to work with both ng-change and ng-click. I just added bind on click and bind on change with an if statement to check the element.context.tagName was select or not
directive('ngConfirmClick', ['$modal',
function($modal) {
var ModalInstanceCtrl = function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close();
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
};
return {
restrict: 'A',
scope:{
ngConfirmClick:"&",
item:"="
},
link: function(scope, element, attrs) {
console.log(element.context.tagName);
if(element.context.tagName == 'SELECT'){
element.bind('change', function() {
var message = attrs.ngConfirmMessage || "Are you sure ?";
var modalHtml = '<div class="modal-header"><h4 id="title-color" class="modal-title"><i class="fa fa-exclamation"></i> Please Confirm</h4></div><div class="modal-body">' + message + '</div>';
modalHtml += '<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>';
var modalInstance = $modal.open({
template: modalHtml,
controller: ModalInstanceCtrl
});
modalInstance.result.then(function() {
scope.ngConfirmClick({item:scope.item});
}, function() {
});
});
} else {
element.bind('click', function() {
var message = attrs.ngConfirmMessage || "Are you sure ?";
var modalHtml = '<div class="modal-header"><h4 id="title-color" class="modal-title"><i class="fa fa-exclamation"></i> Please Confirm</h4></div><div class="modal-body">' + message + '</div>';
modalHtml += '<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>';
var modalInstance = $modal.open({
template: modalHtml,
controller: ModalInstanceCtrl
});
modalInstance.result.then(function() {
scope.ngConfirmClick({item:scope.item});
}, function() {
});
});
}
}
}
}
]);

Related

Modal not working properly

I am facing a problem with the pop up modal in Angularjs. I have a button and on clicking I need to show a modal. Here is my code. When I click on Details button for first time, the modal pop up appears but when I click again.. the app.directive doesn't get called. Greatly appreciate the help. Thanks.
JS:
myApp.directive('modal', function () {
return {
template: '<div class="modal fade">' +
'<div class="modal-dialog">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
'<h4 class="modal-title">{{ title }}</h4>' +
'</div>' +
'<div class="modal-body" ng-transclude></div>' +
'</div>' +
'</div>' +
'</div>',
restrict: 'E',
transclude: true,
replace:true,
scope:true,
link: function postLink(scope, element, attrs) {
scope.title = attrs.title;
scope.$watch(attrs.visible, function(value){
if(value == true)
$(element).modal('show');
else
$(element).modal('hide');
});
$(element).on('shown.bs.modal', function(){
scope.$apply(function(){
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.modal', function(){
scope.$apply(function(){
scope.$parent[attrs.visible] = false;
});
});
}
};
});
.state("abc"){
controller:function(){
$scope.showModal = false;
$scope.toggleModal = function(){
$scope.showModal = false;
$scope.showModal = !$scope.showModal;
var aclpGuid = $stateParams.aclpGuid;
};
}
}
HTML:
<td>
<button ng-click="toggleModal()">Details</button>
<modal visible="showModal">
<label>Room Details</label>
</modal>
</td>
I guess the issue is the isolate scope which you have here scope:true inside the directive. Using this, the scope inside a directive is seperated from the scope outside. To confirm this I checked the scope variable with and without scope: true. Note the $$watchers here:
Without scope: true
With scope: true
The isolated scope has $$watchers == null. So any changes in the scope variable showDialog doesn't fire the watch event. So you might wanna use the parent scope i.e the controller's scope here.
The event fires once as when compiling the directive for the first time it attaches the $watchers to the element only once and for subsequent calls there is no relation between the outer scope to a directive's inner scope.
With that change and few others I have a working plunk.
controller:
myApp.controller('myCtrl', function($scope) {
$scope.showDialog = false;
$scope.toggleModal = function() {
$scope.showDialog = !$scope.showDialog;
};
});
Setting the showDialog to false, whenever the user closes the modal as #Mistalis suggested.
$(element).bind('hidden.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = false;
scope.showDialog = false;
});
});
directive:
myApp.directive('modal', function() {
return {
restrict: 'E',
transclude: true,
replace: true,
templateUrl: 'modal-partial.html',
//scope: true,
link: function postLink(scope, element, attrs) {
scope.showModal = function(visible, elem) {
if (!elem)
elem = element;
if (visible)
$(elem).modal("show");
else
$(elem).modal("hide");
}
scope.title = attrs.title;
scope.$watch(attrs.visible, function(value) {
if (value === true)
$(element).modal('show');
else
$(element).modal('hide');
});
$(element).bind('shown.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = true;
});
});
$(element).bind('hidden.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = false;
scope.showDialog = false;
});
});
}
};
});

AngularJs ui.bootstrap - Modal show error [$injector:unpr]

i am trying to use angular ui.bootstrap to implement some functionality like this but this shows an error
[$injector:unpr]
http://errors.angularjs.org/1.5.2/$injector/unpr?p0=%24modalProvider%20%3C-%20%24modal%20%3C-%20ngReallyClickDirective
my code is
app.js
var app = angular.module('app',['ui.router','oc.lazyLoad','ui.bootstrap','ngReallyClickModule']);
ngReallyClickModule.js
angular.module('ngReallyClickModule',['ui.router'])
.directive('ngReallyClick', ['$modal',
function($modal) {
var ModalInstanceCtrl = function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close();
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
};
return {
restrict: 'A',
scope:{
ngReallyClick:"&",
item:"="
},
link: function(scope, element, attrs) {
element.bind('click', function() {
var message = attrs.ngReallyMessage || "Are you sure ?";
var modalHtml = '<div class="modal-body">' + message + '</div>';
modalHtml += '<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>';
var modalInstance = $modal.open({
template: modalHtml,
controller: ModalInstanceCtrl
});
modalInstance.result.then(function() {
scope.ngReallyClick({item:scope.item});
}, function() {
//Modal dismissed
});
});
}
}
}
]);
View
<a ng-really-message="Are you sure ?" ng-really-click="test(item)" item="item" ng-repeat="item in [1,2,3,4,5]">Delete</a>
i want modal dialog on ng-really-click click. And call a function of the current controller on click of the modal Ok button.
i am using angular-ui-router and oclazyLoading
The answer is from the error, your are using some provider that you have not defined/injected.
It is hard to say exactly what is the problem considering you have many injections here, but from the error you gave us, there is no provider for $modal. AngularUI recently switched to uib prefixing most of its providers and directives (i.e. $uibModal). Try using the new version.
https://angular-ui.github.io/bootstrap

Angularjs Click event handling within link

I am trying to make a custom directive which would show different buttons based on attribute present. One of the button requires click event handler and I want to handle it within the directive as there will be multiple instances of this directive in same page. I tried the code below but to no avail.
'use strict';
angular
.module('test-template', [])
.directive('testTemplateBricks', [
'$compile',
'$timeout',
function($compile,$timeout) {
return {
restrict: 'A',
link: function($scope, iElm, iAttrs, controller) {
var el = "";
if(iAttrs.needImg=="true")
{
el += '<input type="file" style="display:none;" class="browse-file"/><button class="btn btn-info" ><span class="glyphicon glyphicon-picture" ng-click="browse()"></span></button>';
}
if(iAttrs.needTxt=="true")
{
el += ' <button class="btn btn-info"><span class="glyphicon glyphicon-pencil"></span></button>';
}
$compile(el)($scope);
iElm.append(el);
$scope.browse = function() { console.log("browsing");};
$timeout(function(){
iElm.on("click",function(e){
console.log("Browsing");
iElm.find("input[type=file]").click();
});
});
}
};
}
]);
EDIT: http://plnkr.co/edit/bNRLvWjEE7LLvhwRFIae?p=preview
In this sample I want to display the hidden file browser when the image button is clicked.
So I wouldn't recommend this approach to toggling the visibility of elements, it's something that's better handled in the template logic.
But to get you started I've taken your code and amended it somewhat (https://jsbin.com/negawu)
angular
.module('test-template', [])
.directive('testTemplateBricks', [
'$compile',
'$timeout',
function($compile,$timeout) {
return {
restrict: 'A',
template: '<input type="file" class="browse-file"/>' +
'<button class="btn btn-info" ng-show="showImage">' +
'<span class="glyphicon glyphicon-picture" ng-click="browse()"></span>' +
'</button>' +
'<button class="btn btn-info" ng-show="showText">' +
'<span class="glyphicon glyphicon-pencil"></span>' +
'</button>',
link: function($scope, iElm, iAttrs, controller) {
$scope.showImage = false;
$scope.showText = false;
if (iAttrs.needImg == "true") {
$scope.showImage = true;
}
if (iAttrs.needTxt == "true") {
$scope.showText = true;
}
$scope.browse = function() {
console.log("browsing");
};
}
};
}
]);

ng-model two way binding not working with ui bootstrap modal

I have following code for working with ui bootstrap modal. I am having a input field whose value has to be captured on the controller. But the input field value is not getting reflected on the controller after an value is entered on the modal.
angular.module('myApp')
.controller('mainController', ['$scope', '$modal', function($scope, $modal) {
$scope.openModal = function() {
$modal.open({
templateUrl: 'modal.html',
controller: BoardController
});
};
}])
.directive('modalDialog', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
template:
'<div class="modal-content">' +
'<div class="modal-header">' +
'<h4 ng-bind="dialogTitle"></h4>' +
'</div>' +
'<div class="modal-body" ng-transclude></div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-default" ' +
'ng-click="cancel()">Close</button>' +
'<button type="button" class="btn btn-primary" ' +
'ng-click="ok()">Save</button>' +
'</div>' +
'</div>'
};
});
var BoardController = function ($scope, $modalInstance) {
$scope.dialogTitle = 'Create new item';
$scope.placeholder = 'Enter item name';
$scope.inputname = '';
$scope.ok = function () {
console.log($scope.inputname);
$modalInstance.dismiss('cancel');
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
In 'modal.html' i have the following code:
<modal-dialog>
<input type="text" class="form-control" id="enter-name"
ng-model="inputname" placeholder={{placeholder}}>
{{ inputname }}
</modal-dialog>
So, after entering some text into the inputfield when i click the save the following line under $scope.ok() prints blank.
console.log($scope.inputname);
I guess this has something to do with scopes or may be transclusion. But i am not able to figure out whats causing this. I couldnt find the updated value in developer console also.
The problem here is transclusion. ngTransclude directive creates one more scope, but it is a sibling scope. Using transclusion makes it very difficult to access your scope. In your case you could retrieve model value like this:
$scope.ok = function () {
console.log($scope.$$childHead.$$nextSibling.inputname);
$modalInstance.dismiss('cancel');
};
But of course this is terrible. Fortunately, you can control what scope transclusion will use for rendered template if you make transclusion manually. For this you need to use link function with the fifth argument which is transclude function.
Your directive will become (note, that you don't use ng-tranclude directive in template anymore):
.directive('modalDialog', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
template:
'<div class="modal-content">' +
'<div class="modal-header">' +
'<h4 ng-bind="dialogTitle"></h4>' +
'</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-default" ng-click="cancel()">Close</button>' +
'<button type="button" class="btn btn-primary" ng-click="ok()">Save</button>' +
'</div>' +
'</div>',
link: function(scope, element, attrs, controller, transclude) {
transclude(scope, function(clone) {
var modalBody = element[0].querySelector('.modal-body');
angular.element(modalBody).append(clone);
});
}
};
});
Demo: http://plnkr.co/edit/I7baOyjx4pKUJHNkxkDh?p=preview

AngularJS: minifications breaks my directive

I use a directive to ask user for action confirmations in modals.
It works like a charm during development, but, after minification, it's broken.
This is the dreadful "$injector: unpr" error I get:
Error: [$injector:unpr] Unknown provider: aProvider <- a
...
I presume the problem is that $scope and $modalInstance are renamed, and should not be, but I don't know how to avoid this...
This is the directive code:
'use strict';
app.directive('reallyClick', ['$modal', function($modal) {
var modalInstanceCtrl = function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
return {
restrict: 'A',
scope: {
reallyClick: '&',
item: '='
},
link: function (scope, element, attrs) {
element.bind( 'click', function() {
var message = attrs.reallyMessage || 'Are you sure?';
var modalHtml = '<div class="modal-body">' + message + '</div>';
modalHtml += '<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>';
var modalInstance = $modal.open({
template: modalHtml,
controller: modalInstanceCtrl
});
modalInstance.result.then(function () {
scope.reallyClick({item:scope.item}); // raise an error : $digest already in progress
}, function() {
// modal dismissed
});
});
}
};
}]);
I use it this way:
...
<td title="Delete customer">
<button
class="btn btn-primary glyphicon glyphicon-trash"
really-message="Are you really sure to remove customer <i>{{customer.name}}</i> ?" really-click="deleteCustomer(customerId)"
></button>
</td>
...
If it can be of any help, these are the modules I use during the build phase:
'auto_install',
'clean:dist',
'favicons',
'wiredep',
'useminPrepare',
'concurrent:dist',
'autoprefixer',
'concat',
'ngmin',
'copy:dist',
'cdnify',
'cssmin',
'uglify',
'filerev',
'usemin',
'htmlmin',
and these are the modules I inject in my app:
var app = angular.module('smallBusinessApp', [
'ngSanitize',
'ngRoute',
'firebase',
'ui.bootstrap',
]);
The modalInstance Controller needs to be created with the dependency injection syntax as well,
'use strict';
app.directive('reallyClick', ['$modal', function($modal) {
return {
restrict: 'A',
scope: {
reallyClick: '&',
item: '='
},
link: function (scope, element, attrs) {
element.bind( 'click', function() {
var message = attrs.reallyMessage || 'Are you sure?';
var modalHtml = '<div class="modal-body">' + message + '</div>';
modalHtml += '<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>';
var modalInstance = $modal.open({
template: modalHtml,
controller: modalInstanceCtrl
});
modalInstance.result.then(function () {
scope.reallyClick({item:scope.item}); // raise an error : $digest already in progress
}, function() {
// modal dismissed
});
});
}
};
}]);
ModelInstanceController:
app.controller('modalInstanceCtrl',['$scope','$modalInstance',function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}]);
Was a problem for me too and had to separate the controller part of the modal and do it like this, hope it helps!!
I guess it's your modalInstanceCtrl. Try to not implement is as an object inside your directive, but as a controller:
app.controller('modalInstanceCtrl', [ '$scope', '$modalInstance',
function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}]);
And lateron reference it by putting it in quotes:
var modalInstance = $modal.open({
template: modalHtml,
controller: 'modalInstanceCtrl'
});

Resources