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");
};
}
};
}
]);
Related
I have a custom directive to show contents when depending on if it is marked special:-
myApp.directive('actionSpace', function() {
//define the directive object
var directive = {};
//restrict = E, signifies that directive is Element directive
directive.restrict = 'E';
directive.link = function(scope, elem, attr) {
console.log(scope.typeEv);
if(attr.special == "1") {
elem.html("");
} else {
elem.replaceWith('<div class="event-list-control"><button class="event-action-btn" data-ng-click="whoWas()"> '+
'<i class="fas fa-edit"></i>' +
'</button><button class="event-action-btn" data-ng-click="tellMe()">' +
'<i class="fas fa-trash-alt"></i></button></div>');
}
}
return directive;
});
I can see in console directive's parent scope is available (its printing one of the variables), but data-ng-click does not work.
You need to compile the inserted html before inserting it into the element, please refer the below example. I use the $compile method to make the data-ng-click work!
var app = angular.module('myApp', []);
app.controller('MyController', function MyController($scope) {
});
app.directive('actionSpace', function($compile) {
//define the directive object
var directive = {};
//restrict = E, signifies that directive is Element directive
directive.restrict = 'E';
directive.link = function(scope, elem, attr) {
var html = ''
scope.tellMe = function(){console.log("telling");}
scope.whoWas = function(){console.log("this is");}
if(attr.special == "1") {
html = '';
} else {
html = '<div class="event-list-control"><button class="event-action-btn" data-ng-click="whoWas()">'+ '<i class="fas fa-edit"></i>' +
'Who Was</button><button class="event-action-btn" data-ng-click="tellMe()">' +
'<i class="fas fa-trash-alt"></i>Tell Me</button></div>';
}
var el = $compile(html)(scope);
elem.append(el);
}
return directive;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller='MyController' ng-app="myApp">
<action-space></action-space>
</div>
Instead of composing the template in the postLink function, do it in a template function:
myApp.directive('actionSpace', function() {
//define the directive object
var directive = {};
//restrict = E, signifies that directive is Element directive
directive.restrict = 'E';
directive.template = function(tElem, tAttrs) {
if (tAttrs.special == "1") {
return "";
} else {
return `
<div class="event-list-control">
<button class="event-action-btn" data-ng-click="whoWas()">
<i class="fas fa-edit"></i>
</button>
<button class="event-action-btn" data-ng-click="tellMe()">
<i class="fas fa-trash-alt"></i>
</button>
</div>
`;
};
}
return directive;
});
For more information, see AngularJS Comprehensive Directive API Reference - template
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
I have a list of javascript objects I do an ng-repeat on.
I associate each of them a toggle button from UI-Bootstrap.
I want the toggle button to be toggeled depending on the value in my javascript model.
var app = angular.module('App', []);
app.controller('Ctrl', function($scope) {
$scope.list = [{a : '10'},
{a : '20'},
{a : '42'}];
});
<div ng-repeat="data in list">
{{data.a}} <button type="button" class="btn btn-primary" ng-model="data.state" btn-checkbox="">TEST</button>
</div>
http://plnkr.co/edit/JcnzNSKhy68dXtGHlXLe?p=preview
For example, in this case I want the button associated with a = 42 to be already toggeled
Edit : The data from the list are fetched from a GET request so I it's can't be statically written
I'm not sure if this is exactly what you are looking for, but you can add ng-click to the button:
<button type="button" class="btn"
ng-class="{'btn-primary': data.state}"
ng-model="data.state"
ng-click="data.state = !data.state">
TEST
</button>
EDIT
You can set the state in your controller:
var app = angular.module('App', []);
app.controller('Ctrl', function() {
var _this = this;
_this.list = [{a : '10'},
{a : '20'},
{a : '42'}];
var i;
for (i = 0; i < _this.list.length; i++) {
if (_this.list[i].a == 42) {
_this.list[i].state = true;
}
}
});
http://plnkr.co/edit/C8NW5h4pzfzBxrCSntDs?p=preview
While the above answer works, I'd argue that the more Angular approach to this is to use a directive. And it is one less attribute to account for.
html
<button class="btn" nx-toggle ng-model="data.state">toggle</button>
javascript
app.directive('nxToggle', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function($scope, elem, attrs, ngModel) {
$scope.$watch(function() {
return ngModel.$modelValue
}, function(val) {
if (val == true)
elem.addClass('btn-primary')
else
elem.removeClass('btn-primary')
})
elem.bind('click', function() {
$scope.$apply(function() {
if (ngModel.$modelValue == true)
ngModel.$setViewValue(false)
else
ngModel.$setViewValue(true)
})
})
}
}
})
http://plnkr.co/edit/bq4roWHUUlaBnj5xr31Z?p=preview
The function tied to the ng-click event is never fired. The ng-click is inside a directive bootstrapped with $compile. Does anybody know why the click event never fires? The function is visible on the scope. A working plunker is provided. The code is as follows:
var app = angular.module('plunker', ['ui.bootstrap']);
app.controller('MainCtrl', function($scope) {
$scope.datum = new Date('2015-05-01');
});
var link = function($compile) {
return function(scope, element, attributes, form) {
scope.opened = false;
var tag = '<span class="input-group-btn">' +
'<button type="button" class="btn btn-default" ng-click="open($event)">' +
'<i class="glyphicon glyphicon-calendar"></i>' +
'</button>' +
'</span>';
console.log(scope.opened)
element
.removeAttr('szp-date')
.attr('datepicker-popup', "")
.attr('is-open', "opened")
.wrap('<p class="input-group" style="width: 10em"></p>')
.after(tag);
$compile(element)(scope);
};
};
var forminput = function($compile) {
return {
restrict: "A",
require: "^form",
controller: function($scope) {
$scope.open = function($event) {
// This never gets fired
$event.preventDefault();
$event.stopPropagation();
$scope.opened = true;
console.log(scope.opened);
};
},
link: link($compile)
};
};
app.directive("szpDate", forminput);
EDIT:
It is interesting that modifying the input tag as follows:
element
.removeAttr('szp-date')
.attr('datepicker-popup',"")
.attr('is-open', "opened")
// added this line of code
.attr('ng-click', 'open($event)')
.wrap('<p class="input-group" style="width: 10em"></p>')
.after(tag);
produces a clickable input element, but this is not what I want to achieve. I want the button to be clickable.
Edit 2:
More interestingly, I seem to have got it working if i modify this line.
.after(tag)
with
.after($compile(tag)(scope));
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() {
});
});
}
}
}
}
]);